it:ad:patterns:unitofwork_pattern

IT:AD:Patterns:UnitOfWork Pattern


According to M.F. a UoW “keeps track of everything you do during a business transaction that can affect the Db. When you're done, it figures out everything that needs to be done to alter the database as a result of your work.”

  • In other words, it is the EF ObjectContext that contains methods to Add, Attach, Update, Delete, Commit.
  • Creating a Repository (Repository per Aggregate Root Entity can lead to a scenario with the various Repositories are working with different Db contexts (ObjectContect (or DbContext)).
  • A way to ensure they all use the same Db COntext – and syncronize their Commit across them – is the UnitOfWork Pattern.
  • In essense:
    • a UnitOfWork wraps a common DbContext,
      • it is injected into each Repository
      • The repository does not concern itself with the notion of Commit()
      • When a commit is required, the Controller invokes the UnitOfWork's Commit method, not the Repository's.
      • It is true that the UnitOfWork's wrapped DbContext does it's Commit all in one transaction, but The UnitOfWork

The above is a rather simplistic scenario.

  • Regarding the relationship between Repository and Object Context:
    • In 99% of cases, a Repository will work with only one Db Context. In which case:
      • The DbContext could be injected into the Repository.
        • Pros:
          • Follows SOLID methodology.
        • Cons:
          • As Context was created prior to Repo, Repo can't close it and start another. Ie: Repository cannot work with secondary Contexts, or allow and partial rollbacks.
    • A UnitOfWork (wrapping a DbContext) can be injected into the Repository:
      • Pros:
        • Follows SOLID methodology.
      • Cons:
        • As Context was created prior to Repo, Repo can't close it and start another. Ie: Repository cannot work with secondary Contexts, or allow and partial rollbacks.
          • Note that if UoW has a Current property, it could wrap a IUoWManager (see below), but I consider that spaghetti (actually Chicken or the Egg) pattern.
    • The Repository could have an internal property called DbContext that invokes a UnitOfWorkManager.Current that in turn wraps a UnitOfWorkFactory if the Current hasn't been created yet.
      • Pros:
        • Can now work with a Current (or a Stack) context.
      • Cons:
        • Lose SOLID pattern to some extent (DI is shifted from DbContext to a UnitOfWorkManager injection).
    • But there is that other 1% case:
      • It could be that in some cases one is working with an default DbContext, and a TOmbstone DbContext that mirrors the same schema, but without any triggers (this is a common design pattern for keeping track of offline delete scenarios, while being able to undelete the records).
      • Therefore there is no 1-1 relationship between

Could resolve the 1% by something like this:

class ContactRepository : IContactRepository : IGenericRepository {}
class TombstoneContactRepository : ITombstoneContactRepository : IContactRepository {}

that have constructor signatures for:

IUnifWork and ITombstoneUnitOfWork respectively.

What's not addressed above are two other questions.

  • Does a UnitOfWork have a 1-1 relationship to a DbContext?
  • Transactions / Multiple DbContexts.

MF defined a UnitOfWork to be against a Repository. And in 99% of the cases a REpository is against a single vendor product (eg EF).

A UnitOfWork is per vendor Context.

But if one were to have one UnitOfWork for EF, and another for a WCF based Repo, then the Commit has to hook into something larger – a TransactionScope.

So A UoW has to look for a Current Scope, and if found, hook into it. If non existent, then it is does not hook into an ambient scope, and is a scope in itself.

Requirements: * UoW:

  • UoW has 1-1 Relationship to ObjectContext (ie, it wraps it)
  • UoWFactory has to hook new UoW into any available Ambient Namespace
  • UoWManager, injected into Repository, and laer invoked by Repository, has to return UoW appropriate to current context.
    • By Type (ie EF, other)
    • By ObjectContext (Standard, or Tombstone)
public EFContactFactory : IRepository {
..
IUoWFactory UoW { get { return _iowFactory.GetCurrent<ObjectContext>();} }
..
}

public UoWFactory : IUoWFactory {

  IoW GetCurrent<T> (string tag) {
     if (T is ObjectContext) {
       if (tag == "default") {...}
     }
  }
}
  • /home/skysigal/public_html/data/pages/it/ad/patterns/unitofwork_pattern.txt
  • Last modified: 2023/11/04 03:29
  • by 127.0.0.1