'Where to place business logic layer in relation to data access while providing unit testability by dependency injection?

I am looking for a way to have a separate layer of business logic in a MVC4 app with Entity Framework, without having to inject my (real or fake) database context in it. Right now we have a fake and real database context that derives from IContext, a repository Repository and a controller MyController. The IContext is injected into a controller, and also the repository, with this pattern:

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); 
    }
    (...)
}

This is set up like that since it is the repository which has method that access the database and need a fake context, while the unit tests aim at the controllers and needs to be instantiated.

Now I am looking for a way to add a business logic class to the project where methods will be put that have business logic. These methods will need information obtained from the data access layer (Repository) as well.

It feels a bit strange to also inject the IContext for the Repository in this business logic layer with the above pattern. E.g.,

BusinessLogic(IContext db) 
{
     _repo = new Repository(db);
}

I see two ways to avoid this problem:

  1. Call the repository in the controllers and use data obtained as parameters to methods in the business logic layer.
  2. Inject anyway.

I am looking for a good idea to set up the business logic layer in relation to data access and provide unit testability, preferably without having to inject the database context in every class.

Hope someone can provide me with some insights. Thanks.



Solution 1:[1]

You have your EF context , which inherits from IContext, or DbContext if you want to use EF6:

    public class UserContext : DbContext 
    {
        DbSet<User> users { get; set; }        
    }

You don't want to inject your context directly into your Controler.

Use a Service / DAO / Repository that takes a IContext / DbContext instance in the constructor and handles the data access. In my example I use an IUserService that takes a context and returns a user by ID.

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

    public class UserService : IUserService
    {

      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();
      }

    }

Then inject the Service via interface into your controller.

  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));
     }
 }

Also, consider using a mocking framework to mock your context e.g. 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);
}

Solution 2:[2]

I agree with GMich. As well... There should be hard boundaries between your layers. It could look something like this:

IUserController <-- IUserProcessor <-- IUserRepository <-- IContext

Your DI and constructors could be:

public UserController(IUserProcessor userProcessor){...}
public UserProcessor(IUserRepository userRepository){...}
public UserRepository(IContext context){...}
public MyAppContext(string connectionString){...}

It is also convenient to create containers which encapsulate business logic and repositories as properties. For example:

public class RepositoryContainer : IRepositoryContainer
{
  private readonly IContext _context;
  
  private IUserRepository _userRepository;
  private IUserProfileRepository _userProfileRepository;
  
  public RepositoryContainer(IContext context)
  {
    if (context == null) throw new ArgumentNullException("context");
    
    _context = context;
  }
  
  public IUserRepository UserRepository
  {
    get 
    { 
      return _userRepository = _userRepository ?? new UserRepository(_context); 
    }
  }
  
  public IUserProfileRepository UserProfileRepository
  {
    get
    {
      return _userProfileRepository = _userProfileRepository ?? new UserProfileRepository(_context);
    }
  }
}
And to clarify, when using this approach, there could be a IBusinessLogicContainer as well. We wouldn't want to join BL and Repo objects in the same container.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Shirkan
Solution 2 Community