2009-04-27

Setting the ID of an ECO object before it is saved

This might seem like a bit of an odd requirement. I am currently implementing an offline pessimistic locking service in an ECO app. This will mean that I can use a LockingService to place read/write locks on objects. As usual I have gone for an interface approach (ILockable) rather than making all my lockable classes descend from a base Lockable class. Remember, it's what I DO, not what I AM.

The idea is that I have a Lockable object which holds a list of Sessions. These Sessions indicate who has a read lock, and who has a write lock on the object. Each ILockable business entity will create its own private instance of Lockable when it is created.

The thing is, this application will at times need to lock thousands of objects in as little time as possible. As there is no common ancester for these ILockable classes I can't just use EnsureRelatedObjects to traverse an association name and load all LockObject instances in a single select.

I decided that what I needed was to be able to get the ID of the privately owned LockObject and store it in the ILockable entity. This way ILockable only needs to look like this

public interface ILockable
{
  Guid GetLockID();
}


I can then
  1. Make a list of lock ID's I need to lock.
  2. Inject each into the EcoSpace cache directly, because I know that each ID is always a LockObject.
  3. Add the resulting IObject to an IObjectList.
  4. Call IPersistenceService.EnsureRange on the IObjectList to fetch them all at once.

The key to this approach is to know the ID of the LockObject from the owning ILockable without having to navigate the association self.LockObject in order to retrieve it. Now this isn't really that difficult if the object has already been saved because you can use the IExternalIdService to get the object's primary key.

However, the LockObject is created WITH the ILockable object, we can't get the LockObjects key yet because it hasn't been saved. We can't save the LockObject first because the app might never save the ILockable entity and we'd get an orphaned LockObject.

So, here is a little trick to set the primary key of a new ECO object instance before you have saved it. This way we know what the primary key will be before we even save it. First I created an interface like so:


public interface IObjectIdentityService
{
  void SetIdentity(IObjectProvider instance, Guid identity);
}

Then in the LockObject class I added the following code to the IEcoServiceProvider (new object instance) constructor.


ID = Guid.NewGuid(); //Just a GUID attribute on the class
serviceProvider.GetEcoService<IObjectIdentityService>()
  .SetIdentity(this, ID);

That's all there is to it. Obviously I need the key on my objects to be a GUID because there is no way of getting an integer value you can be sure is unique. Finally here is the implementation of the IObjectIdentityService, I implemented it within my EcoSpace to gain access to FrontsidePolicy.ObjectRepresentationProvider, but I could have easily passed this in a constructor:


void IObjectIdentityService.SetIdentity(IObjectProvider instance, Guid identity)
{
  if (!instance.AsIObject().ServiceProvider.GetEcoService<IStateService>().IsNew(instance))
    throw new InvalidOperationException("Instance must be new");

  Locator locator = FrontsidePolicy.ObjectRepresentationProvider.LocatorForIObject(instance.AsIObject().ObjectInstance);
  ObjectId newID = new DefaultId(identity, locator.Id.ClassId);
  FrontsidePolicy.Cache.SetObjectId(locator, newID);
}

No comments: