EF代码优先模块化设计

5
使用EF4 Code First,您可以创建和编译类和DbContext。如果您想在已编译的模型集的dll中添加一些类/表和关系,会发生什么情况?
到目前为止,我想出的解决方案是使用“partial”类,稍后会被补充,第二个解决方案是编写一个包含第一个dbcontext的全新dbcontext或扩展它的dbcontext,但这意味着每个模块(每个dbcontext)额外的db连接。对此有任何想法吗?最佳实践是什么?还需要能够使用迁移。
更明确地说,可能的情况如下:
A)您创建一个带有某些dbContextBase类和表(类)的.dll。
B)您创建其他.dll,这些.dll以自己的方式依赖/扩展了dbContextBase*
C)您在项目中引用了这些.dll并对其进行了扩展。
所以基本上您可以有一个核心dbContext,然后向其中添加菜单模块,然后向其中添加博客模块(但可以通过菜单模块查看以创建最新的博客文章菜单等)。除此之外,如果您想要针对博客的特定一次性功能,可以快速集成它,但也可以保持您的博客模块可更新性。
随着我开始看到它,最好的方法是使用源代码为每个模块提供模型(等)的Nuget包,而不是编译的dll。
3个回答

5
您可以在核心程序集中构建一些基础设施,用于发现模块中的实体并将它们注册到单个上下文中。每个实体必须具有从 EntityTypeConfiguration<>(对于复杂类型使用 ComplexTypeConfiguration<>)派生的类,用于描述映射关系。
一旦有了映射类,您可以使用某个模块接口来收集每个模块中的所有映射类,也可以使用反射浏览程序集并创建映射类的实例。这些类可以直接由 DbModelBuilder 使用(可以在 OnModelCreating 中或直接使用)。

此外,我需要能够使用迁移。

我不确定迁移是否已经准备好支持此功能,因为它有一些前提条件:

  • 所有共享表必须由核心程序集处理-其自己的 DbMigration 派生类(或新版本的类)
  • 每个模块必须处理其自己的表-其自己的 DbMigration 派生类(或新版本的类)
  • 模块不能更改共享表
  • 模块不能更改或访问其他模块的表

这意味着您必须为核心设置特殊的迁移集,并为每个模块设置一个迁移集。每个迁移集在单独的程序集中定义,这可能是潜在问题。我自己没有尝试过,所以不知道 EF 迁移是否能够处理此问题-我尤其针对那些需要可添加或可删除模块的情况,因此您需要安装(Up 方法)和卸载(Down 方法)。

迁移存在的问题是您无法强制执行“必须”和“不得”,因此如果您开发出可以添加自定义模块的平台,则永远无法确定它们是否会破坏您的核心。


感谢您详细的回复,非常有见地!我在这种类型的设计方面没有任何经验,所以我需要仔细研究您的回复。不过,有一个澄清,由于目前不会有外部开发人员,并且我不介意模块能够访问其他模块,这是否会使得涵盖迁移的解决方案更容易实现? - Mihalis Bagos
也许我没有正确理解你最初的问题 - 你所说的模块是什么意思?它只是你代码中的一个库,所有库都被编译并一起发布,还是一些可以添加或删除而不需要更改或重新编译应用程序的现有产品的附加组件? - Ladislav Mrnka
我没有解释清楚。是的,库将被编译和发布。我有一个核心不断开发,但有时会根据客户进行修改。我想创建本地NuGet包,其中包含每个功能的最新版本,以便轻松更新并无缝合并更改,但同时保留数据库版本控制!与此同时,我开始非常喜欢你的架构!这将使远程部署功能成为可能... - Mihalis Bagos
在同一个数据库上使用多个DbContext似乎会偶尔混淆迁移类生成器,导致脚本尝试删除其他表。您可以更新输出以使其正确,并且它似乎可以工作。由于某种原因,Add-Migration将数据库与上下文进行比较,而不是将上下文与上下文的最新哈希值进行比较,我猜测在先前的迁移中没有存储足够的信息。除非您有更好的运气,否则我建议每个模块使用单独的数据库。 - Betty

2

由于没有回答专注于我提出的问题,因此我发布了一个答案,并在此时提供了最佳解决方法。

为了完全支持迁移,甚至是自定义迁移,并且在代码优先设计方面提供全面支持,最好的方法是直接导入源代码并进行编译。

我们使用本地NuGet Feed以便能够自由快速地同步多个子模块。这也会带来良好的更新体验,因为需要时可以轻松创建或导入/集成迁移。


1
这种情况怎么办:一个DbContext有一些实体,在OnModelCreating中,查找从基类继承的其他类,这些基类在此DbContext所在的程序集中。我想能够根据这些类更新已创建的数据库,假设它们不会更改基表,只可能添加新表。这是可能的吗?到目前为止,从我的经验来看,使用MigrateDatabaseToLatestVersion,它仅仅忽略了新实体,也就是说,不会生成任何新表。

这是我在问题中提出的一般想法,但是是一种我没有尝试过的不同实现。从你所说的来看,它似乎不起作用!仍在寻找与仅导入源代码不同的解决方案... - Mihalis Bagos
@Mihalis:是的,到目前为止,还没有运气...如果你找到了什么,请在这里发布一下,好吗?我也会这样做!谢谢! - Ricardo Peres
@Mihalis:顺便说一下,由于我正在自动发现实体,所以我不得不编写这个扩展方法: public static void Entity(this DbModelBuilder modelBuilder, Type entityType) { typeof(DbModelBuilder).GetMethod("Entity", BindingFlags.Public | BindingFlags.Instance).MakeGenericMethod(entityType).Invoke(modelBuilder, null); } - Ricardo Peres
我们现在的做法是直接导入源代码,甚至从我们找到的基类继承,但这样做会带来很多问题,不值得冒险。我喜欢你的方法,我们会尝试研究一下! - Mihalis Bagos
@Mihalis:不错!如果你愿意,可以通过rjperes@hotmail联系我。 - Ricardo Peres
相关博客文章:http://romiller.com/2012/03/26/dynamically-building-a-model-with-code-first/ - Betty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接