HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpCritical

Unit of Work and Repository with Entity Framework 6

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withrepositoryworkentityandframeworkunit

Problem

Based on the reply of this question I have created the following code. I need to check whether it's good or not.

Here is my entity class:

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Designation { get; set; }
}


This is my db context implementation:

public class MyDataContext : DbContext where T:class
{
    private IDbSet _dbSet;

    public MyDataContext() : base("name=DefaultConnectionString")
    {
        _dbSet = this.Set();
    }

    public MyDataContext(IDbSet dbSet )
        : base("name=DefaultConnectionString")
    {
        this._dbSet = dbSet;
    }

    public IDbSet DbSetOjbect
    {
        get { return _dbSet; }
    }
}


Now I have implemented the EmployeeService business logic class and the IEmployee service class:

public interface IEmployeeService
{
    List GetEmployees();
}


Here is the implementation:

public class EmployeeService : IEmployeeService 
{
    private IDbSet employee;

    public EmployeeService()
    {
        var employeeContext = new MyDataContext();
        employee = employeeContext.DbSetOjbect;
    }

    public EmployeeService(IDbSet employee) 
    {
        this.employee = employee;
    }

    public List GetEmployees() 
    {
        return employee.ToList();
    }
}


The following is my controller code in ASP.NET MVC controller.

public class EmployeeController : Controller
{
    private readonly IEmployeeService _employeeService;

    public EmployeeController()
    {
        _employeeService = new EmployeeService();
    }

    public EmployeeController(IEmployeeService employeeService)
    {
        _employeeService = employeeService;
    }

    public ActionResult Index()
    {
        return View(_employeeService.GetEmployees());
    }
}


I want to check whether it is a good approach for TDD Test Driven Development or not.

Solution

I've never TDD'd, but don't do that:

public class MyDataContext : DbContext where T : class


This gives you a context-per-entity, which might work for ultra-simplistic CRUD scenarios, but doesn't scale very well and will quickly give you headaches as soon as you need to deal with more than a single entity type in a single transaction - because that's what a unit-of-work encapsulates: a transaction.

DbContext is a unit-of-work, and IDbSet is a repository; they are an abstraction; by wrapping it with your own, you're making an abstraction over an abstraction, and you gain nothing but complexity.

This blog entry sums it up pretty well. In a nutshell: embrace DbContext, don't fight it.

If you really want/need an abstraction, make your DbContext class implement some IUnitOfWork interface; expose a Commit or SaveChanges method and a way to get the entities:

public interface IUnitOfWork
{
    void Commit();
    IDbSet Set() where T : class;
}


Then you can easily implement it:

public class MyDataContext : DbContext, IUnitOfWork
{
    public void Commit()
    {
        SaveChanges();
    }
}


I don't like IEmployeeService either. This looks like an interface that can grow hair and tentacles and become quite a monster (GetByName, FindByEmailAddress, etc.) - and the last thing you want is an interface that needs to change all the time.

I'd do it something like this, but I'm reluctant to use the entity types directly in the views, I'd probably have the service expose EmployeeModel or IEmployee instead (see this question for more details - it's WPF, but I think lots of it applies to ASP.NET/MVC), so as to only have the service class aware of the Employee class, leaving the controller and the view working off some IEmployee implementation, probably some EmployeeModel class, idea being to separate the data model from the domain model.

public class EmployeeService
{
    private readonly IUnitOfWork _unitOfWork;

    public EmployeeService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    IEnumerable GetEmployees()
    {
        return _unitOfWork.Set().ToList();
    }
}

Code Snippets

public class MyDataContext<T> : DbContext where T : class
public interface IUnitOfWork
{
    void Commit();
    IDbSet<T> Set<T>() where T : class;
}
public class MyDataContext : DbContext, IUnitOfWork
{
    public void Commit()
    {
        SaveChanges();
    }
}
public class EmployeeService
{
    private readonly IUnitOfWork _unitOfWork;

    public EmployeeService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    IEnumerable<Employee> GetEmployees()
    {
        return _unitOfWork.Set<Employee>().ToList();
    }
}

Context

StackExchange Code Review Q#47879, answer score: 54

Revisions (0)

No revisions yet.