2009-04-30

Injecting into the ECO cache

In my previous post I showed an interface ILockable. The actual interface is

public interface ILockable
{
  Guid GetLockID();
  Type GetLockObjectType();
}


Any lockable resource implements this interface. In its constructor it creates an instance of a class which descends from LockObject, a Document class for example would create an instance of DocumentLockObject which descends from LockObject. The "Document" class would store away the ID of the DocumentLockObject in a private attribute. Now when my LockingService is asked to lock multiple (un-related) instances which implement ILockable I want to do this

1: Create a new EcoSpace
2: Load objects efficiently
3: Get locks
4: Update the DB
5: Dispose of the EcoSpace

If I experience an OptimisticLockingException then loop and try again. The item I would like to discuss is #2, "Load objects efficiently". In my model a LockObject has a *--1 association to Session identifying which session has locked it, I want to ensure that the LockObject + LockObject.ExclusiveLockedSession instances are loaded as quickly as possible. If I have a collection of LockObject I can easily use the following code to efficiently load all relevant sessions

objectSpace.EnsureRelatedObjects(result, x => x.ExclusiveLockedSession);


This extension method assumes that the association name is the same as the class’s property name and is implemented like so:

public static class PersistenceServiceExtender
{
  public static void EnsureRelatedObjects<T>(
    this IEcoServiceProvider instance,
    IEnumerable<T> objects,
    Expression<Func<T, object>> member
  ) where T : IEcoObject
  {
    MemberExpression memberExpression = (MemberExpression)member.Body;
    var ps = instance.GetEcoService<IPersistenceService>();
    ps.EnsureRelatedObjects(objects, memberExpression.Member.Name);
  }
}


This is possible because I have a list of ECO class instances as my basis and can therefore rely on the ExclusiveLockedSession association to get ECO to quickly load the related objects. The problem I need to solve is this: given an interface (ILockable) how do I get all LockObjects? Remember that these lockable objects (Document, Image, Video, etc) have no common superclass and therefore have no common association to their LockObject, and this is exactly why I wanted to know the LockObject’s primary key in my last post.

Given a collection of ILockable I am able to determine 2 things.
1: The primary key of its LockObject
2: The exact type of the LockObject it refers to (DocumentLockObject, VideoLockObject, etc).

In my new EcoSpace instance I can now inject locators for these objects directly into the cache.

private IEnumerable<LockObject> GetLockObjects(IEcoServiceProvider objectSpace, IEnumerable<ILockable> subjects)
{
  //Get a reference to the cache content service so that we can inject
  var cache = objectSpace.GetEcoService<ICacheContentService>();

  //Create an IObjectList, a list of object locators of type LockObjecct
  var variableFactoryService = objectSpace.GetEcoService<IVariableFactoryService>();
  IObjectList locators = variableFactoryService.CreateTypedObjectList(typeof(LockObject), false);

  //Inject each locator directly into the cache, avoiding the DB completely
  foreach (var subject in subjects)
  {
    Guid lockObjectID = subject.GetLockObjectID();
    Type lockObjectType = subject.GetLockObjectType();
    IObject lockObjectLocator = cache.GetObject(lockObjectID, lockObjectType);
    locators.Add(lockObjectLocator);
  }

  //Now I have a list of locators I can fetch the instances from
  //the DB in an efficient way by converting them to IList<LockObject>
  var result = locators.GetAsIList<LockObject>();

  //And finally I ensure related objects
  objectSpace.EnsureRelatedObjects(result, x => x.ExclusiveLockedSession);
  return result;
}

No comments: