MEF插件和EF CodeFirst - 如何? [英] MEF Plugins and EF CodeFirst - How?

查看:186
本文介绍了MEF插件和EF CodeFirst - 如何?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:

我们有一个包含许多模块的项目。我们正在使用具有FluentAPI(CodeFirst)的EntityFramework 4.2。



有一个名为Diverto.ORM.EntityFramework.SQLServer的中心项目,其中包含使用FluentAPI构建上下文的部分类(并且已经提到了解决方案中的每个其他项目)。


最近我们收到了客户要求实施许多其他功能,解决方案将需要其他几个项目。其中一些项目将来自另一个系统(人力资源部门),其中一些将被创建。现有解决方案的核心是财务系统。



我们要使用MEF启用和禁用这些新项目(以及GUI,业务逻辑和所有)。 。他们将作为插件进行交互,并且应用程序的主菜单也将使用MEF进行填充。

但是,我们并没有真正了解如何启用/禁用这些模块/项目(新的和HR因为他们必须共享的数据。



考虑这一点:

- DivertoContext(主上下文)与DbSet&ClassA>和DbSet&ClassB>

- PluginContext(来自插件)与DbSet&ClassC>。



现在,考虑到在GUI内部我必须访问ClassA,ClassB和ClassC的数据(如果插件在那里)。

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).

找到解决方案!

我有注意到有人检查出来,并将其标记为喜爱或upvoting。请记住,这个答案可以追溯到2012年,EntityFramework从此改变了很多

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.

另请参阅 PLEASE ,请记住,每个项目都有自己的需求。当时我需要这个功能。您的项目可能根本不需要这个,或者只是其中的一部分!

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!

最后,只是为了确保一切都被覆盖,是的,可以做这项工作使用EF 6.1和EF迁移,也可能与其他ORM和迁移框架一样。

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).

推荐答案

找到解决方案



嗯,我在这里尝试解释,因为我在其他地方找不到。对于必须创建单个基础软件的人们来说,这将是有趣的,它将接收多个插件,这些插件必须与单个数据库中的现有数据进行交互。



首先,我将使用CodeFirst API和所有的Entity Framework进行工作。所以如果你要进入这个我建议阅读 EntityTypeConfiguration ,以及代码第一流利的API 也来自MSDN。



现在,让我们讲一些东西:

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:


  • 您必须只有一个上下文才能正常工作。我将介绍这一点,并展示一种从应用程序的上下文中插入类的方法,但是为了工作,您必须了解通用存储库模式。我只会在这里显示一点,但我建议你努力学习,并尝试为你的应用程序创建最好的界面。

  • MEF将是我们的朋友。我会认为你已经知道如何使用MEF创建一个简单的插件,以及如何访问该插件中的一个方法。此外,我会尽量避免深入MEF,因为在这里并不是这样,因为您可以使用其他解决方案。事实上,我正在使用MEF,因为我已经熟悉它了。

  • 如果你要进入哦,我需要处理多个上下文创建,指向一个单一的数据库,并且所有的你做错了。这是关于简单的配置和只是一些流利的API。相信我:我通过互联网搜索了一个星期,最后在和一个朋友交谈后,我们发现了这个解决方案,很简单。




解决方案: MEFTest

项目:

Solution: MEFTest
Projects:


  • Base.ORM(将保存ORM的接口)

  • Base.ORM.EntityFramework.SQLServer(将保存EF的基类)

  • SampleApplication.ORM.EntityFramework.SQLServer(将保存应用程序的上下文)

  • SampleApplication(可执行文件)

  • MEFPlugin(将保存我们的插件) / li>
  • Base.ORM (that will hold interfaces for ORM)
  • Base.ORM.EntityFramework.SQLServer (will hold base classes for EF)
  • SampleApplication.ORM.EntityFramework.SQLServer (will hold the context for the application)
  • SampleApplication (executable)
  • MEFPlugin (will hold our plugin)

在Base.ORM项目中创建你的Generic Repository接口与您认为合适的方法,但接口不是键入。它将类似于:

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:

