【问题标题】:Creating model on the fly即时创建模型
【发布时间】:2016-06-21 13:02:41
【问题描述】:

是否可以在每次传入连接字符串时使用实体框架动态创建整个模型(数据库优先)?

我尝试了以下方法:

MetaModel model = new MetaModel();
model.RegisterContext(() => new Model1(connectionString),
new ContextConfiguration()
 {
  ScaffoldAllTables = true
 });

但它总是给我一个错误

System.Web.DynamicData.dll 中出现“System.ArgumentException”类型的未处理异常

附加信息:不支持上下文类型“DbContext.Model1”。

更多信息:

我在数据库中拥有我无法控制的所有必需表,如果我需要旧表中的任何新表或列,那么数据库人员会为我运行适当的脚本。

我正在尝试使用 EF 创建一个通用 DLL,我可以在各种应用程序 6 上使用它,包括 winformsmvc 网络应用程序。我正在尝试找出解决此问题的最佳方法。

我可以将codefirstdatabasefirst 混合在一起吗?

codefirst 不会为我生成我已经拥有的表格吗?

我的每个应用程序都包含 1 个 winforms 和 1 个 Web 应用程序,它们共享数据源,例如

测试 1 Windows 应用和测试 1 MVC 应用 = DB 1

测试 2 Windows 应用和测试 2 MVC 应用 = DB 2

测试 3 Windows 应用和测试 3 MVC 应用 = DB 3

所以我需要将connectionstring 传递给DBContext。我的实体将如何运作?

如果需要更多信息,请告诉我。

【问题讨论】:

  • 你为什么要这么做?为什么首先需要代码中的元模型?
  • @BassamAlugili 我目前有一个 dll,我在连接字符串、查询或存储过程中传递它,并且 dll 负责处理它。我想沿着 EF 的路线走,并且我在数据库中拥有所有表,因此我认为也许可以动态创建模型
  • 嗨@Code也许你可以参考this
  • @ShawnYan 谢谢你的链接,正如你从我的代码 sn-p 中看到的那样,我已经传入了连接字符串,但我需要弄清楚实体是如何工作的?例如如果连接字符串从A 更改为B 并且数据库中的大多数表都不同,那么会发生什么?
  • 您的问题不清楚。 How would my Entities work? - 您的实体需要在编译时定义,如果您的数据库模型相同,则无需重新定义实体。 Wouldn't codefirst generate tables for me which I already have? 如果您的数据库人员在您询问时运行适当的脚本,为什么您需要生成表?

标签: c# asp.net asp.net-mvc winforms entity-framework


【解决方案1】:

技术上您可以在运行时创建实体框架模型,至少通过动态创建程序集并使用代码首先工作的 API 和属性或通过创建模型所需的 xml。 但是您不需要在运行时创建模型类。

事实上,在运行时创建模型类是没有用的,因为您创建的模型在编译时和运行时都可以工作。您创建模型是因为您想将Type1 传递给方法或编写诸如.Where(x=>x.SomeFiled == SomeValue) 之类的类型化查询。

如果您为不同的客户提供不同的应用程序实例

如果您为不同的客户提供不同的应用程序实例,则无需执行任何具体操作。您的应用程序已编写好可以使用,只需在 webconfigappconfig 中为不同的客户使用不同的连接字符串。

如果您有一个适用于所有客户的应用程序实例

在这种情况下,您有一个多租户应用程序,您可以简单地向您的数据库上下文构造函数添加一个重载,该构造函数接受连接字符串作为输入。然后,当您需要创建数据库上下文的实例时,您可以使用该重载并根据您的租户检测策略在连接字符串中注入合适的用户名、密码和数据库名称。

public partial class SampleDbEntities
{
    public SampleDbEntities(string connectionString) : base(connectionString)
    {
    }
}

最好将新的重载放在部分类中。那么每次更新上下文运行的edmx.tt模板时就不会被触动了。

例如,您可以通过这种方式创建连接字符串和上下文:

var connectionTemplate =
    @"metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;" +
    @"provider=System.Data.SqlClient;" +
    @"provider connection string=""data source={0};" +
    @"initial catalog={1};" +
    @"persist security info=True;" +
    @"user id={2};" +
    @"password={3};" +  
    @"MultipleActiveResultSets=True;App=EntityFramework""";

