C# 服务模式实体框架asp.net mvc
我想在我的asp.net mvc entity framework 6项目中进行单元测试,这迫使我查看代码。我让我的控制器暴露在我的上下文中,并决定可能应该将其分离出来。我最初查看的是repository/UoW模式,但在阅读了大量内容后,我决定使用DI的简单服务模式(即,控制器被注入一个具有C# 服务模式实体框架asp.net mvc,c#,asp.net-mvc,entity-framework,asp.net-mvc-4,C#,Asp.net Mvc,Entity Framework,Asp.net Mvc 4,我想在我的asp.net mvc entity framework 6项目中进行单元测试,这迫使我查看代码。我让我的控制器暴露在我的上下文中,并决定可能应该将其分离出来。我最初查看的是repository/UoW模式,但在阅读了大量内容后,我决定使用DI的简单服务模式(即,控制器被注入一个具有GetProducts(),FindProduct()等功能的服务) 我的问题与此模式中的更改跟踪有关。在此之前,我会在控制器方法中完成大量工作后调用SaveChanges(),但将所有内容分离后会生成如下
GetProducts()
,FindProduct()
等功能的服务)
我的问题与此模式中的更改跟踪有关。在此之前,我会在控制器方法中完成大量工作后调用SaveChanges()
,但将所有内容分离后会生成如下方法:
public void AddRequest(Request request)
{
using (PricedNotesContext ctxt = new PricedNotesContext())
{
ctxt.Requests.Add(request);
ctxt.SaveChanges();
}
}
public void DeleteRequestData(BaseRequestData reqData)
{
using (PricedNotesContext ctxt = new PricedNotesContext())
{
ctxt.RequestData.Remove(reqData);
ctxt.SaveChanges();
}
}
在上面您可以看到,SaveChanges()
被调用了两次。但我只想叫一次。我只希望在两个操作都成功完成的情况下,我的更改保持不变。建议的解决方案是将名为SaveChanges()
和Dispose()
的方法公开给控制器以管理事务,该方法显然调用SaveChanges()
和Dispose()
谢谢你的帮助 这是我想到的
public class EfNotesService : INotesService
{
private readonly PricedNotesContext _ctxt = new PricedNotesContext();
public IEnumerable<Request> GetAllRequests()
{
return _ctxt.Requests.ToList();
}
public void SaveRequest(Request request)
{
_ctxt.Entry(request).State = EntityState.Modified;
}
public void DeleteRequestData(BaseRequestData reqData)
{
_ctxt.RequestData.Remove(reqData);
}
// ...
// Other methods...
// ...
// Transaction control
public void SaveChanges()
{
_ctxt.SaveChanges();
}
public void Dispose()
{
_ctxt.Dispose();
}
}
public类EfNotesService:INotesService
{
private readonly PricedNotesContext_ctxt=new PricedNotesContext();
公共IEnumerable GetAllRequests()
{
返回_ctxt.Requests.ToList();
}
公共作废保存请求(请求)
{
_ctxt.Entry(request.State=EntityState.Modified;
}
public void DeleteRequestData(BaseRequestData reqData)
{
_ctxt.RequestData.Remove(reqData);
}
// ...
//其他方法。。。
// ...
//事务控制
公共void SaveChanges()
{
_ctxt.SaveChanges();
}
公共空间处置()
{
_ctxt.Dispose();
}
}
它不是UoW或Repository,但它很简单,而且运行良好。此外,我还保留了DbContext的所有“存储”优势。您的服务应该提供这样一种功能,即消费者不必关心保存上下文。实际上,它不知道上下文。你为什么不做一些简单的事情,比如:
public class EfNotesService : INotesService
{
public ExecuteSomeBusinessOperation(input parameters here)
{
// Validate input parameters
using (PricedNotesContext ctxt = new PricedNotesContext())
{
ctxt.Requests.Add(...);
ctxt.RequestData.Remove(...);
// other logic
ctxt.SaveChanges();
}
}
}
或者更好,您还可以使用依赖项注入在请求开始时注入上下文,并在请求结束时处置上下文。然后将此上下文注入EfNotesService的构造函数中,然后您可以在不使用using语句的情况下使用它:
public class EfNotesService : INotesService
{
private readonly PricedNotesContext _ctxt;
public EfNotesService(PricedNotesContext ctxt )
{
_ctxt = ctxt;
}
public ExecuteSomeBusinessOperation(input parameters here)
{
// Validate input parameters
_ctxt .Requests.Add(...);
_ctxt .RequestData.Remove(...);
// other logic
_ctxt .SaveChanges();
}
}
这样,同一上下文可以跨多个服务,您不必担心在这些服务中创建和处理上下文
此外,在您的服务操作中,您当然可以使用业务组件甚至域层来执行业务逻辑,而不是在服务中执行所有操作
并添加一个全局异常处理程序,以优雅地处理异常。我建议您阅读有关工作单元的内容。在应用程序/业务层中只实例化一次工作单元/上下文,而不是在数据层组件中每次实例化一次。更好的是,注射它。至少,您应该将PriceNotesContext注入到控制器中,而不是在每个方法中实例化它。在web环境中,您实际上不需要工作单元,因为上下文本身就像一个工作单元。不过,我会在BeginRequest中创建一个TransactionScope,然后在EndRequest中提交它。现在你们的操作都不是原子的。好的,这就是我现在所知道的。我已经做了一些事务控制,尽管只是在我的服务方法中使用了SaveChanges()。(问题中的方法在服务中,而不是控制器(它们以前是)。因此,只实例化上下文一次。您现在执行两次。是的,我将添加我的解决方案。一秒钟。这不是“事务控制”在您的服务中。您需要使用TransactionScope来实现这一点。您需要将TransactionScope范围限定到请求,因为它会将所有服务调用包装到同一事务中。此服务的使用者需要实际调用SaveChanges()不是有点奇怪吗?在我看来,这个数据逻辑应该被抽象出来。使用者应该只调用所需的方法,而不应该关心对其调用Save。这是公平的。好的,我会研究其他内容。@L-Four确切地说,您需要一个设置打开的事务范围的事务属性。请看这里是的,好的。谢谢。这是非常c我现在正在使用DI在服务中注入上下文,就像你的例子一样,我不会公开SaveChanges()但是只要在业务操作中处理好它就行了。在一个服务操作中处理这个逻辑并没有什么错——就像这样,你真的暴露了操作的业务意图,而不是简单的CRUD操作。是的,那是我的错误。我在我的服务中做的是CRUD操作,而不是业务。