0

I have a scenario using WebApi, Generic Repository, EF6 and unit of work pattern (in order to wrap all changes from several calls to the same context.)

Manager layer is used to perform calls to different repositories and also to other managers.

Currently Customer Manager does inject both repos and other Managers like:

public class CustomerManager  {
    public CustomerManager(IRepository<Customer> _customerRepository, IRepository<Order> orderRepository, IManager itemManager) {
        _orderReporsitory = orderReporsitory;
        _itemManager = itemManager;
        _customerRepository = customerRepository;
}

    public bool Save(Customer customer) {
        _orderReporsitory.Find...
        _itemManager.IsItemUnique(ItemId)
        _customerRepository.Save(customer);
    }
}

This code does not compile, for reference only.

Approaches like this

http://blog.longle.net/2013/05/11/genericizing-the-unit-of-work-pattern-repository-pattern-with-entity-framework-in-mvc/

Will wrap several repositories under a unit of work and flush the changes all together.

My issue involves also adding another Manager layer, to be wrapped also inside unit of work and allow both calls to repositories and other managers (as I want to reuse some manager logic. Like in the example, I am re-using some ItemManager logic)

This code https://stackoverflow.com/a/15527444/310107

using (var uow = new UnitOfWork<CompanyContext>())
{
  var catService = new Services.CategoryService(uow);
  var custService = new Services.CustomerService(uow);

  var cat = new Model.Category { Name = catName };
  catService.Add(dep);

  custService.Add(new Model.Customer { Name = custName, Category = cat });

  uow.Save();
}

is using something similar of what I need but I would also like to be able to inject the services to unit test them (and not creating instances in the body of my manager/service method)

What would the best approach to do this ?

Thanks

Community
  • 1
  • 1
nerlijma
  • 935
  • 1
  • 9
  • 24
  • Related: https://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why – Steven Apr 13 '15 at 21:12
  • How this is related to my question ? I want a way to reuse services within the same uow. Services that call other services. And every service can also call repositories. – nerlijma Apr 13 '15 at 22:26
  • Sorry, I should have added a bit more context. That answer describes how you can remove the creation and mamagement of the unit of work and improve your overall application design using generic interfaces and decorators. – Steven Apr 14 '15 at 05:35
  • any useful code that matches my example from above will be appreciated. – nerlijma Apr 14 '15 at 20:01

1 Answers1

2

Your code snippet with the unit of work has several problems, such as:

  • You create and dispose the unit of work explicitly within that method, forcing you to pass along that unit of work from method to method and class to class.
  • This causes you to violate the Dependency Inversion Principle, because you now depend on concrete types (CategoryService and CustomerService), which complicates your code and makes your code harder to test.
  • If you need to change the way the unit of work is created, managed or disposed, you will have to make sweeping changes throughout the application; A violation of the Open/Closed Principle.

I expressed these problems in more details in this answer.

Instead, I propose to have one DbContext, share it through a complete request, and control its lifetime in the application's infrastructure, instead of explicitly throughout the code base.

A very effective way of doing this is by placing your service layer behind a generic abstaction. Although the name of this abstraction is irrelevant, I usually call this abstraction 'command handler:

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

There are a few interesting things about this abstaction:

  1. The abstraction describes one service operation or use case.
  2. Any arguments the operation might have are wrapped in a single message (the command).
  3. Each operation gets its own unique command class.

Your CustomerManager for instance, might look as follows:

[Permission(Permissions.ManageCustomerDetails)]
public class UpdateCustomerDetailsCommand {
    public Guid CustomerId { get; set; }
    [Required] public string FirstName { get; set; }
    [Required] public string LastName { get; set; }
    [ValidBirthDate] public DateTime DateOfBirth { get; set; }
}

public class UpdateCustomerDetailsCommandHandler
    : ICommandHandler<UpdateCustomerDetailsCommand> {

    public UpdateCustomerDetailsCommandHandler(
        IRepository<Customer> _customerRepository, 
        IRepository<Order> orderRepository, 
        IManager itemManager) {
        _orderReporsitory = orderReporsitory;
        _itemManager = itemManager;
        _customerRepository = customerRepository;
    }

    public void Handle(UpdateCustomerDetailsCommand command) {
        var customer = _customerRepository.GetById(command.CustomerId);
        customer.FirstName = command.FirstName;
        customer.LastName = command.LastName;
        customer.DateOfBirth = command.DateOfBirth;
    }
}

This might look like just a bunch of extra code, but having this message and this generic abstraction allows us to easily apply cross-cutting concerns, such as handling the unit of work for instance:

public class CommitUnitOfWorkCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand> {

    private readonly IUnitOfWork unitOfWork;
    private readonly ICommandHandler<TCommand> decoratee;

    public CommitUnitOfWorkCommandHandlerDecorator(
        IUnitOfWork unitOfWork,
        ICommandHandler<TCommand> decoratee) {
        this.unitOfWork = unitOfWork;
        this.decoratee = decoratee;
    }

    public void Handle(TCommand command) {
        this.decoratee.Handle(command);
        this.unitOfWork.SaveChanges();
    }
}

The class above is a decorator: It both implements ICommandHandler<TCommand> and it wraps ICommandHandler<TCommand>. This allows you to wrap an instance of this decorator around each command handler implementation and allow the system to transparently save the changes made in the unit of work, without any piece of code having to do this explicitly.

It is also possible to create a new unit of work here, but the easiest thing to start with is to let the unit of work live for the duration of the (web) request.

This decorator will however just be the beginning of what you can do with decorators. For instance, it will be trivial to:

  • Apply security checks
  • Do user input validation
  • Run the operation in a transaction
  • Apply a deadlock retry mechanism.
  • Prevent reposts by doing deduplication.
  • Register each operation in an audit trail.
  • Store commands for queuing or background processing.

More information can be found in the articles, here, here and here.

Maarten
  • 22,527
  • 3
  • 47
  • 68
Steven
  • 166,672
  • 24
  • 332
  • 435
  • thanks Steven to direct the answer to my sample +1. What about inside the command, if it has many dependencies injected. Like being a complex task that performs many queries to repositories or other services. How to do you maintain it simple ? Can a command call another command? – nerlijma Apr 16 '15 at 17:38
  • 1
    @nerlijma There are multiple visions on what a command is. In my view commands should not be nested. About how to keep things simple, that's a whole discussion on its own, but if a handler gets too big, smaller services should be extracted out of it. – Steven Apr 16 '15 at 17:46