通过接口获取DbContext设置<T>

Nuc*_*mer 2 c# generics entity-framework dependency-injection

背景

我有一个多层应用程序,我试图重构.我的数据层本质上只是一个EF数据库上下文(及其所有实体).我想要实现的目标之一是删除我的Biz层和我的数据层之间的依赖关系(使用依赖注入),以便我的单元测试不必命中数据库.为此,我创建了一个我的上下文实现的简单接口IDataStore.

public interface IDataStore
{
   void Insert<T>(T entity) where T:class;
   void Update<T>(T entity) where T:class;
   void Delete<T>(T entity) where T:class;
   T GetById<T>(int id) where T:class;
   IQueryable<T> All<T>() where T:class;
}

public partial class MyContext:DbContext,IDataStore
{
   public void Insert<T>(T entity) where T:class{
      this.Set<T>().Add(entity);
      this.SaveChanges();
   }
   ...
   public IQueryable<T> All<T>() where T:class{
      return this.Set<T>();
   }
}
Run Code Online (Sandbox Code Playgroud)

使用IoC contianer,我能够将我的Biz层中的所有引用删除到MyContext(而不是引用IDataStore),但我仍然卡在引用我的特定实体类.在下面的例子中,我使用的是IDataStore,但我也使用了Data.Color(我的一个实体类).

public static void MyBizMethod(){
   ...
   IDataStore store = myUnityContainer.Resolve<IDataStore>();
   List<string> colorNames = store.All<Data.Color>().Select(c => c.name).ToList();
   ...
}
Run Code Online (Sandbox Code Playgroud)

我接下来要做的是创建一个IColor接口并使用一个分部类来使Data.Color实现IColor.然后,我修改了我的Biz层逻辑,调用DataStore.All <IColor>()而不是DataStore.All <Data.Color>();

public interface IColor{
   string name;
}

public partial class Color: IColor { }
Run Code Online (Sandbox Code Playgroud)

并在我的商务层....

public static void MyBizMethod(){ 
   ...
   List<string> colorNames = store.All<IColor>().Select(c => c.name).ToList();
   ...
}
Run Code Online (Sandbox Code Playgroud)

问题

当使用T接口(在我的情况下为IColor)调用DbContext.Set <T>()时,我得到一个异常,因为上下文有一个DbSet <Color>而不是DbSet <IColor>.

有没有相反,上下文有一个DbSet <IColor>?如果没有,是否有其他方法通过接口从DbContext通用检索DbSet?我真的很想避免写一个巨大的开关声明.

或者......有没有更好的方法来完成这项工作?优选地,不涉及为我拥有的每种类型的实体创建单独的接口,

更新

@doctor留下的帖子解决了我的初始问题(如何通过接口查询表),但是其他帖子解释说这甚至不是必要的(因为我的实体类已经被抽象了),我觉得他们帮助更多了我问题的根源

尽管如此,我想看看其他帖子的解决方案是否适用于我最初提出的问题,而且在大多数情况下确实如此.值得注意的例外是IQueryable的.Include()方法.

请考虑以下代码示例:

var people = store.All<IPerson>().Where(p => p.IsAlive).Include("Children").ToList();
foreach (IPerson parent in people)
{
   Console.WriteLine(parent.Name + " has " + p.Children.Count() + " children");
}
Run Code Online (Sandbox Code Playgroud)

当调用store.All <Person>()时,.Include()会按预期转换为SQL.然而,当使用IPerson时,它似乎被完全忽略,并且每次循环都会生成单独的查询.有趣的是.Where()子句在两种情况下都被正确翻译(我使用SQL分析器验证).无论如何,我决定不为我拥有的每个实体实现单独的接口,而是直接引用实体.只想指出奇怪的.Include()行为.

Str*_*ior 6

大多数情况下,当使用实体框架时,人们不会将业务逻辑放在实体本身上.他们使用像"商务层"类这样的服务来使用这些实体上表示的数据.

如果遵循此模式,则不需要模拟实体,因为它们是数据结构,而不是服务.您可以轻松地创建它们的真实实例.

此外,实体框架中的类类型本身代表了实际的数据集合:您可能有一些实现相同的接口,但当您说"我想要一组Colors"时,您要求的是来自数据库中的特定表.由于您已经与Color数据存储中存在的一组s 的概念紧密耦合,因此您不可能通过使用接口在此级别表示它们来获得任何收益.