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

Generic repository and unit of work code

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

Problem

I am writing a WPF application that needs to access information from a database (I am currently using Entity Framework code first, so data access is via DbContext).

My ViewModels directly instantiate my DbContext derived class and query this to obtain the information they require. I understand that this is bad for the following reasons:

  • My ViewModel now has a dependency on DbContext.



  • my viewModel is more difficult to test.



  • It will be difficult to switch to a different data provider if required e.g. ObjectContext instead of DbContext.



The common solution I have seen to this problem is to define a repository to keep my ViewModels unaware of which data access technology I am using.

I have defined the following generic repository class:

public interface IRepository
{
    IEnumerable GetAll();
    IEnumerable Find(Expression> where);

    // other data access methods could also be included.

    void Add(T entity);
    void Attach(T entity);
    void Delete(T entity);
}


My concrete repository looks like this:

public class Repository : IRepository where T : class
{
    private DbSet _entitySet;

    public Repository(DbContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        _entitySet = context.Set();
    }

    public IEnumerable GetAll()
    {
        return _entitySet;
    }

    public IEnumerable Find(Expression> where)
    {
        return _entitySet.Where(where);
    }

    public void Add(T entity)
    {
        _entitySet.Add(entity);
    }

    public void Attach(T entity)
    {
        _entitySet.Attach(entity);
    }

    public void Delete(T entity)
    {
        _entitySet.Remove(entity);
    }
}


Any viewModel that requires access to the database now has a constructor parameter so that a repository can be injected. As far as I can tell this solves all of the problems listed above - my viewModels no longer depend on 'DbContext', they can be tested by using

Solution

NOTE: After Ben correctly commented that he was doing this from the WPF not MVC approach my answer was biased to MVC so although I'll leave it here it it may not have much relevance to what he is actually looking for.

Typically I worked on the assumption that as much as possible my viewModels were DTO's
perhaps with validation attribute capabilities, or I've seen some uses where they use message notification to provide updates to the view. I guess the theory is that the view models themselves could be populated by a variety of methods, or the data used to populate them could come from a variety of places.

From what I gather a view model is essentially a method of of providing a set of data that is required by the specially by the caller/view in question. This data may come from a variety of places, and these places may vary. The viewmodel itself doesn't care, it just transports the data to the view that knows what to do with it.

Also, in my mind by doing this I think unit tests may be easier to write. There are many ways of skinning the cat of course. My favourite flavor of the month is using a service like class to do any mapping from data model to view model. I've also heard modules such as Auto mapper are excellent modules for doing this mapping for you as well.

NOTE: These are just examples of how you might use your Repository and IOW without making them part of your viewmodel.

So in my current way of working, I do something like below:

public class TopLevelClass
{
    private readonly IUnitOfWork _unitOfWork;

    public TopLevelClass(IUnitOfWork iow)
    {
       // injected by a DI framework such as Ninject, Unity ??
       _unitOfWork = iow;
    }

    // just an example of doing something with not using IUnitOfWork in viewmodel
    public void DoMySpecialSomething(string mySpecialId)
    {
       var repository = _unitOfWork.RepositoryFor();
       var mySpecialModel = repository.SingleOrDefault(p => p.Id = mySpecialId);

       var viewModel = new MySpecialService().GetViewModel(mySpecialModel);

       // do something with my view model ???
    }

    public MySpecialViewModel GetSomethingEvenMoreSpecial(string mySpecialName, string goldMedal)
    {
       var mySpecialModel = new MySpecialModel
                               {
                                  Name = mySpecialName,
                                  OlympicMedal = goldMedal
                               };

       return new MySpecialService().GetViewModel(mySpecialModel);
    }
}

// Maybe this might also work from an IMySpecialService interfaces ????
public class MySpecialService
{
    public MySpecialViewModel GetViewModel(MySpecialModel model)
    {
       return new MySpecialViewModel
                {
                   Id = model.Id,
                   IsEnabled = !string.isNullOrEmpty(model.Name),
                   Name = model.Name,
                   OlympicMedal = model.Medal
                };
    }
}

public class MySpecialViewModel
{
    [DisplayName("Your Id")]
    public int Id { get; set; }
    // other properties etc
}

public class MySpecialModel
{
   public int Id { get; set; }
   // other properties here
}


I guess if I was doing TDD I would have created the view model and stubbed the MySpecialService first and created perhaps a test like

// Using standard microsoft testing project
[TestMethod]
public void MySpecialModel_IsValidTest()
{
   var model = new MySpecialModel
                 {
                    Id = 45
                 };

   var service = new MySpecialService();
   var viewModel = service.GetViewModel(model);

   Assert.AreEqual(model.Id, 45);
}  

I hope that might give you some ideas in what you might want to (or not) do differently.

Code Snippets

public class TopLevelClass
{
    private readonly IUnitOfWork _unitOfWork;

    public TopLevelClass(IUnitOfWork iow)
    {
       // injected by a DI framework such as Ninject, Unity ??
       _unitOfWork = iow;
    }

    // just an example of doing something with not using IUnitOfWork in viewmodel
    public void DoMySpecialSomething(string mySpecialId)
    {
       var repository = _unitOfWork.RepositoryFor<MySpecialModel>();
       var mySpecialModel = repository.SingleOrDefault(p => p.Id = mySpecialId);

       var viewModel = new MySpecialService().GetViewModel(mySpecialModel);

       // do something with my view model ???
    }

    public MySpecialViewModel GetSomethingEvenMoreSpecial(string mySpecialName, string goldMedal)
    {
       var mySpecialModel = new MySpecialModel
                               {
                                  Name = mySpecialName,
                                  OlympicMedal = goldMedal
                               };

       return new MySpecialService().GetViewModel(mySpecialModel);
    }
}

// Maybe this might also work from an IMySpecialService interfaces ????
public class MySpecialService
{
    public MySpecialViewModel GetViewModel(MySpecialModel model)
    {
       return new MySpecialViewModel
                {
                   Id = model.Id,
                   IsEnabled = !string.isNullOrEmpty(model.Name),
                   Name = model.Name,
                   OlympicMedal = model.Medal
                };
    }
}

public class MySpecialViewModel
{
    [DisplayName("Your Id")]
    public int Id { get; set; }
    // other properties etc
}

public class MySpecialModel
{
   public int Id { get; set; }
   // other properties here
}
// Using standard microsoft testing project
[TestMethod]
public void MySpecialModel_IsValidTest()
{
   var model = new MySpecialModel
                 {
                    Id = 45
                 };

   var service = new MySpecialService();
   var viewModel = service.GetViewModel(model);

   Assert.AreEqual(model.Id, 45);
}  

I hope that might give you some ideas in what you might want to (or not) do differently.

Context

StackExchange Code Review Q#14226, answer score: 2

Revisions (0)

No revisions yet.