C# 服务模式实体框架asp.net mvc

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(),但将所有内容分离后会生成如下

我想在我的asp.net mvc entity framework 6项目中进行单元测试,这迫使我查看代码。我让我的控制器暴露在我的上下文中,并决定可能应该将其分离出来。我最初查看的是repository/UoW模式,但在阅读了大量内容后,我决定使用DI的简单服务模式(即,控制器被注入一个具有
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操作,而不是业务。