public interface IRepository
{
   bool Add<T>(T item);
}

从现在开始,我将其称为IR存储库以保持简单。 br>
我将考虑一个名为Add(T item)的方法来进行示例编码。

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.

在Base.ORM.EntityFramework.SQLServer中创建一个BaseContext类它从DbContext继承并实现了IRepository。它应该如下所示:

Inside the Base.ORM.EntityFramework.SQLServer create a BaseContext class that inherits from DbContext and that implements IRepository. It should look like this:

public class BaseContext : IRepository
{
   public bool Add<T>(T item)
   {
      try { Set<T>().Add(item); return true; }
      catch { return false; }
   }
}

您可以在这里添加自定义的IDatabaseInitializer基本实现用于数据库版本控制。我已经用SQL文件winthin标准文件夹,但这是旧的编码,因为EF现在支持迁移。

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.

在SampleApplication项目中添加:

ClassA(int Id,string Name)和ClassB(int Id,DateTime TestDate)。

Inside the SampleApplication project, add:
ClassA (int Id, string Name) and ClassB (int Id, DateTime TestDate).

SampleApplication.ORM.EntityFramework.SQLServer创建您的标准上下文。

我将在这里使用三个有趣的名称:ClassA,ClassB和ClassC。

ClassA和ClassB都被引用从这个项目,它将如下所示:

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:

public class Context : BaseContext
{
   public DbSet<ClassA> ClassA { get; set; }
   public DbSet<ClassB> ClassB { get; set; }

   protected override void OnModelCreating(DbModelBuilder modelBuilder)
   {
      /* I'll talk about this later. Just override the OnModelCreating and leave it */
      base.OnModelCreating(modelBuilder);
   }
}

现在有趣的部分:插件将有一个方法像这样:

Now the funny part: The plugin will have a method like this:

public void Setup(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<ClassC>().ToTable("ClassC");
   modelBuilder.Entity<ClassC>().HasKey(_classC => _classC.Id);
   modelBuilder.Entity<ClassC>().Property(_classC => _classC.Date2).HasColumnType("datetime2").HasPrecision(7);
}

当然ClassC在插件​​项目中。您没有从主项目中引用它。

您将不得不使用MEF找到此方法(安装程序),当然也可以使用接口。我将只显示WHERE来放置这个,并且如何使它工作=)

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 =)

回到Context类,OnModelCreating方法将是这样的:

Back to the Context class, the OnModelCreating method will be like this:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<ClassA>().ToTable("ClassA");
   modelBuilder.Entity<ClassA>().HasKey(_classA => _classA.Id);

   modelBuilder.Entity<ClassB>().ToTable("ClassB");
   modelBuilder.Entity<ClassB>().HasKey(_classB => _classB.Id);
   modelBuilder.Entity<ClassB>().Property(_classB => _classB.TestDate).HasColumnType("datetime2").HasPrecision(7);

   /* Use MEF to load all plugins. I'll use the mock interface IPlugin */
   foreach (IPlugin plugin in MefLoadedPlugins)
      plugin.Setup(modelBuilder);
}



用法



在你的APP你将只有一个上下文。此上下文从实现IRepository的BaseContext继承。考虑到这一点,您需要编写您的GUI和业务层,以使用IRepository(来自Base.ORM)和特定类(特定于业务的dll内)。

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).

嗯,这是在这里工作。



