背景
我们有一个包含许多模块的项目。我们正在使用EntityFramework 4.2和FluentAPI(CodeFirst)
有一个名为Diverto.ORM.EntityFramework.SQLServer的中央项目,它包含使用FluentAPI构建上下文的部分类(并且它引用了解决方案中的所有其他项目)。
最近我们收到了客户要求实施许多其他功能的请求,该解决方案还需要其他几个项目。其中一些项目将来自另一个系统(人力资源),一些将被创建。现有解决方案的核心是财务系统
我们希望使用MEF“动态”启用和禁用这些新项目(以及GUI,业务逻辑和所有项目)。它们将作为插件进行交互,应用程序的主菜单也将使用MEF进行填充
但是,由于他们必须共享的数据,我们并不真正了解如何启用/禁用这些模块/项目(新模块/项目)。
考虑一下:
- 具有DbSet< ClassA>的DivertoContext(主要上下文)和DbSet< ClassB>。
- 具有DbSet< ClassC>的PluginContext(来自插件)
现在,考虑到在GUI内部,我必须能够访问ClassA,ClassB和ClassC中的数据(如果插件存在的话)。
找到解决方案!见下文
<嗨>嘿,你在那里,请在回答之前阅读!我注意到有人在检查这个并将其标记为收藏或赞成。请记住,这个答案可以追溯到2012年,EntityFramework已经改变了很多。
另外,请 ,请,请记住每个项目都有自己的需求。那时候,我需要这个功能。您的项目可能根本不需要这个,或者只是其中的一部分!
最后,只是为了确保所有内容都被掩盖,是的,可以使用EF 6.1和EF迁移来实现这一点,并且也可以使用其他ORM和迁移框架。
您可能需要一些其他接口,作为迁移加载的接口,并正确处理特定的插件迁移(不要将其与其他插件混合使用,因此请尝试为每个插件实现某种类型的唯一标记)。
答案 0 :(得分:28)
好吧,我会尝试在这里解释,因为我无法在其他地方找到这个。对于那些必须创建单个基础软件以接收多个插件并且这些插件必须与单个数据库中的现有数据进行交互的人来说,这很有趣。
首先,我将使用CodeFirst API与所有实体框架一起工作。因此,如果您要进行此操作,我建议您从MSDN和EntityTypeConfiguration读取MSDN中的Code First Fluent API。
现在,让我们解释一些事情:
解决方案: MEFTest
项目:
在Base.ORM项目中,根据您认为合适的方法创建Generic Repository接口,但不输入接口。它将类似于:
public interface IRepository
{
bool Add<T>(T item);
}
从现在开始,我只是将其称为IRepository,以保持简单 我将考虑一种名为Add(T item)的方法来进行样本编码。
在Base.ORM.EntityFramework.SQLServer中创建一个继承自DbContext并实现IRepository的BaseContext类。它应该是这样的:
public class BaseContext : IRepository
{
public bool Add<T>(T item)
{
try { Set<T>().Add(item); return true; }
catch { return false; }
}
}
您可以在此处添加自定义IDatabaseInitializer基础实现,以进行数据库版本控制。我已经用标准文件夹中的SQL文件完成了它,但这是旧的编码,因为EF现在支持迁移。
如果您仍然需要手动处理此操作,请记住将数据库设置为单用户模式BEFORE并在之后恢复为多用户模式。请记住:尝试...捕捉...最终将在这里提供帮助,因为你可以在最终内部恢复到多用户,所以即使在出错时也不会留下任何问题。
在SampleApplication项目中,添加:
ClassA(int Id,string Name)和ClassB(int Id,DateTime TestDate)。
在SampleApplication.ORM.EntityFramework.SQLServer中创建标准上下文 我将在这里使用三个非常有趣的名字:ClassA,ClassB和ClassC ClassA和ClassB都是从这个项目引用的,它将是这样的:
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);
}
}
现在有趣的部分:该插件将有这样的方法:
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放置它以及如何使它工作=)
回到Context类,OnModelCreating方法将是这样的:
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中)。
嗯,它在这里工作。
我想我已经在这里展示了所有相关部分
当然,类中有更多的代码,但事实并非如此。我试图只展示你真正需要创建/实现它来完成它。
感谢来自SO和MSDN的人们通过我发现的评论和其他帖子帮助了我很多。
感谢Caio Garcia(BR)帮助我提供了有关其他基于插件的系统的一些说明。
下面是一些示例代码:
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);
}
}
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);
}
}
public void DoSomething(IRepository repo)
{
var classA = new ClassA() { Name = "First Name" };
repo.Add(classA);
repo.Save();
}