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()行为.
大多数情况下,当使用实体框架时,人们不会将业务逻辑放在实体本身上.他们使用像"商务层"类这样的服务来使用这些实体上表示的数据.
如果遵循此模式,则不需要模拟实体,因为它们是数据结构,而不是服务.您可以轻松地创建它们的真实实例.
此外,实体框架中的类类型本身代表了实际的数据集合:您可能有一些实现相同的接口,但当您说"我想要一组Colors"时,您要求的是来自数据库中的特定表.由于您已经与Color数据存储中存在的一组s 的概念紧密耦合,因此您不可能通过使用接口在此级别表示它们来获得任何收益.
| 归档时间: |
|
| 查看次数: |
3144 次 |
| 最近记录: |