我知道有很多关于这个话题的文章,但无论我在哪里看,它对我来说都太复杂或不清楚。
我的团队开发首先使用Entity框架代码的Web应用程序。我们还使用Autofac进行依赖注入。
目前,数据访问方式如下:
提供给不包含EF的项目的API:
public class DataService
{
private IDbContextFactory<MyContext> _factory;
private IDataServiceExecutor _dataServiceExecutor;
public DataService(IDbContextFactory<MyContext> factory, IDataServiceExecutor executor)
{
_factory = factory;
_dataServiceExecutor = executor;
}
public void AddItem(Product prod)
{
using (var context = _factory.Create())
{
if (_dataServiceExecutor.AddItem(context, prod))
context.SaveChanges();
}
}
}
我的DataServiceExecutor:
public class DataServiceExecutor
{
private IRepository<Product> _products;
public DataService(IRepository<Product> products...more repositories)
{
_products = products;
}
public bool AddItem(DbContext context, Prouduct prod)
{
try
{
_products.AddItem(context, prod);
return true;
}
catch(Exception ex)
{
Log(..)
return false;
}
}
}
我的所有存储库都继承自这个抽象存储库:
public abstract class EFRepository<T> : IRepository<T>
{
public void AddItem<T>(DbContext context, T item)
{
context.Set<T>().Add(item);
}
.
.
.
}
好处是每个事务都以这种方式使用上下文。
糟糕的是,我的服务方法和我的存储库方法都直接使用上下文。应用程序不是那么大,所以现在方法注入上下文很好,但它可能会变得更大,所以在我看来,在它当前状态下的上下文注入是有问题的。它看起来很糟糕。
也许有更多的优点和缺点,我不知道..
我有什么方法可以让数据访问更好吗?
答案 0 :(得分:1)
像DataServiceExecutor
这样的类(基本上是一个动词)总是会产生设计缺陷。这是一种伪装成类的方法(Execute...
)。这些课程的责任不明确,因为他们的职能不可避免地属于其他课程。
像Inversion of Control这样的大型模式的问题是IoC容器可用于注入任何依赖项。因此,它们允许您创建一系列依赖关系和分散的职责,并且仍然可以在管理生命周期方面做得不错。它们可能会掩盖您的生命周期问题。
所以,让我们暂时忽略IoC ,如果你调用DataServiceExecutor.AddItem
,看看你的代码在创建简单对象时会是什么样子。
var product = new Product();
var factory = new DbContextFactory<MyContext>(); // Dependencies unknown
var productRepository = new Repository<Product>(context);
var executor = new DataServiceExecutor(productRepository);
var dataService = new DataService(factory, executor);
你基本上有dataServiceAddItem
方法:
using (var context = _factory.Create())
{
executor.AddItem(context, product);
context.SaveChanges();
}
如果DataService
收到productRepository
而不是executor
,则会归结为:
productRepository.AddItem(context, product);
context.SaveChanges();
executor
可以轻松取出。它唯一的作用似乎是错误记录。但这也可以由DataService
完成。
但我们还没有完成。正如您自己指出的那样,将上下文作为参数的这些方法有点尴尬。但是现在DataServiceExecutor
已经不合适了,这样做更自然:
var product = new Product();
var factory = new DbContextFactory<MyContext>();
using (var context = _factory.Create())
{
var productRepository = new Repository<Product>(context);
productRepository.AddItem(product);
// And for example
var orderRepository = new Repository<Order>(context);
orderRepository.AddItem(order);
// One SaveChanges call!
context.SaveChanges();
}
DataService
也消失了。
现在EFRepository
将其context
存储为成员变量,AddItem
如下所示:
public void AddItem<T>(T item)
{
_context.Set<T>().Add(item);
}
现在回到IoC。
根据IoC,您的代码的主要问题是DataService.AddItem
:
using (var context = _factory.Create())
您创建了一个不受IoC容器管理的上下文。它的生命周期范围是AddItem
方法。因此,您必须在整个地方传递它,以确保业务事务中的所有内容都使用此实例。通过将存储库的依赖(到上下文)带回构造函数注入,让IoC容器管理上下文的生命周期要容易得多。
顺便说一句,你说DataService
是“提供给不包含EF的项目的API”的一部分。但它确实在其泛型参数中引用了MyContext
。也许MyContext
也是一种抽象,我不知道。无论如何,你也可以通过IoC提供这种抽象的实例。
答案 1 :(得分:0)
我的观点是解决方案没有正确分层。我想DataService是从外界访问的顶层?
在这种情况下,我会改为: