我有一个DbContext
,其中包含以下几种类型的成员:
public DbSet<JobLevel> JobLevels { get; set; }
public DbSet<Country> Countries { get; set; }
public DbSet<Race> Races { get; set; }
public DbSet<Language> Languages { get; set; }
public DbSet<Title> Titles { get; set; }
所有这些都是where T: IdNamePairBase
,其中只有Id
和Name
个成员。我正在拼命寻找一个可以访问这些成员的通用接口,将以下MVC3控制器代码概括为一个控制器:
public ActionResult Edit(DropDownListModel model, Guid)
{
var dbSet = _dbContext.Countries;
var newItems = model.Items.Where(i => i.IsNew && !i.IsDeleted).Select(i => new { i.Name });
foreach (var item in newItems)
{
if (!string.IsNullOrWhiteSpace(item.Name))
{
var undead = ((IEnumerable<IdNamePairBase>)dbSet).FirstOrDefault(p => p.Name.ToLower() == item.Name.ToLower());
if (undead != null)
{
// Assign new value to update to the new char. case if present.
undead.Name = item.Name;
undead.IsDeleted = false;
_dbContext.SaveChanges();
continue;
}
var newPair = new Country { Name = item.Name };
dbSet.Add(newPair);
_dbContext.SaveChanges();
}
}
return RedirectToAction("Edit", new {listName = model.ListName});
}
我怎样才能解决我的问题,现在我需要为每个DbContext
成员设置一个控制器,就像上面的那个专用于DbSet<Country> Countries
?
PARTIAL SOLUTION:在下面与GertArnold的答案相似的行之前,在我了解_dbContext.Set<T>
所有亮点之前,我在我的上下文类中实现了此方法以获取特定类型的集合:
public IEnumerable<DbSet<T>> GetDbSetsByType<T>() where T : class
{
//var flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance;
var props = GetType().GetProperties()
.Where(p => p.PropertyType.IsGenericType && p.PropertyType.Name.StartsWith("DbSet"))
.Where(p => p.PropertyType.GetGenericArguments().All(t => t == typeof(T)));
return props.Select(p => (DbSet<T>)p.GetValue(this, null));
}
答案 0 :(得分:9)
使用
可以进行一些概括var dbSet = _dbContext.Set<T>
并将您的大多数方法放在带有泛型类型参数的方法中。
但是,某处应该有一个开关来决定应该指定哪种类型以及要创建哪种类型,因为我认为类型是作为模型的属性提供的(是吗?)。所以它可能不会看起来很优雅,但可能会更短,使用DRY-er代码。
答案 1 :(得分:3)
要添加Gert Arnold的答案,我想要注意dbContext上还有另一个方法重载,它从类型对象返回一般DbSet:
var dbSet = dbContext.Set(typeof(T))
如果要添加盲对象,则使用set.Create()
方法创建对象,或者如果已经使用“new
”keyowrd创建了对象,则可以使用(类似于this answer)
var entity = dbSet.Create();
dbSet.Add(entity);
DbEntityEntry entry = context.Entry(entity);
entry.CurrentValues.SetValues(yourObject);
答案 2 :(得分:3)
我一直在寻找这个问题的答案,我发现使用Managed Extensibility Framework很容易。在这篇文章的底部有一个更快的方法,但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...
}
我发现这是最佳做法,不是为每个实体创建单独的接口,除非您在高测试环境中工作。实际上,接口应仅用于需要抽象级别的地方;主要是当多个具体类继承时,或者使用过度热情的Inversion of Control引擎时。如果您的生产模型中的所有内容都具有接口,那么您的架构很可能存在重大缺陷。无论如何,足够的漫无边际。
现在我们已将所有实体“制定战略”,我们可以使用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; }
每个Import
ed和Export
ed属性都称为“part”。拼图的最后一块是组成这些部分。将以下内容添加到上下文的构造函数中:
// 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&lt;&gt; s:
/// <summary>
/// Gets or sets the job levels.
/// </summary>
/// <value>
/// The job levels.
/// </value>
[Export("Sets", typeof(DbSet<object>))]
public DbSet<JobLevel> JobLevels { get; set; }
现在它可以正常工作。