string connection = string.Format(connectionTemplate, 
    @"(localdb)\v11.0", @"SampleDB1", @"user1" , @"password1");

var db = new SampleDbEntities(connection);

或者您可能想要 Windows 身份验证,然后使用 @"integrated security=True;" + 代替用户 ID 和密码。

您可以根据不同的策略检测不同的租户,包括:

  • 应用程序的 URL(域和子域)
  • 查询字符串或路由值
  • 用户名

您可以将创建合适上下文的角色分配给基于您的租户策略工作的类。

如果数据库中的表发生变化怎么办?

只需在设计时更新您的 edmx 模型并重新构建您的应用程序并重新分发它。如上所述,如果向表中添加了一个字段或向数据库中添加了一个新表,并且您想在应用程序db.Tabe1.Where(x=>x.Field1==value1) 中编写此类类型的查询,那么您需要从数据库更新模型并重建您的应用程序。在运行时重新生成模型没有意义。

如何提高工作效率?

我知道您的目标是提高生产力,但在运行时生成模型并不是您想要的。相反,您可以创建一些通用数据访问层和通用业务逻辑层来提高生产力。例如,如果您有很多需要 CRUD 操作的实体,您可以创建一个 EntityBusiness<TContext, TModel> 类并具有通用的 void Create(TModel entity)IList<TModel> GetAll(object key)TModel GetByKey(object key)TModel Update(TModel entity)void DeleteByKey(object key) 等。这样你可以简单地创建EntityBusiness<SampleDbEntities, Product> 的实例或从它继承。该类包含简单 CRUD 操作的所有行为,您可以通过添加对其他一些有用案例(如验证)的支持来增强它。作为创建通用存储库和工作单元的示例,请查看 asp.net mvc 站点中的 article

【讨论】:

  • 可能答案并不完全是你喜欢做的事,但至少它会帮助你朝着正确的方向前进:)
【解决方案2】:

是的,您可以在运行时指定连接字符串。

是的,如果修改了表,则需要更新 C# 类。如果您使用数据库优先,请更新 edmx 并保存它。如果您使用代码优先,请手动更新 C# 类。

一个例子。

一个实体类

public class Table1 {
    // props...
}

DbContext

public class MyContext: DbContext {
    public MyContext (string connectionString): base(connectionString) { }
    public DbSet<Table1> Table1 { get; set; }
}

用法...

string connectionstring1 = "someConnectionString";  // Put this in the app.config
using (var context = new MyContext(connectionstring1)) {
    var table1sFromDb1 = context.Table1.ToList();
}

string connectionstring2 = "someOtherConnectionString"; // Put this in the app.config
using (var context = new MyContext(connectionstring2)) {
    var table1sFromDb2 = context.Table1.ToList();
}

【讨论】:

  • 我将始终使用数据库优先方法,我将使用 3 个数据库,每个数据库都有自己的表。使用您的方法,我不必为每个数据库创建 3 个单独的 DLL。我想要实现的是拥有一个通用 DLL,我通过连接字符串并生成适当的实体,这可能吗?
  • @Code 即使您可以在运行时生成实体,如果实体仅在运行时生成,您将如何在编译时从其他程序集/项目中使用它们?恕我直言,非常不切实际。
  • 你说得有道理。我可能需要重新考虑这一点。
  • 您认为最适合我的方案的方法是什么?
  • 你想达到什么优势?当您创建不能以通用方式使用的“通用”程序集时,我看不到任何内容,因为您需要使其针对每种情况。
【解决方案3】:

如果您只有一个应用程序,则该应用程序必须使用最少的表和列集(即实体和属性)。
从数据库生成模型是没有用的,因为应用程序必须只使用共享表和列。
在您的情况下,一个好的方法可能是使用 CodeFirst(在您的情况下是来自数据库的 CodeFirst),然后禁用模型检查(要检查模型,您仍然可以使用单元测试)。

【讨论】:

    猜你喜欢
    • 2011-09-12
    • 2019-03-10
    • 2012-11-23
    • 2015-05-20
    • 1970-01-01
    • 2011-08-02
    • 2017-12-21
    • 1970-01-01
    相关资源
    最近更新 更多