views:

197

answers:

4

I've been reading Rockford Lhotka's "Expert C# 2008 Business Objects", where there is such a thing as a data portal which nicely abstracts where the data comes from. When using the DataPortal.Update(this), which as you might guess persists 'this' to the database, an object is returned - the persisted 'this' with any changes the db made to it, eg. a timestamp.

Lhotka has written often and very casually, that you have to make sure to update all references to the old object to the new returned object. Makes sense, but is there an easy way to find all references to the old object and change them? Obviously the GC tracks references, is it possible to tap into that?

Cheers

+4  A: 

There are profiling API's to do this but nothing for general consumption. One possible solution and one which I've used myself is to implement in a base class a tracking mechanism where each instance of the object adds a WeakReference to itself to a static collection.

I have this conditionally compiled for DEBUG builds but it probably wouldn't be a good idea to rely on this in a release build.

// simplified example
// do not use. performance would suck
abstract class MyCommonBaseClass {

    static readonly List<WeakReference> instances = new List<WeakReference>();

    protected MyCommonBaseClass() {
        lock (instances) {
            RemoveDeadOnes();
            instances.Add(new WeakReference(this));
        }
    }

}
Josh Einstein
+1: Nice technique for the case when you control the object.
Vlad
+4  A: 

The GC doesn't actually track the references to the objects. Instead, it calculates which objects are reachable starting from global and stack objects at the runtime, and executing some variant of "flood fill" algorithm.

Specifically for your problem, why not just have a proxy holding reference to the "real" object? This way you need to update at only one place.

Vlad
Yes, you could use a pattern similar to that of a Flyweight. http://en.wikipedia.org/wiki/Flyweight_pattern
Josh Einstein
+1. As Vlad has pointed out, an easy way is to introduce one more level of indirection. (@Josh: Are you sure that *Flyweight* is the proper terminology here?)
andras
@andras, in the GoF example which uses a text editor's glyphs as an example it may seem strange. But if what Vlad means is have one "heavy" data object that contains the data and a bunch of other "link" objects that reference this, yeah I think that would be very similar to the intent of a flyweight. Even though space savings isn't the primary motivation here.
Josh Einstein
@Josh: GoF also brings the example of ref. counting pointers - just like the ones we all love in COM - under *Proxy*. So I would still slide with Vlad in this case. (I'm a nitpicker though, I freely admit.)
andras
I didn't think vlad and I were on different sides. :) I think people take patterns too literally sometimes. They never 100% describe a real world scenario. Nearly every implementation of a pattern is an adaptation or combination.
Josh Einstein
Agreed. I guess even Jon Skeet would have a hard time finding the proper terminology when talking about patterns.
andras
( Wouldn't it be nice to create tags like "Proxy", "Flyweight", "Factory", "Observer". I imagine the trouble this would bring. xD )
andras
A: 

There isn't a simple way to do this directly, however, Son of Strike has this capability. It allows you to delve into all object references tracked by the CLR, and look at what objects are referencing any specific object, etc.

Here is a good tutorial for learning CLR debugging via SoS.

Reed Copsey
A: 

If you are passing object references around and those object references remain unchanged, then any changes made to the object in a persistence layer will be instantly visible to any other consumers of the object. However if your object is crossing a service boundary then the assemblies on each side of the object will be viewing different objects that are just carbon copies. Also if you have made clones of the object, or have created anonymous types that incorporate properties from the original object, then those will be tough to track down - and of course to the GC these are new objects that have no tie-in to the original object.

If you have some sort of key or ID in the object then this becomes easier. The key doesn't have to be a database ID, it can be a GUID that is new'ed up when the object is instantiated, and does not get changed for the entire lifecycle of the object (i.e. it is a property that has a getter but no setter) - as it is a property it will persist across service boundaries, so your object will still be identifiable. You can then use LINQ or even old-fashioned loops (icky!) to iterate through any collection that is likely to hold a copy of the updated object, and if one is found you can then merge the changes back in.

Having said this, i wouldn't think that you have too many copies floating around. IF you do then the places where these copies are should be very localized. Ensuring that your object implements INotifyPropertyChanged will also help propagate notifications of changes if you hold a list in one spot which is then bound to directly or indirectly in several other spots.

slugster