在哪里放置与数据访问相关的业务逻辑层,同时通过依赖注入提供单元可测试性?

Erw*_*ers 0 c# unit-testing entity-framework dependency-injection asp.net-mvc-4

我正在寻找一种方法,在具有Entity Framework的MVC4应用程序中拥有单独的业务逻辑层,而无需在其中注入我的(真实或虚假)数据库上下文.现在我们有一个假的,真实的数据库上下文,它来自IContext一个存储库Repository和一个控制器MyController.IContext使用以下模式将注入到控制器和存储库中:

public class MyController : Controller
{
    readonly Repository _repo;

    /// <summary>
    ///     Initializes a new instance of the <see cref="Controllers.MyController"/> class which
    ///     makes use of the real Entity Framework context that connects with the database.
    /// </summary>
    public MyController()
    {
        _repo = new Repository();
    }

    /// <summary>
    ///     Initializes a new instance of the <see cref="Controllers.MyController"/> class which
    ///     can make use of a "Fake" Entity Framework context for unit testing purposes.
    /// </summary>
    /// <param name="db">Database context.</param>
    public MyController(IContext db)
    {
        _repo = new Repository(db); 
    }
    (...)
}
Run Code Online (Sandbox Code Playgroud)

这是这样设置的,因为它是具有访问数据库并需要伪上下文的方法的存储库,而单元测试针对控制器并且需要实例化.

现在我正在寻找一种方法来向项目添加业务逻辑类,其中将放置具有业务逻辑的方法.这些方法也需要从数据访问层(Repository)获得的信息.

使用上述模式在此业务逻辑层中注入IContextfor 感觉有点奇怪Repository.例如,

BusinessLogic(IContext db) 
{
     _repo = new Repository(db);
}
Run Code Online (Sandbox Code Playgroud)

我看到两种方法来避免这个问题:

  1. 调用控制器中的存储库,并将作为参数获取的数据用于业务逻辑层中的方法.
  2. 无论如何都要注射.

我正在寻找一个好主意,建立与数据访问相关的业务逻辑层,并提供单元可测试性,最好不必在每个类中注入数据库上下文.

希望有人能为我提供一些见解.谢谢.

小智 5

如果要使用EF6,则具有继承自IContext或DbContext的EF上下文

    public class UserContext : DbContext 
    {
        DbSet<User> users { get; set; }        
    }
Run Code Online (Sandbox Code Playgroud)

您不希望将上下文直接注入Controler.

使用Service/DAO/Repository,它在构造函数中获取IContext/DbContext实例并处理数据访问.在我的示例中,我使用IUserService接受上下文并按ID返回用户.

    public interface IUserService
    {        
       User GetUserByID(int userId);        
    }

    public class UserService : IService
    {

      private readonly UserContext _context;

      //Inject your mock or real context here
      public UserService(UserContext context)
      { 

         this._context = context;

      }

      //Implement IUserService
      public User GetUserByID(int userId)
      {  
          _context.Users.Where(u=>u.ID==userId).FirstOrDefault();
      }

    }
Run Code Online (Sandbox Code Playgroud)

然后通过接口将服务注入控制器.

  public class UserController : Controller
  {

     private readonly IUserService _service;

     //Consider using a Dependency injection framework e.g. Unity
     public UserController(IUserService service)
     { 
        this.service = service;
     } 

     //The method that is tightly coupled to the view and uses the service
     [HttpGet] 
     public ActionResult GetUserByID(int id)
     {
          return View(_service.GetUserById(id));
     }
 }
Run Code Online (Sandbox Code Playgroud)

另外,考虑使用模拟框架来模拟您的上下文,例如Moq

[TestMethod]
public void UserServiceTest()
{
        //Initialize your Mock Data
        var testDataUser = new List<Users> 
        { 
            new User{
                                ID = 1,
                                Name = "MockUser"
            }
        }.AsQueryable();

         //Initialize the Mock DbSet
        var mockSetUser = new Mock<DbSet<User>>();
        mockSetUser.As<IQueryable<User>>().Setup(m => m.Provider).Returns(testDataUser. .Provider);
        mockSetUser.As<IQueryable<User>>().Setup(m => m.Expression).Returns(testDataUser .Expression);
        mockSetUser.As<IQueryable<User>>().Setup(m => m.ElementType).Returns(testDataUser .ElementType);
        mockSetUser.As<IQueryable<User>>().Setup(m => m.GetEnumerator()).Returns(testDataUser .GetEnumerator());

         //Initialize the mock Context
        var mockContext = new Mock<UserContext>();

        //Return the mock DbSet via the mock context
        mockContext.Setup(c => c.Users).Returns(mockSetUser.Object);

        //Create the service and inject the mock context
        var userService = new UserService(mockContext.Object)

        //Test your context via the service
        var user = userService.GetUserByID(1);

        Assert.AreEqual("MockUser", user.Name);
}
Run Code Online (Sandbox Code Playgroud)