views:

401

answers:

3

In our multi-tier business application we have ObservableCollections of Self-Tracking Entities that are returned from service calls.

The idea is we want to be able to get entities, add, update and remove them from the collection client side, and then send these changes to the server side, where they will be persisted to the database.

Self-Tracking Entities, as their name might suggest, track their state themselves. When a new STE is created, it has the Added state, when you modify a property, it sets the Modified state, it can also have Deleted state but this state is not set when the entity is removed from an ObservableCollection (obviously). If you want this behavior you need to code it yourself.

In my current implementation, when an entity is removed from the ObservableCollection, I keep it in a shadow collection, so that when the ObservableCollection is sent back to the server, I can send the deleted items along, so Entity Framework knows to delete them.

Something along the lines of:

protected IDictionary<int, IList> DeletedCollections = new Dictionary<int, IList>();

protected void SubscribeDeletionHandler<TEntity>(ObservableCollection<TEntity> collection)
{
    var deletedEntities = new List<TEntity>();
    DeletedCollections[collection.GetHashCode()] = deletedEntities;

    collection.CollectionChanged += (o, a) =>
        {
            if (a.OldItems != null)
            {
                deletedEntities.AddRange(a.OldItems.Cast<TEntity>());
            }
        };
}

Now if the user decides to save his changes to the server, I can get the list of removed items, and send them along:

ObservableCollection<Customer> customers = MyServiceProxy.GetCustomers();

customers.RemoveAt(0);

MyServiceProxy.UpdateCustomers(customers);

At this point the UpdateCustomers method will verify my shadow collection if any items were removed, and send them along to the server side.

This approach works fine, until you start to think about the life-cycle these shadow collections. Basically, when the ObservableCollection is garbage collected there is no way of knowing that we need to remove the shadow collection from our dictionary.

I came up with some complicated solution that basically does manual memory management in this case. I keep a WeakReference to the ObservableCollection and every few seconds I check to see if the reference is inactive, in which case I remove the shadow collection.

But this seems like a terrible solution... I hope the collective genius of StackOverflow can shed light on a better solution.

EDIT:

In the end I decided to go with subclassing the ObservableCollection. The service proxy code is generated so it was a relatively simple task to change it to return my derived type.

Thanks for all the help!

+1  A: 

Instead of rolling your own "weak reference + poll Is it Dead, Is it Alive" logic, you could use the HttpRuntime.Cache (available from all project types, not just web projects).

Add each shadow collection to the Cache, either with a generous timeout, or a delegate that can check if the original collection is still alive (or both).

It isn't dreadfully different to your own solution, but it does use tried and trusted .Net components.

Other than that, you're looking at extending ObservableCollection and using that new class instead (which I'd imagine is no small change), or changing/wrapping UpdateCustomers method to remove the shadow collection form DeletedCollections

Sorry I can't think of anything else, but hope this helps.
BW

Binary Worrier
Thanks for the reply. Your answer is making me reconsider approaches I had previously rejected like extending ObservableCollection itself. Will need to investigate the implications of extending it...
Yannick M.
In the end I decided to subclass the `ObservableCollection`. The service proxy classes are generated anyway so the amount of changes was reduced to a minimum. Thanks for the help!
Yannick M.
Excellent, glad to be of help thanks :)
Binary Worrier
+1  A: 

If replacing ObservableCollection is a possibility (e.g. if you are using a common factory for all the collections instances) then you could subclass ObservableCollection and add a Finalize method which cleans up the deleted items that belongs to this collection.

Another alternative is to change the way you compute which items are deleted. You could maintain the original collection, and give the client a shallow copy. When the collection comes back, you can compare the two to see what items are no longer present. If the collections are sorted, then the comparison can be done in linear time on the size of the collection. If they're not sorted, then the modified collection values can be put in a hash table and that used to lookup each value in the original collection. If the entities have a natural id, then using that as the key is a safe way of determining which items are not present in the returned collection, that is, have been deleted. This also runs in linear time.

Otherwise, your original solution doesn't sound that bad. In java, a WeakReference can register a callback that gets called when the reference is cleared. There is no similar feature in .NET, but using polling is a close approximation. I don't think this approach is so bad, and if it's working, then why change it?

As an aside, aren't you concerned about GetHashCode() returning the same value for distinct collections? Using a weak reference to the collection might be more appropriate as the key, then there is no chance of a collision.

mdma
When subclassing, I wouldn't need to bother with Finalizing. The shadow collection is never referenced from elsewhere, and its scope is tied to the derived `Observablecollection`. Keeping state on the server is not a possibility, and this approach would not allow me to only send back the deleted entities to reduce transfer payload for instance.However the part about `GetHashCode()` colliding is something I will need to address if I decide not to subclass `ObservableCollection`. Yet another reason to consider the latter :) Thanks for the reply!
Yannick M.
You're right about not needing finalize if subclassing - you can make the deleted members list a member of the collection. An as you noticed, this gets around the hashCode problem, since that is no longer necessary also. But, your current solution, polling weakreferences on a background thread - is that so bad?
mdma
+1  A: 

I think you're on a good path, I'd consider refactoring in this situation. My experience is that in 99% of the cases the garbage collector makes memory managment awesome - almost no real work needed.

but in the 1% of the cases it takes someone to realize that they've got to up the ante and go "old school" by firming up their caching/memory management in those areas. hats off to you for realizing you're in that situation and for trying to avoid the IDispose/WeakReference tricks. I think you'll really help the next guy who works in your code.

As for getting a solution, I think you've got a good grip on the situation

-be clear when your objects need to be created -be clear when your objects need to be destroyed -be clear when your objects need to be pushed to the server

good luck! tell us how it goes :)

stuck
Thanks, in the end I did end up chucking the code though. Subclassing made more sense since it tied the scope of the shadow collection to the `ObservableCollection`.
Yannick M.