MEF 插件和 EF CodeFirst - 如何?
背景:
我们有一个包含许多模块的项目。我们将 EntityFramework 4.2 与 FluentAPI (CodeFirst) 结合使用。
有一个名为 Diverto.ORM.EntityFramework.SQLServer 的中心项目,其中包含使用 FluentAPI 构建上下文的部分类(并且引用了解决方案上的每个其他项目)。
最近,我们收到了客户的请求,要求实现许多其他功能,该解决方案将需要几个其他项目。其中一些项目将来自另一个系统(人力资源),有些将被创建。现有解决方案的核心是财务系统。
我们希望使用 MEF“即时”启用和禁用这些新项目(以及 GUI、业务逻辑等)。它们将作为插件进行交互,应用程序的主菜单也将使用 MEF 进行填充。
然而,由于它们必须共享数据,我们并不真正了解如何启用/禁用这些模块/项目(新模块/项目和 HR 模块/项目)。
考虑一下:
- DivertoContext(主上下文)与 DbSet
- PluginContext(来自插件)与 DbSet
现在,考虑在 GUI 内部我必须能够访问 ClassA、ClassB 和 ClassC 的数据(如果插件存在)。
找到解决方案!请参阅下文
嘿,您在,请在回答之前阅读此内容!
我注意到有些人查看了此内容并将其标记为最喜欢或赞成。请记住,这个答案可以追溯到 2012 年,自那以后 EntityFramework 发生了很大变化。
另外,请请、请记住,每个项目都有自己的需求。我当时需要这样的功能。您的项目可能根本不需要这个,或者只是其中的某些部分!
最后,为了确保所有内容都被覆盖,是的,可以在 EF 6.1 和 EF 迁移中使用此功能,并且也可以在其他 ORM 和迁移框架中使用。
您可能需要一些其他接口,作为要加载的迁移的接口,并正确处理特定的插件迁移(不要将其与其他插件混合,因此尝试为每个插件实现某种唯一的令牌)。
Background:
We have a project with many modules. We're using EntityFramework 4.2 with FluentAPI (CodeFirst).
There is a central project named Diverto.ORM.EntityFramework.SQLServer which contains partial classes that build the context using the FluentAPI (and which has references to every other project on the solution).
Recently we received a request from the customer to implement many other features and the solution will need several other projects. Some of these projects will come from another system (Human Resources) and some will be created. The core of the existing solution is a Finance system.
We want to enable and disable these new projects (and GUI, business logic and all) "on the fly" using MEF. They will interact as plugins and the main menu from the application will get populated also using MEF.
However we don't really have a clue on how to enable/disable these modules/projects (new ones and HR ones) because of data that they must share.
Consider this:
- DivertoContext (main context) with DbSet<ClassA> and DbSet<ClassB>.
- PluginContext (from a plugin) with DbSet<ClassC>.
Now, consider that inside the GUI I must have access to data from ClassA, ClassB and ClassC (if the plugin is there).
Solution found! See bellow
HEY, YOU THERE, READ THIS BEFORE THE ANSWER!
I've noticed some people checking this out and marking this as favorite or upvoting. Please, bear in mind that this answer dates back to 2012 and EntityFramework has changed a lot since that.
Also, please, please, PLEASE, remember that each project has it's very own needs. I needed this feature, this way, at that time. Your project might not need this at all, or just some parts of this!
Finally, just to make sure everything is covered up, yes, it's possible to make this work with EF 6.1 and EF Migrations and it might be possible with other ORM and migration framework as well.
You might need some other interfaces, as one for the migration to load, and properly handle specific plugin migration (don't mix it with other plugins so try to implement some sort of unique token for each plugin).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
找到解决方案了!
好吧,我会尝试在这里解释一下,因为我在其他地方找不到这个。对于必须创建一个将接收多个插件的单一基础软件的人来说,这很有趣,并且这些插件必须与单个数据库中的现有数据进行交互。
首先,我将使用带有 CodeFirst API 等的实体框架。因此,如果您要了解此内容,我建议您阅读 EntityTypeConfiguration 来自 MSDN 和 Code First Fluent API 也来自 MSDN。< br>
现在,让我们了解一些事情:
首先要事
解决方案: MEFTest
项目
现在,Base.ORM 项目内部的编码
使用您认为合适的方法创建通用存储库接口,但该接口没有类型化。它将与此类似:
从现在起,为了简单起见,我将其称为 IRepository。
我将考虑一种名为 Add(T item) 的方法来进行示例编码。
在 Base.ORM.EntityFramework.SQLServer 内部创建一个继承自 DbContext 并实现 IRepository 的 BaseContext 类。它应该如下所示:
您可以在此处添加自定义 IDatabaseInitializer 基本实现以进行数据库版本控制。我已经使用标准文件夹中的 SQL 文件完成了此操作,但这是旧编码,因为 EF 现在支持迁移。
如果您仍要手动处理此问题,请记住之前将数据库设置为单用户模式,之后恢复为多用户模式。请记住:try...catch...finally 在这里会有所帮助,因为您可以在finally 中恢复为多用户,因此即使出现错误也不会留下任何问题。
在 SampleApplication 项目中,添加:
ClassA(int Id,字符串名称)和ClassB(int Id,DateTime TestDate)。
在 SampleApplication.ORM.EntityFramework.SQLServer 内创建您的标准上下文。
我将在这里使用三个类,它们的名称非常有趣:ClassA、ClassB 和 ClassC。
ClassA 和 ClassB 都从这个项目中引用,它会像这样:
现在有趣的部分:插件将有一个像这样的方法:
当然 ClassC 位于插件项目内部。您在主项目中没有对它的任何引用。
您必须使用 MEF,当然还有接口来找到此方法(设置)。我将只展示在哪里放置它以及如何使其工作 =)
回到 Context 类,OnModelCreating 方法将如下所示:
用法
在您的应用程序内部,您将只有一个 Context。该上下文继承自实现 IRepository 的 BaseContext。考虑到这一点,您需要对 GUI 和业务层进行编码,以使用 IRepository(来自 Base.ORM)和特定类(在特定于业务的 dll 内)。
有用!
嗯,它在这里工作。
我想我已经在这里展示了所有相关部分。
当然,类中还有更多代码,但事实并非如此。我试图仅展示您真正需要创建/实现的内容来完成它。
不要忘记:
感谢
来自 SO 和 MSDN 的人们,他们在我找到的评论和其他帖子上为我提供了很多帮助。
感谢 Caio Garcia (BR) 帮助我了解了有关其他基于插件的系统的一些说明。
示例代码(完整)
以下是一些示例代码:
对于基本项目(解决方案预定义项目)
插件
对于常规代码上的
Solution found!
Well, I'll try to explain here since I couldn't find this elsewhere. This is interesting for people that have to create a single base software that will receive multiple plugins and these plugins must interact with existing data within a single database.
First of all, I'll be working with Entity Framework with CodeFirst API and all. So if you're going into this I recomend reading of EntityTypeConfiguration from MSDN and of Code First Fluent API also from MSDN.
Now, let's understang some things:
First things first
Solution: MEFTest
Projects:
Now, the coding
Inside the Base.ORM project create your Generic Repository interface with methods as you see fit, but the interface is not typed. It will be similar to this:
From now on I'll just call it IRepository to keep things simple.
I'll consider one method called Add(T item) for sample coding.
Inside the Base.ORM.EntityFramework.SQLServer create a BaseContext class that inherits from DbContext and that implements IRepository. It should look like this:
You might add a custom IDatabaseInitializer base-implementation here for database versioning. I've done it with SQL files winthin a standard folder but this is old coding as EF now supports migrations.
If you'll still up to handling this manually, remember to set the database to single user mode BEFORE and reverting to multi user mode AFTER. Remember: try...catch...finally will help here because you can revert to multi user inside the finally so even on error there will be no problems left behind.
Inside the SampleApplication project, add:
ClassA (int Id, string Name) and ClassB (int Id, DateTime TestDate).
Inside the SampleApplication.ORM.EntityFramework.SQLServer create your standard context.
I'll use three classes here with very interesting names: ClassA, ClassB and ClassC.
Both ClassA and ClassB are referenced from this project and it will be like this:
Now the funny part: The plugin will have a method like this:
Of course ClassC is inside the plugin project. You don't have any reference to it from the main project.
You will have to find this method (Setup) using MEF and of course, interfaces. I'll just show WHERE to place this and how to make it work =)
Back to the Context class, the OnModelCreating method will be like this:
Usage
Inside your APP you will have just one Context. This context inherits from BaseContext which implements IRepository. With that in mind you need to code your GUI and business layer to use IRepository (from Base.ORM) and the specific class (inside the business-specific dll).
It works!
Well, it's working here.
I think I've shown every relevant part here.
Of course there is more code inside the classes but it's not the case. I'm trying to show only what you really need to create/implement to get it done.
Don't forget:
Thanks
Thanks to people from SO and from MSDN that helped my a lot with comments and other posts that I've found.
Thanks to Caio Garcia (BR) that helped me with some instructions about other plugin-based systems.
Sample codes (full)
Here follows some sample codes:
FOR THE BASIC ITEMS (solution predefined items)
FOR THE PLUGIN
ON YOUR REGULAR CODE