背景
我有一个多层应用程序,我正在尝试重构。我的数据层本质上只是一个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>();
}
}
使用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();
...
}
我接下来要做的是创建一个IColor接口并使用一个分部类来使Data.Color实现IColor。然后,我修改了我的Biz层逻辑,以调用DataStore.All&lt; IColor&gt;()代替DataStore.All&lt; Data.Color&GT;();
public interface IColor{
string name;
}
public partial class Color: IColor { }
并在我的商务层......
public static void MyBizMethod(){
...
List<string> colorNames = store.All<IColor>().Select(c => c.name).ToList();
...
}
问题
当DbContext.Set&lt;使用T接口调用T&gt;()(在我的情况下是IColor)我得到一个异常,因为上下文有一个DbSet&lt;色&GT;不是DbSet&lt; ICOLOR取代。
是否有某种情况使上下文具有DbSet&lt; ICOLOR&GT;代替?如果没有,是否有其他方法通过接口从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");
}
调用store.All&lt; Person&gt;(),.Include()会按预期转换为SQL。然而,当使用IPerson时,它似乎被完全忽略,并且每次循环都会生成单独的查询。有趣的是.Where()子句在两种情况下都被正确翻译(我使用SQL分析器验证)。无论如何,我决定不为我拥有的每个实体实现单独的接口,而是直接引用实体。只想指出奇怪的.Include()行为。
答案 0 :(得分:6)
大多数情况下,当使用Entity Framework时,人们不会将业务逻辑放在实体本身上。他们像您的“商务层”类一样使用服务来使用这些实体上显示的数据。
如果您遵循此模式,则实体不需要模拟,因为它们是数据结构,而不是服务。您可以轻松地创建它们的真实实例。
此外,实体框架中的类类型本身代表了实际的数据集合:您可能有一些实现相同的接口,但当您说“我想要Color
的集合时“你要求数据库中特定表格中的那些。由于您已经与数据存储中存在的一组Color
概念紧密耦合,因此您不可能通过使用接口在此级别表示它们来获得任何收益。