This thread looks to be a little on the old side and therefore may no longer be relevant. Please see if there is a newer thread on the subject and ensure you're using the most recent build of any software if your question regards a particular product.
This thread has been locked and is no longer accepting new posts, if you have a question regarding this topic please email us at support@mindscape.co.nz
|
Sorry for the headline of this post and perhaps also for the length of it… But I am hoping that that other people may have some help from these questions / answers as well since they are quite general onces. Difference between UnitOfWork.Add and UnitOfWork.Attach? From the documentation I read that UnitOfWork.Add is used “under the hood” each time an entity is loaded from the memory. Fine, this means the changes to it will be saved when using Save on the UnitOfWork. This UnitOfWork.Add method is exposed as public for us to use as well. However, the UnitOfWork is also exposing the UnitOfWork.Attach method meaning that we can attach existing objects to our UnitOfWork. Why do we really need both these methods (Add and Attach)? I can make a lot of assumptions reading the source code etc, but I might as well let you answer this one… I guess that was the easy question, now it becomes trickier: UnitOfWork in a Web Application Reading the manual again (and answers in previous posts), the same UnitOfWork is preferably used throughout the request. This means that all objects will be loaded into the same UnitOfWork as you say. However, if you, for some reason, would like to save some changes specifically on an entity, and not others in the request based UnitOfWork, you would have to do something like this instead: public abstract class SaveRepository<T> : BaseRepository<T> where T : Entity<T> {
public void Save(T entity) { using (var unitOfWork = LightSpeedContext.CreateUnitOfWork()) { unitOfWork.Attach(entity); unitOfWork.SaveChanges(); } UnitOfWork.Attach(entity); //necessary to reattach it to the request based UoW.
} … I found out that I had to reattach the entity to the request based UnitOfWork after the save. Otherwise I would get an disposed UnitOfwork attached to the entity after the save method. And you might want to continue doing stuff after the save operation. Quite natural really. So, question: Is this the way you would recommend to go about or am I overdoing things? I might add that from a Lightspeed users “not having source code perspective”, it is not very clear that the Attaching a UnitOfWork is actually replacing the previous UnitOfWork. But I guess that lies with out saying in the Fowler described pattern? My final question is the toughest one I think The problem with an unfinished UnitOfWork… So my final question deals with an issue we have had with the Lightspeed O/R Mapper since we started using it one year ago. I am not saying it is Lightspeed’s fault, but I am wondering here what your recommendations are with regards to this. In a more complex model, all entities are kind of attached to each other due to the bidirectional references. Say for instance you are extracting a User object from the repository. Obviously, the User object can have references to quite a few references. These references have references to new entities etc. In the end, most of your entities in the domain model kind of know of each other via some neighbor. (Note that I am not complaining here since there are some benefits from this as well.) However, a particular save problem can arise in a Session based application. Not necessary speaking web context, since an application with a (logged in) session very well could have a e.g. User object in some kind of global reference (e.g. Singleton). So, in one place in your application you might have a wizard that finishes off with a Save button at the final page which persists all changes to your e.g. User object. However, in the middle of this wizard, the end-user may suddenly exit the wizard and do something completly else in the application. This means that the session stored User object will have a lot of non-persisted changes to it that still resides in the UnitOfWork. So when the end-user tries to save in another part of the application, he’ll get a save error due to the fact that everything is glued together and the previous save operation was not finished… As an application developer with a more complex domain model, this scenario can be hard to foresee. What is your recommended approach in such a scenario? Would each new action leading up to a save action reload data from the database just to be sure that no invalid old data is stored or is there a much simpler way to cancel old changes in a UnitOfWork? There is also the alternative to use several different domain models in your application (instead of one to "rule them all"). I think Eric Evans speaks about this in the last part “Strategic Design” in his book Domain Driven Design. (at least that’s what I gathered hearing him speak at a conference recently, Have not read myself just yet…). So in essence it could mean that you could have pre-domain model to work with in your application/domain and only use the Lightspeed domain model when you are actually interacting with the database. (This is quite similar as to using a Web Service generated proxy model.) Although tempting, I would hate to reinforce this alternative since it definitely will give us more work to sync domain models. But perhaps this approach is something you people at Lightspeed would recommend for larger and more complex so called “Enterprise applications”? much obliged for answers, Tobias |
|
|
Now I know why I get this strange CSS stuff in the start... It comes when you cut'n paste from a Microsoft Word document (spell check). Ohh, well, at least I get some nice coloring of the code though. Hope you can oversee this. |
|
|
Add vs Attach The difference is that Add "registers the entity as a pending insert" whereas Attach allows existing entities to be shuffled between units of work. In practice this is the proverbial difference that makes no difference: I believe that if you create a new entity and Attach it instead of Adding it, the outcome will be the same. Add merely performs some additional checking to make sure that what you are adding really is a new entity. Saving some changes but not others This isn't really a pattern that LightSpeed tries to support. The unit of work pattern says that the UOW represents a business transaction, and manages the writing out of the implementation-level changes involved in that transaction. The idea of saving part of a unit of work is, on this reading, far from "natural" -- it is like committing only part of a transaction! (I am not saying that it is wrong to do partial saves: I am just saying that it runs somewhat contrary to the unit of work pattern.) On this reading, if you have another business transaction which involves a different but overlapping (or, in your case, subsetted) set of data, then it should be a different unit of work. If we were to take this approach, your example would change to something like: using (IUnitOfWork subtransaction = context.CreateUnitOfWork()) Whether this is practical depends on things like where the changes are coming from, why the separate business transactions with shared entities are required, concurrency considerations, etc. In particular, if the entity is participating in a logically separate business transaction from the "main" UOW, should it really be being edited in the "main" UOW (which is what the nasty CopyChanges thing represents handling)? Shouldn't it be being edited in the "subtransaction" UOW? But then of course what if the entity is involved in the main UOW as well? I guess the resolution depends on what the business transactions are, how they relate to each other, and why a particular entity or subset of entities might need to be flushed to the database early. The grizzled oldsters of Mindscape occasionally speak of "nested units of work" which may be relevant to this discussion, but I will have to leave it to JD and JB to chime in on that one! Unfinished unit of work I think the same reading of the UOW pattern helps to answer your final question. If the user has started on a business transaction which is surfaced in the UI as a wizard, and cancels out of that and starts doing something else instead, then we have a new business transaction and therefore a new unit of work. Yes, that does mean reloading things from the database. If the wizard was just part of a business transaction, and the cancel is meant to take us back to an earlier stage in the business transaction rather than aborting the whole thing, then we want to keep the same UOW, but roll back the changes made within the wizard. The latter can be done using BeginEdit/CancelEdit. I'm a bit dubious about the idea of multiple domain models because, as you note, it seems to present all sorts of synchronisation and duplication problems, but not being familiar with Evans' argument I'm reluctant to comment definitively. Our expectation and intention is that you should be able to work with LightSpeed as your domain model, with your application code manipulating LightSpeed entities directly rather than having a layer of wrapper entities around them. The introduction of multiple units of work in 2.0 creates the potential to get plumbing (UOW management) code mixed up with the application code, but really that stuff should be hidden behind some sort of repository interface, and if the plumbing code is getting complex, then it may be that UOWs are being created/used in a way that is not well aligned with the business transactions. As always, that does not necessarily mean that the use of UOWs is wrong -- there may, for example, be a legitimate need to optimise by keeping objects in memory instead of reloading them for each new transaction, or to share non-persisted state across multiple business transactions -- just that it is a sign to take a step back and check what one is doing. It may also be a sign that Unit Of Work is not the right pattern to be applying in that particular application. (And to be clear I am not saying you are in this position: I'm talking about a hypothetical case where someone is finding that they have to layer additional models over the LightSpeed model in order to remove plumbing from the app code.) I hope this helps to answer your questions! |
|
|
Thanks Ivan. I'll do some thinking and perhaps I'll come back with some follow up question. But until then I'll set these questions as solved. |
|