Question

How do I use DTO's with EF, Autofac, and a generic data repository?

I'm in the process of setting up a new project and have configured my DI/IoC (Autofac), using a generic repository for data access through EF, and created an OData service endpoint, ContentTypesController.

Here's the top part of the controller:

public class ContentTypesController : ODataController, IContentTypesController
{

    // add repository reference
    private readonly IRepository<ContentType> _repository;

    private ProjectV001Context _db = new ProjectV001Context();

    /// <summary>
    /// Constructor - setup for DI
    /// </summary>
    /// <param name="contentTypeRepository"></param>
    public ContentTypesController(IRepository<ContentType> repository)
    {
        _repository = repository;
    }

    // GET odata/ContentTypes
     [Queryable]
    public Task<IQueryable<ContentType>> Get(ODataQueryOptions<ContentType> options)
    {
        // mock
        var userId = 102;
        //var unitOfWork = new Repository.UnitOfWork(_db);   <-- removed from the original question
        var result = _repository.Query()
            .Filter(u => u.UserId == userId)
            .GetAsync();

        return result;
    }

ContentType is the EF class in Project.DAL.Data.Models, which contains fields that do not need to be seen/used by the client - ie. UserId. In inserting or updating methods, the value of this field will be set pragmatically.

My DTO is located in Project.Core.ContentTypes.

1) Am I using the unit of work pattern here correctly?

2) I want my methods to return the DTO, and not all the fields from the actual entity object. How do I set this up?

-- UPDATE --

In a previous project (and different repository) I was implementing the UoW as follows:

public class ContentTypesController : ODataController { private ProjectDbContext _db = new ProjectDbContext();

public IEnumerable<ContentType> Get(ODataQueryOptions<ContentType> options)
{
    var unitOfWork = new Project.Repository.UnitOfWork(_db);

    var contentTypes = options.ApplyTo(unitOfWork.Repository<ContentType>().Queryable
        .OrderBy(c => c.Description))
        .Cast<ContentType>().ToList();

    unitOfWork.Save();      // includes Dispose()

    return contentTypes;
}
Was it helpful?

Solution

Well you're not actually using the UnitOfWork pattern; just the generic repository pattern. As for returning DTOs, look into the Factory pattern (I use AutoMapper for this). You can create your dtos and use automapper as a factory to take your entities and convert to dtos or your dtos and convert to entities. Configuring AutoMapper is pretty simple and looks like:

Mapper.CreateMap<ContentType, ContentTypeDto>();

And to actually map a ContentType to ContentTypeDto (assuming your property names match):

var dto = Mapper.Map<ContentType, ContentTypeDto>(contentTypeEntity);

As for unit of work: there's nothing wrong with not using one unless you plan on swapping out your ORM sometime. But if you do want it, it's pretty simple:

public class UnitOfWork : IUnitOfWork
{
    private readonly IDbContext _context;
    private readonly Dictionary<Type, object> _repos;

    public IRepository<T> GetRepository<T>() {
        if (_repos.ContainsKey(typeof (T)))
            return _repos[typeof (T)] as IRepository<T>;

        var repo = new Repository(_context);
        _repos.Add(typeof (T), repo);

        return repo;
    }

    public int SaveChanges() {
        return _context.SaveChanges();
    }

    dispose stuff - make sure to dispose your context
}

IUnitOfWork implements IDisposable and defines GetRepository() and SaveChanges().

You'll also have to create IDbContext which just defines Set, SaveChanges(), and implements IDisposable.

Then you can inject IUnitOfWork into your controller and use GetRepository() to retrieve the correct repository for your controller. This way, all repositories share 1 single context.

Implement a Generic Repository and a Unit of Work Class example is about half way down this page. Only difference is their UnitOfWork is not generic but I showed you that above.

Edit: I just noticed you're getting a UnitOfWork from your repository:

var unitOfWork = new Repository.UnitOfWork(_db);
var result = unitOfWork.Repository<ContentType>()...

I'm not sure what's going on here. Your UnitOfWork should be dishing out your repositories but it seems like your repository is giving you your unit of work, and then your UnitOfWork is giving you back a repository? Would you mind sharing your repository? I believe to correctly implement UnitOfWork, you'll need to edit your repository.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top