Differences

This shows you the differences between two versions of the page.

Link to this comparison view

it:ad:patterns:unitofwork_pattern [2019/03/24 12:17] (current)
Line 1: Line 1:
 +# IT:​AD:​Patterns:​UnitOfWork Pattern #
 +
 +
 +
 +<callout type="​Navigation"​ class="​small">​
 +* [[../​|(UP)]]
 +{{indexmenu>​.#​2|nsort tsort}}
 +
 +
 +
 +
 +---
 +
 +
 +</​callout>​
 +
 +
 +## Sumary ##
 +
 +According to [M.F.](http://​bit.ly/​v7o0Xf) 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."
 +    * Reference: [http://​bit.ly/​v7o0Xf](http://​bit.ly/​v7o0Xf)
 +    * In other words, it is the EF ObjectContext that contains methods to Add, Attach, Update, Delete, Commit.
 +
 +
 +## Considerations ##
 +
 +* Creating a Repository ([[IT/​AD/​Patterns/​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.
 +
 +
 +## Relationship between UnitOfWork and DbContext ##
 +
 +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.  ​
 +
 +## Relationship between UnitOfWork and Transaction ##
 +
 +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"​) {...}
 +         }
 +      }
 +    }
 +
 +