我想我已经在这里展示了每一个相关的部分。 br>
当然在类中还有更多的代码,但不是这样。我试图只显示你真正需要创建/实现的东西才能完成。

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.


  1. 不要忘记你必须编写自己的代码来种子数据库。对于我的情况,在这个插件的同一个界面下,我有一些类似于种子(IRepository)的东西,我只是处理所有的东西。

  2. 不要忘记你没有主要项目到插件。这意味着您必须通过接口和提供程序找到一种加载菜单,GUI,业务和数据的方法。我已经设法解决了使用诸如IPlugin(业务),IFormPlugin(GUI - Winforms)和IPluginRepository(数据)之类的东西。您将不得不找到自己的名称和方法,这可能适合您的需要,但这应该是一个好的起点。

  3. 不要忘记,如果您加载插件,您必须创建表在数据库或EF CodeFirst内将无法初始化。请记住,您可能需要SQL文件,并手动运行它们以根据需要创建表。

  4. 不要忘记,如果卸载插件,则必须删除表或EF也将失败。 / li>
  5. 不要忘记你真的需要备份。我没有这样做,但我已经标记了这将在DDL命令之前和之后完成。

  6. 这是MY解决我的情况。这个应该是新项目的一个很好的开始,但只是这样。不要以为这是我在这里完成的,每个案例都可以工作100%。

  1. Don't forget that you will have to write your own code to seed the database. For my case here inside the same interface for the plugin I have something like Seed(IRepository) and I just handle everything there.
  2. Don't forget that you don't have references from the main project to the plugins. This means that you MUST find a way to load menu, GUI, business and data all through interfaces and providers. I've managed to solve that using something like IPlugin (business), IFormPlugin (GUI - Winforms) and IPluginRepository (data). You will have to find your own names and methods that might fit your needs but this should be a good start point.
  3. Don't forget that if you load a plugin you must create the tables inside the database or EF CodeFirst will fail to initialize. Remember that you might need SQL files and manually run them to create tables as needed.
  4. Don't forget that if you unload a plugin you must remove tables too or EF will fail too.
  5. Don't forget that you REALLY NEED BACKUPS. I didn't do this yet but I've already marked where this will be done (before and after DDL commands).
  6. This is MY solution for MY case. This should be a good start for new projects but just that. Don't think that by following what I've done here it will work 100% for every case.



谢谢



感谢来自SO和MSDN的人对我发现的评论和其他职位的帮助很多。

感谢Caio Garcia(BR),帮助我了解其他基于插件的系统的一些说明。

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.

以下是一些示例代码:

public class ClassA
{
   public int Id { get; set; }
   public string Name { get; set; }
}

public class ClassB
{
   public int Id { get; set; }
   public string OtherName { get; set; }
}

public interface IRepository
{
   bool Add<T>(T item);
   bool Save();
}

public class BaseContext : DbContext, IRepository
{
   public bool Add<T>(T item)
   { 
      try { Set<T>().Add(item); return true; } catch { return false; }
   }
   public bool Save()
   {
      try { SaveChanges(); return true; } catch { return false; }
   }
}

public class Context : BaseContext
{
   // Fill this list using MEF - check for the IPluginContext interface on assemblies
   public List<IPluginContext> MefLoadedPlugins;

   protected override void OnModelCreating(DbModelBuilder modelBuilder)
   {
      modelBuilder.Entity<ClassA>().ToTable("TableB", "Schema_1");
      modelBuilder.Entity<ClassA>().HasKey(_a => _a.Id);

      modelBuilder.Entity<ClassB>().ToTable("TableB", "Schema_1");
      modelBuilder.Entity<ClassB>().HasKey(_b => _b.Id);

      if (MefLoadedPlugins != null)
         foreach (var pluginContext in MefLoadedPlugins)
            pluginContext.Setup(modelBuilder);
   }
}



对于插件



FOR THE PLUGIN

public class ClassC
{
   public int Id { get; set; }
   public string Description { get; set; }
}

public interface IPluginContext
{
   void Setup(DbModelBuilder modelBuilder);
}

public class Just_A_Sample_Plugin_Context : IPluginContext
{
   public void Setup(DbModelBuilder modelBuilder)
   {
      modelBuilder.Entity<ClassC>().ToTable("TableC", "Schema_2");
      modelBuilder.Entity<ClassC>().HasKey(_c => _c.Id);
   }
}



您的常规代码



ON YOUR REGULAR CODE

public void DoSomething(IRepository repo)
{
   var classA = new ClassA() { Name = "First Name" };
   repo.Add(classA);
   repo.Save();
}

这篇关于MEF插件和EF CodeFirst - 如何?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