How does unit of work class know which repository to call on commit?
* Preface: I'm pretty new to the unit of work pattern *
My goal is to implement a unit of work class that will be able to keep track of all objects that have been changed throughout a given transaction. Everything I read about the unit of work pattern has it side by side with the repository pattern. So this is the approach I'd like to use.
Say for开发者_JAVA技巧 example I create a new User. On my unit of work object, I have a list of newly created objects, so I add my new User to this list. My user repository has a method titled Create, which takes in a User and calls a stored procedure to add the data to the database. When I call commit on my unit of work, how will it know which repository and method to call based on the list of new objects? Say it contains a User object and a Comment object. Both are newly created and need to be added on commit. I'm uncertain how to accomplish this.
Could somebody explain this a bit better and maybe even a small example if possible?
Thanks.
UnitOfWork is an infrastructural pattern that is already implemented by ORM, just like Identity Map. You don't have to reinvent the wheel. Repository on the other hand is a part of your domain model. Repository and UnitOfWork operate at different levels. UnitOfWork does not need to call Repository because it does not know what Repository is. It deals with different abstractions. It has a built-in cache of entities and it knows what state these entities are in. UnitOfWork however can be injected into Repository.
Proper implementation of UnitOfWork, IdentityMap, Change Tracking, Lazy loading is tedious. You should really be using existing ORM as an infrastructure layer that helps you keeping focus on what is important - domain.
One of most common ways of solving this is using inversion of control.
For example, you've your classes User and Comment, and you've implemented a generic repository IRepository<TDomainObject>
. That's getting a repository of User or Comment is just giving the TDomainObject parameter:
IRepository<User>
IRepository<Comment>
Later you've configured who's implementing IRepository<User>
and IRepository<Comment>
, so if you use something like Microsoft Pattern & Practices' Common Service Locator, and we're in the body of commit method in your unit of work:
foreach(DomainObject some in NewObjects)
{
((IRepository<DomainObject>)ServiceLocator.Current.GetInstance(Type.GetType(string.Format("NamespacesToGenericRepository.IRepository`1[[{0}]]", some.GetType().FullName)))).Add(some);
}
Note IRepository<TDomainObject>
has a contravariant TDomainObject generic parameter which type must inherit a base type of Domain Object called DomainObject, which permits an upcast of something like IRepository<User>
to IRepository<DomainObject>
.
In other words, your IRepository<TDomainObject>
interface signature will look like this:
public interface IRepository<out TDomainObject>
where TDomainObject : DomainObject
This is just a summary and/or hint about how to implement locating the concrete repository so an unit of work of domain objects can manage ones of any specialized domain object.
If you want to learn more about inversion of control check this Wikipedia article:
- http://en.wikipedia.org/wiki/Inversion_of_control
And, because of my own experience, I'd like to suggest you Castle Windsor as inversion of control framework of choice:
- http://www.castleproject.org/container/
This is typically implemented via change tracking , whatever action that happens on an entity the Unit of work/session/context is aware and performs the appropriate action on commit.
Change states could include New, Modified and Deleted.
check this link for more info.
It might help to use an implementation of it to get a better idea.
The entity framework implements the unit of work pattern. Maybe using it will let you understand it better.
You have multiple approaches:
- Change tracking, by creating a UnitOfWork class that stores changes in memory till you ask to commit, an implementation could be: RegisterAdd(entity), RegisterDelete(entity), RegisterUpdate(entity), .... then Commit(); which will iterate through all registered changes and commit. (http://msdn.microsoft.com/en-us/magazine/dd882510.aspx)
- You can use TransactionScope as a distributed transaction to group all updates and commit at the end of unit of work. (how to implement Unit Of Work just using just TransactionScope and sqlconnections)
精彩评论