Question

I am learning about good design practices and came upon the Unit of Work design pattern, along with the Repository. This allows me to keep the application logic from knowing about the persistence details, however there are some corner cases that i'm having some difficulty dealing with.

First of all, behind the IUnitOfWork interface i'm using an implementation that accesses my data using EF. For my queries, i use the Specification design pattern to create query objects that the client can use.

So, the thing is pretty much abstracted from the datasource until now, but here's the deal: imagine i want to retrieve some data with the AsNoTracking option. How am i supposed to tell my IUnitOfWork implementation that i dont need the resulting objects to be tracked?

Moreover, EF doesn't really support batch CUD, nor Future Queries, but there is an extension that supports. How do i support this in a way that the client code is totally agnostic to the way i persist objects? I might want to use flat files, for all the code cares.

TL;DR; How do i keep my UnitOfWork abstraction from leaking into my clients? Has anyone dealt with this?

Was it helpful?

Solution

Most of this would be accomplished by injecting a repository who's implementation gives out those non-tracked objects or talks read/writes to flat files.

A lot of what the conceptual repository does is at cross purposes with UoW. The repo implementation ends up having a lot of these:

public class MyRepository : IWhateverRepository
{
    // ...
    public Customer GetCustomerByName(string customerName)
    {
        using (var db = new MyContext())
        {
            var customer = db.Customers.Where(cust => cust.Name == customerName).SingleOrDefault();
            return customer;
        }
    }

    public Customer StoreCustomer(Customer customer)
    {
        using (var db = new MyContext())
        {
            db.Entry(customer).State = customer.ID == 0 ? EntityState.Added : EntityState.Modified;
            db.SaveChanges();
        }
    }
}

As you can see, there isn't a whole lot of usage of the UoW. I think most of the usefulness of EF when using a repository is in not having to write SQL by hand.

However, UoW works well when there is no repository abstraction and the business logic works directly with the entities and the context. For instance in an UI <-> MVC <-> Service Layer scenario you might have the MVC controller instruct the service layer to create a new invoice:

IMyService svc = new MyServiceLayer();
IInvoice invoice = svc.CreateInvoiceForCustomer(customer, items, paymentInfo);
InvoiceViewModel invoiceVm = Map(invoice);
return RedirectToAction("Display", "Invoice", invoiceVm);

You can picture that the service layer might do the following:

public ServiceResponse<Invoice> CreateInvoiceForCustomer(Customer customer, IEnumerable<InvoiceItem> items, PaymentInfo paymentInfo)
{
    using (var db = new MyContext())
    {
        db.Entry(customer).State = customer.ID == 0 : EntityState.Added : EntityState.Modified;

        var invoice = new Invoice
            {
                Customer = customer,
                InvoiceDate = _timeService.Now(),
                PaymentInfo = paymentInfo
            };
        foreach (var item in items)
            invoice.Items.Add(items);

        db.Invoices.Add(invoice);
        db.SaveChanges();

        return new SeviceResponse<Invoice> 
            {
                IsSuccess = true,
                Data = invoice
            };
    }

}

In this example, the UoW pattern acts as a replacement for a transaction. You get Atomicity without getting your hands dirty.

The real power of UoW is in a stateful system, like WPF, where you could have a long-lived context that is used to process an big Queue of commands that are executing asynchronously on a different thread.

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