我一直在寻找这个问题的答案,我发现使用托管可扩展性框架很容易做到。在这篇文章的底部有一种更快的方法,但是 MEF 允许一种更具可扩展性的方法。
MEF 允许您从不同的程序集构建动态访问插件;但是它可以用于在单个程序集应用程序中快速填充集合。本质上,我们将使用它作为将我们的程序集反射回类的安全方式。为了使这个功能充分发挥作用,我还将在实体框架模型中实现策略模式。
添加对您项目的引用,指向System.ComponentModel.Composition。这将提供对 MEF 库的访问权限。
现在,我们需要实现策略模式。如果您没有 Interfaces 文件夹,请创建一个并添加 IEntity.cs,如下所示。
IEntity.cs
namespace Your.Project.Interfaces
{
/// <summary>
/// Represents an entity used with Entity Framework Code First.
/// </summary>
public interface IEntity
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>
/// The identifier.
/// </value>
int Id { get; set; }
}
}
现在,你们每个具体实体都需要实现这个接口:
public class MyEntity : IEntity
{
#region Implementation of IEntity
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>
/// The identifier.
/// </value>
public int Id { get; set; }
#endregion
// Other POCO properties...
}
我发现最好的做法是不要为每个实体创建单独的接口,除非您在高测试环境中工作。务实地说,接口应该只在需要该抽象级别的地方使用;主要是在多个具体类将继承时,或者在使用过度热情的控制反转引擎时。如果您的生产模型中的所有内容都有接口,那么您的架构很可能存在重大缺陷。无论如何,足够的漫无边际。
现在我们已经对所有实体进行了“战略化”,我们可以使用 MEF 来整理它们并在您的上下文中填充一个集合。
在您的上下文中,添加一个新属性:
/// <summary>
/// Gets a dynamically populated list of DbSets within the context.
/// </summary>
/// <value>
/// A dynamically populated list of DbSets within the context.
/// </value>
[ImportMany(typeof(DbSet<IEntity>))]
public IEnumerable<DbSet<IEntity>> Sets { get; private set; }
此处的 [ImportMany(typeof(DbSet<IEntity>))] 允许 MEF 填充集合。
接下来,将相应的Export 属性添加到上下文中的每个 DbSet:
[Export(typeof(DbSet<IEntity>))]
public DbSet<MyEntity> MyEntities { get; set; }
Imported 和Exported 属性中的每一个都称为“部分”。拼图的最后一块是组成这些部分。将以下内容添加到上下文的构造函数中:
// Instantiate the Sets list.
Sets = new List<DbSet<IEntity>>();
// Create a new Types catalogue, to hold the exported parts.
var catalogue = new TypeCatalog(typeof (DbSet<IEntity>));
// Create a new Composition Container, to match all the importable and imported parts.
var container = new CompositionContainer(catalogue);
// Compose the exported and imported parts for this class.
container.ComposeParts(this);
现在,如果运气好的话,您应该在上下文中拥有一个动态填充的 DbSet 列表。
我已使用此方法通过扩展方法轻松截断所有表。
/// <summary>
/// Provides extension methods for DbSet objects.
/// </summary>
public static class DbSetEx
{
/// <summary>
/// Truncates the specified set.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <param name="set">The set.</param>
/// <returns>The truncated set.</returns>
public static DbSet<TEntity> Truncate<TEntity>(this DbSet<TEntity> set)
where TEntity : class, IEntity
{
set.ToList().ForEach(p => set.Remove(p));
return set;
}
}
我在上下文中添加了一个方法来截断整个数据库。
/// <summary>
/// Truncates the database.
/// </summary>
public void TruncateDatabase()
{
Sets.ToList().ForEach(s => s.Truncate());
SaveChanges();
}
编辑(大修):
上述解决方案现已弃用。现在必须做一些调整才能使其正常工作。要完成这项工作,您需要将 DbSet 导入到类型为“object”的 DbSet 的临时集合中,然后将此集合转换为所需接口类型的 DbSet。对于基本目的,IEntity 接口就足够了。
#region Dynamic Table List
/// <summary>
/// Gets a dynamically populated list of DbSets within the context.
/// </summary>
/// <value>
/// A dynamically populated list of DbSets within the context.
/// </value>
public List<DbSet<IEntity>> Tables { get; private set; }
/// <summary>
/// Gets a dynamically populated list of DbSets within the context.
/// </summary>
/// <value>
/// A dynamically populated list of DbSets within the context.
/// </value>
[ImportMany("Sets", typeof (DbSet<object>), AllowRecomposition = true)]
private List<object> TableObjects { get; set; }
/// <summary>
/// Composes the sets list.
/// </summary>
/// <remarks>
/// To make this work, you need to import the DbSets into a temporary collection of
/// DbSet of type "object", then cast this collection to DbSet of your required
/// interface type. For basic purposes, the IEntity interface will suffice.
/// </remarks>
private void ComposeSetsList()
{
// Instantiate the list of tables.
Tables = new List<DbSet<IEntity>>();
// Instantiate the MEF Import collection.
TableObjects = new List<object>();
// Create a new Types catalogue, to hold the exported parts.
var catalogue = new TypeCatalog(typeof (DbSet<object>));
// Create a new Composition Container, to match all the importable and imported parts.
var container = new CompositionContainer(catalogue);
// Compose the exported and imported parts for this class.
container.ComposeParts(this);
// Safe cast each DbSet<object> to the public list as DbSet<IEntity>.
TableObjects.ForEach(p => Tables.Add(p as DbSet<IEntity>));
}
#endregion
接下来,从构造函数运行 CompileSetsList() 外观(显示了 Web 的最佳实践):
public MvcApplicationContext()
{
// Enable verification of transactions for ExecuteSQL functions.
Configuration.EnsureTransactionsForFunctionsAndCommands = true;
// Disable lazy loading.
Configuration.LazyLoadingEnabled = false;
// Enable tracing of SQL queries.
Database.Log = msg => Trace.WriteLine(msg);
// Use MEF to compile a list of all sets within the context.
ComposeSetsList();
}
然后,像这样装饰你的 DbSet:
/// <summary>
/// Gets or sets the job levels.
/// </summary>
/// <value>
/// The job levels.
/// </value>
[Export("Sets", typeof(DbSet<object>))]
public DbSet<JobLevel> JobLevels { get; set; }
现在它可以正常工作了。