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
|
If I have code like so: IUnitOfWork uofCurrent = LightSpeedContext.UnitOfWorkFactory.Create(); Person pX = new Person(); Address adHome = new Address(pX); pX.Address.Add(adHome); // To delete object The line above does not remove the entity from the EntityCollection Addresses. The reason is the EntityState for adHome is currently New and as such when calling Remove on the UnitOfWork it simples checks the state of adHome sees that it is New and simple removes it from it's internal map. The code that triggers removal from all loaded lists never actually fires. NOTE: This problem does not occur for already existing objects since the call to Remove does set the EntityState to Deleted which then causes all removal code to fire. Thanks, Kavan |
|
|
Hi Kavan, Thanks, I'll take a look at this and get back to you shortly. Cheers, Andrew. |
|
|
Hi Kavan, I'm currently looking into this issue. As an aside, you typically only need to deal with the Repository when working with your top-level (aggregate root) entity. Cheers, Andrew.
|
|
|
Hi Andrew, Actually in my application what is going on is a UnitOfWork is created via UnitOfWorkFactory and passed to a controller. Then certain actions (i,e. deletion) are handled in a generic manner. IUnitOfWork.Remove(), IUnitOfWork.Add() So from the controller's viewpoint it doesn't know whether an object is aggregated nor should it. As a note the removal does work if the objects have been previously saved, this is only an issue for a new object that is later deleted prior to saving. Thanks, Kavan |
|
|
Hi Kavan,
I've fixed the issue in the latest nightly - now available from your store downloads page. With respect to explicit usage of the IUnitOfWorkFactory it is not recommended and I'm thinking of removing the getter on LightSpeedContext. The reason is that LightSpeed needs to be able to obtain the current unit of work in order to do lazy-loading, object graph synchronization and unique validation. Internally, it uses the Repository facade which manages the current unit of work scope. I understand however why you are using the UnitOfWorkFactory in this way - in order to achieve decoupling, testability etc. My original idea for achieving this would be that you could build an abstraction over the LightSpeed Repository. In DDD pseudo-code this may look something like: interface IRepository { Add(Entity entity) Remove(Entity entity) Find(Query query) Commit() } interface ICustomerRepository : IRepository { FindByEmail(string emailAddress) FindByUserName(string userName) //... } I'm interested in hearing your thoughts on this. Cheers, Andrew. |
|
|
Hi Andrew, The system that I am trying to integrate LightSpeed with essentially uses UnitOfWorks for change tracking, as well as, CRUD. This pattern is very useful in multi-screen editing applications, I've actually implemented this with LightSpeed by adding a rollback function on the UnitOfWork and track changes to all objects added to the UOF for rollback and merging purposes. Most ORMs actually provide this functionallity through nested sessions, etc. And I am actually in the process of integrating LightSpeed in place of an older ORM, but this type of encapsulation whether it is built-in to the UOFs directly or through another abstraction really should be part of the Mapper. Thanks, Kavan |
|
|
Hi Kavan, Thanks for the info. We do support nested units of work through the Repository.BeginUnitOfWork method - The Repository again handling scoping through an internal UOW stack. We don't however merge changes back up through the UOW chain. The approach I tend to use for handling transactional object updates in a Rich UI is to leverage IEditableObject. Using this approach I would do something like: 1) Repository.Find Orders Cheers, Andrew. |
|
|
Hi Andrew,The only reservation I have with IEditableObject is that a lot of UI controls interfere in the process by calling BeginEdit, EndEdit, usually at the wrong places.Also with respect to change tracking, are there any plans for field-level change tracking and updating.The solution you propose could work if change tracking was available at the object level. After reviewing the Entity class it seems all the plumbing is basically there.With the change tracking exposed, someone like could easily roll-up multi-level rollback and merging.
|
|
|
Hi Kavan, [quote user="kshaban"]The only reservation I have with IEditableObject is that a lot of UI controls interfere in the process by calling BeginEdit, EndEdit, usually at the wrong places[/quote] Within Windows.Forms this is usually taken care of by using a BindingSource. It's a little bit of work to figure out a solid pattern but once it's in place it can usually be factored into a base framework class. [quote user="kshaban"]are there any plans for field-level change tracking and updating.The solution you propose could work if change tracking was available at the object level. After reviewing the Entity class it seems all the plumbing is basically there.With the change tracking exposed, someone like could easily roll-up multi-level rollback and merging[/quote] Can you provide a bit more detail about how this would work? Cheers, Andrew. |
|
|
Hi Andrew, Essentially, change tracking could be done at the SetValue level with all property changes logged in a dictionary. Then on the Saving event the dictionary could be reset. As an aside, I believe a OnSaved method would help in this regard. Currently there is an OnSaving which of course is a pre-method. I do understand that it may seem like duplication of effort with respect to IEditable, however, most mature frameworks necessitate this duplication for the exact reasons stated earlier about incorrect usage of IEditable by outside controls. But this type of field-level change tracking also would enable object change merging and rollback. And also not to mention the ability for the mapper to just update changed fields. It also would help an automated version conflict resolver. Just to recap field-level change tracking has been present in almost all modern ORMs for some of these reasons and more. Thanks, Kavan
|
|
|
Hi Andrew, Just to add, but also notification of collection and entity loading at the entity level would also help out. If for example, I am watching a currently loaded customer object I would subscribe to it's PropertyChanged event and the new ReferenceLoaded event. Then when subsequent child objects or collections load I could then attach to them as well. I aggree with your earlier point of not keeping a UOF open for a long duration, but to accomlish this I need to track changes at another level so when saving I can Begin a UOF, attach all new, modified, and deleted objects and then save. I'll try to accomplish rollback by calling IEditable.Cancel on all objects I have as modified. But to get this done, I definetly need someway on the Entity level to hook the notfications. Thanks, Kavan |
|
|
Hi Kavan, [quote user="kshaban"]If for example, I am watching a currently loaded customer object I would subscribe to it's PropertyChanged event and the new ReferenceLoaded event. Then when subsequent child objects or collections load I could then attach to them as well.[/quote] Property change events bubble so you will get notified at the Customer level if a new Order is inserted into Orders. You shouldn't need to hook everything in the graph, just work with the Customer at the top-level. When it's time to save LightSpeed will walk down from the Customer handling the child objects appropriately. [quote user="kshaban"] but to accomlish this I need to track changes at another level so when saving I can Begin a UOF, attach all new, modified, and deleted objects and then save.[/quote] Same thing with attaching, just attach the Customer and any associated objects will get persisted too. [quote user="kshaban"]I'll try to accomplish rollback by calling IEditable.Cancel on all objects I have as modified.[/quote] Cool just make sure you call BeginEdit. Cheers, Andrew. |
|
|
Hi Andrew, I've been working on this for the last few days and the IEditableObject interface almost does the job. I say almost, because there is a specific set of situations where it will, in it's current form, not work. For example, if I begin editing an order object, then I start editing a nested order-item object. Say the order-item object in the course of making an update needs to update a property on it's parent order-object (e.g. Balance). Now if the user decides to cancel-edit on the order-item that will go fine, but now the parent order object will have an inconsitent balance property. What I feel would be an indeal solution is to implement a Snapshot interface. This would actually be very similiar to the already implemented IEditable, except the backup information would not be stored in the Entity but instead returned to the caller of the snapshot. In psuedo-code it would be something like this: public interface ISnaphot{ void RestoreFromSnapshot(object objSnapshotData);object CreateSnapshot();} Another possible solution to the nesting problem would be to add a level field to all the IEditiable members and let the entity still manage the undo information. In psuedo-code it would like: public interface IEditableObject2{ void BeginEdit(int iLevel); void CancelEdit(int iLevel);void EndEdit(int iLevel);} Your product really is great, and I understand that you guys want to stick to standard data-binding interfaces, but the fact is the standard .NET data-binding plumbing only really is practical for the simplist of apps. Thanks in advance, Kavan |
|
|
Hi Kavan, One thing I was considering adding but haven't had time is N-Level Undo (such as that in CSLA). Would this feature be helpful in your current scenario? Cheers, Andrew. |
|
|
Hi Andrew, That would definetly help a lot. Thanks, Kavan |
|