我找到了一个适合我的答案。我在上下文中将我的 DbSet 属性声明为派生接口,例如:
IDerivedDbSet<Customer> Customers { get; set; }
IDerivedDbSet<CustomerOrder> CustomerOrders { get; set; }
我的实现包括一个在构造函数中分配的私有 IDbSet,例如:
public class DerivedDbSet<T> : IDerivedDbSet<T> where T : class
{
private readonly IDbSet<T> _dbSet;
public DerivedDbSet(IDbSet<T> dbSet)
{
this._dbSet = dbSet;
}
...
}
我对派生 DbContext 接口的实现隐藏了 Set<>() 方法,如下所示:
new public IDerivedSet<TEntity> Set<TEntity>() where TEntity : class
{
//Instantiate _dbSets if required
if (this._dbSets == null)
{
this._dbSets = new Dictionary<Type, object>();
}
//If already resolved, return stored reference
if (this._dbSets.ContainsKey(typeof (TEntity)))
{
return (IDerivedSet<TEntity>) this._dbSets[typeof (TEntity)];
}
//Otherwise resolve, store reference and return
var resolvedSet = new GlqcSet<TEntity>(base.Set<TEntity>());
this._dbSets.Add(typeof(TEntity), resolvedSet);
return resolvedSet;
}
派生的 DbContext 返回一个新构造的 IDerivedSet 或选择它在字典中缓存的引用。在派生的 DbContext 中,我从构造函数调用一个方法,该方法使用类型反射来遍历 DbContexts 属性并使用它自己的 Set 方法分配值/引用。看这里:
private void AssignDerivedSets()
{
var properties = this.GetType().GetProperties();
var iDerivedSets =
properties.Where(p =>
p.PropertyType.IsInterface &&
p.PropertyType.IsGenericType &&
p.PropertyType.Name.StartsWith("IDerivedSet") &&
p.PropertyType.GetGenericArguments().Count() == 1).ToList();
foreach (var iDerivedSet in iDerivedSets)
{
var entityType = iDerivedSet.PropertyType.GetGenericArguments().FirstOrDefault();
if (entityType != null)
{
var genericSet = this.GetType().GetMethods().FirstOrDefault(m =>
m.IsGenericMethod &&
m.Name.StartsWith("Set") &&
m.GetGenericArguments().Count() == 1);
if (genericSet != null)
{
var setMethod = genericSet.MakeGenericMethod(entityType);
iDerivedSet.SetValue(this, setMethod.Invoke(this, null));
}
}
}
}
对我来说是一种享受。我的上下文类具有我的集合类型的可导航集合属性,它实现了继承 IDbSet 的派生接口。这意味着我可以在我的集合类型中包含查询方法,以便查询是可单元测试的,而不是使用 Queryable 类中的静态扩展。(Queryable 方法由我自己的方法直接调用)。