0
votes

I am building an EntityFramework/WebApi back end.
I want to decouple my WebApi from the Entity Framework, and utilize Dependency Injection so I can swap out the "data source" for the web API. I have been looking at the Unit of Work and Repository patterns.

I also want to use breezejs.

The breezejs TempHire samples has been alot of help, so I will use this as an example for my question -

https://github.com/Breeze/breeze.js.samples/tree/master/net/TempHire

In this sample, on the data side we have the UnitOfWork class -

public class UnitOfWork
{
    private readonly EFContextProvider<TempHireDbContext> _contextProvider;

    public UnitOfWork()
    {
        _contextProvider = new EFContextProvider<TempHireDbContext>();

        StaffingResources = new Repository<StaffingResource>(_contextProvider.Context);
        Addresses = new Repository<Address>(_contextProvider.Context);
        // .. etc.
    }

    public IRepository<StaffingResource> StaffingResources { get; private set; }
    public IRepository<Address> Addresses { get; private set; }
    // .. etc.

    public SaveResult Commit(JObject changeSet)
    {
        return _contextProvider.SaveChanges(changeSet);
    }
}

Then on the WebApi side, it uses it like this -

[BreezeController]
[Authorize]
public class ResourceMgtController : ApiController
{
    private readonly UnitOfWork _unitOfWork = new UnitOfWork();

    [HttpPost]
    public SaveResult SaveChanges(JObject saveBundle)
    {
        return _unitOfWork.Commit(saveBundle);
    }
    // ... etc.
}

I would like to refactor to something like this, so that I could swap out the back end.

public class UnitOfWork : IUnitOfWork

public class ResourceMgtController : ApiController
{
    private readonly IUnitOfWork _unitOfWork;
    public ResourceMgtController(IUnitOfWork unitOfWork) {  
        this._unitOfWOrk = unitOfWork; // Dependency Injected...
    }    
    // ... etc.
}

What I can't wrap my head around, is how I can make it generic. The breeze client needs a method like this -

[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
    return _unitOfWork.Commit(saveBundle);
}

And I can't put this in IUnitOfWork -

SaveResult SaveChanges(JObject saveBundle)

And really keep it decoupled from breeze, be able to swap out the back end for another backend. Am I attempting the abstraction at the wrong point? I guess if I want breeze on the client I will need to couple it on the backend?

1

1 Answers

1
votes

You clearly can define an interface with that method:

public interface IUnitOfWork {
    ...
    SaveResult SaveChanges(JObject saveBundle); // no problem
}

I suspect that you are objecting to the fact that both SaveResult and JObject are classes defined by libraries (Breeze.ContextProvider and Newtonsoft.Json.Linq respectively) you'd rather not reference somewhere.

These references wouldn't bother me any more than I mind referencing System.Linq to get IQueryable. In fact, a test double of SaveResult (a public class of Breeze.ContextProvider) is trivially easy to construct. Here is its definition (and the definition of KeyMapping, its only non-native dependent type):

public class SaveResult
{
  public List<object> Entities;
  public List<KeyMapping> KeyMappings;
  public List<object> Errors;
}

public class KeyMapping
{
  public string EntityTypeName;
  public object TempValue;
  public object RealValue;
}

But if Breeze and Newtonsoft.Json references are that noxious to you and you're willing to surrender some type safety, you can always create the interface like this:

  public interface IUnitOfWork {
     ...
     object SaveChanges(object saveBundle); // no safety, no problem
  }

Then in your concrete UnitOfWork you add a suitable overload:

public object IUnitOfWork.SaveChanges(object saveBundle)
{
    return SaveChanges((JObject) saveBundle);
}

public SaveResult SaveChanges(JObject saveBundle)
{
    return _contextProvider.SaveChanges(saveBundle);
}

... and Bob's your uncle.

Yes, I did try it (in DocCode); worked fine for me.