I'm trying to build a system which follows the repository and unit of work patterns to allow persistence ignorance/unit testing, etc etc. I'm looking for advice on dealing with Rollback. Ideally I want to use POCO's but I think I might need to at least implement an interface to provide a few bits and pieces.
So lets say we have two repositories, one context/unit of work.
I add one item, amend another item and delete a third item. Repeat for second repository, then I call rollback.
In the past I've used something akin to a DataSet for this. Each object has a state of pendingNew, pendingAmended, pendingDeleted, clean. There is also a copy last persisted version of the object for rollback.
How would you implement this?
EDIT:
Ok, here's what I think I'm actually trying to get my head around. Prepare to be patterned :)
Ultimately the project is WPF MVVM. So we're looking at the Model to what ever the store is here.
I think I've been trying to conflate the model with the idea of repository, where as I think the model should use the UOW and Repositories to provide the features the model needs to provide. Does that sound better?
I want complete persistence ignorance, so imagine my domain includes a Customer, an Order and OrderLines.
The GUI let's say has one button new order which allows the user to fill in Customer details, Order details and 1-n OrderLine details. He hits save and they go to the database, he hits cancel they don't.
So, in this case the model might ask the CustomerRepository for a customer, then the OrderRepository for a new Order, then the OrderLineRepository for each new Line, then tell the Unit of Work to save them.
Does that sound reasonable? It does to me, I think it's where the separation is defined. I'm half tempted to have another API between the model and the repositories. No, that's silly.
EDIT 2: This is an excellent article that has sort of helped a little.
Key Takeaways A roll back is an options strategy whereby a trade exits an existing position in order to enter a new position with a nearer-term expiration date. A roll back can be employed with either call or put options and is used to increase the long or short gamma exposure in an options position.
Other roll strategies include roll forward, roll up, and roll down. Roll backs can reduce risk, limit losses, and save on transaction costs. A roll back is one of many options trading strategies available to traders and one of many that is labeled as a roll.
Rolling out of an option means that you close and open a position in an options contract at the same time. Roll backs happen when an investor exits a contract with a long-term expiration date and takes a position in one with a shorter-term date. Investopedia requires writers to use primary sources to support their work.
Any reading from or writing to the database is done within a unit of work. For example, a bank transaction might involve the transfer of funds from a savings account to a checking account.
I designed my unit of work and repository classes similar to how it's described here on MSDN. The basic idea of the IUnitOfWork
class is that it handles all the database work itself.
I then added (to my IUnitOfWork
class and implementations) a BeginTransaction()
method, which opens a TransactionScope()
object, and then added a EndTransaction(bool commit)
method. This method handles closing the transaction, either by committing the transaction to the database (if true) or rolling back the transaction (if false).
This allows me to do control complicated transactions allowing multiple commits to be rolled back.
Edit: My line of thinking is you want your UnitOfWork object to know about the repositories and not the other way around. This is my opinion, and you'll find people who like the opposite but here's why.
When you want to deal with the database in some way, you want it all constrained by your current unit of work. So to me it makes logical sense to go through the unit of work in order to access your repositories, rather than having your repositories access your unit of work.
It also makes it easier if you need to branch out and do multiple things on different databases (for example, if history data is written to a different database than live data, or if you are doing horizontal database partitioning), since each database would have it's own unit of work. The reason is that if you make repositories know of the unit of work, you need to create one unit of work for each database, plus copy of each repository that you need for each unit of work you may need to access it to.
Finally, keeping access to your repositories as being accessed only through your unit of work keeps the API simple for the developers. For starters, you only need to instantiate 1 object (the unit of work) instead of 1 unit of work object plus however many repository objects you may need. It keeps your code simple (imho) and makes things a bit less error prone for the developers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With