it:ad:patterns:repository_pattern

IT:AD:Patterns:Repository Pattern


Summary

Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.

MF

* [0] Implemented by EF 4.2 DbSet (DbContext implements UnitOfWork Pattern)

  • Repository Pattern:

    * But a Repo should be able to work against several DataContexts.

  • SOLID would suggest that a Repo constructor should define up front what it needs…
    • It can define up front that it needs a IContext…
      • .. but this ties a Repo to only one DataContext (as ServiceLocator always returns the same Repo).
      • It can define up front instead that it needs an IContextManager, or IUoWManager if there is a 1-1 relationship, which will return a 'Current' UoW/Context.

      * A Controller should declare up front which Repo it wants.

  • Controller(IMyRepo)
    • MyRepo(IUoWManager)
  • If an ObjectContext IS a UoW, then there is a one to one relationship.
    • And the scoping of multiple UoW's is via TransactionScope.

Let's get one thing out of the way first: one can make a vendor-specific generic Repository, but one can't make – in a way that adds value – a generic Repository that is portable across vendors.

One can wrap up the IUnit of work, and Context:

//Create EF specific ObjectContext:
using (ObjectContext origObjectContext = new XAct_App()){
  //Wrap in order to work with portable wrapper:
  using (IObjectContext objectContext = new EntityObjectContext(origObjectContext)){
    //Use it to define a new UoW:
    using (IUnitOfWork uow = new UnitOfWork(abstractObjectContext)){
      using (IRepository repository = new ExampleRepository(objectContext)){
        ...
        repository.MakeChanges(...);
        ...
        uow.Commit();
      }
    }
  }
}

but it really doesn't get you much further along.

The problem is the lazy loading and include statements. Each vendor has a different solution to the problem. There is no .NET Framework portable Extension method for Including child objects, so to get access to these methods, one has to have a reference to the EF assemblies. It's as simple as that. If you try to wrap up all those juicy vendor extensions, you'll never finish – and if you try to go for a lower common denominator, you'll be losing half of the advantages that ORM's can provide. Sounds like a bad strategy.

As far as I can tell, the only solution is to create a VendorSpecificRepositoryBase that returns IQueryable's, enherit from that (eg: ContactsRepository) that uses vendor specific Extensions Methods (Include. etc) and exposes methods that return IEnumerable, not IQueryable().

The reason they have to return IEnumerable, and not IQueryable, is to not infest the calling Domain layer with technology specific methods, including Repository (that's up to the Application Layer to orchestrate).

If the domain is going to call the Reposotory directly – and it's a quick down and dirty application, I guess it could expose its methods as IQueryable. Really wouldn't recommend it as a long term strategy.

A Repository has a 1-1 relationship to its internal DbSet that is wraps in order to manage a single Aggregagate Entity . If there is a cross relationship (for example, Vertices and Edges in a Graph, it's better to have two Repositories, linked by a common Context, instead of one Repository, with two DbSets.

It certainly makes it easier in terms of method duplication, etc.

  • /home/skysigal/public_html/data/pages/it/ad/patterns/repository_pattern.txt
  • Last modified: 2023/11/04 03:29
  • by 127.0.0.1