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;
}

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);
}

Using a GUID as the key in ECO

  1. Drop a DefaultORMappingBuilder component next to your persistence mapper.
  2. Point PersistenceMapperxxxxx1.NewMappingProvider and RuntimeMappingProvider to point to this new component.
  3. On your PersistenceMapper component expand SqlDatabaseConfig and click the [...] next to KeyMappers, ensure that you have an item named "GuidMapper" with the MapperTypeName set to "Eco.Persistence.GuidKeyMapper". If not then add it.
  4. On your DefaultORMapperBuilder set IdKeySignature to System.Guid (case sensitive).
  5. Set its IdMapperName to GuidMapper.

Now regenerate your DB.

2009-04-23

Prism AOP - 4

I’ve played a little more with Prism. I find it a little difficult to mentally code on two levels. Level one being the code I am writing for the aspect, and level two being the code I am writing which will executed by the target. Having said that, as soon as I ran my app and saw the output everything was worthwhile.

Here is my Person class

type
  [aspect: EcoAspects.BusinessClass(’DomainClasses.Package1’)]
  Person = public class
  private
    FFirstName: String;
    FLastName: String;
  protected
  public
    property FirstName : String read FFirstName write FFirstName;
    property LastName : String read FLastName write FLastName;
  end;


here is the code which uses that class

class method ConsoleApp.Main;
var
  P: Person;
begin
  P := new Person();
  for A in typeOf(Package1).GetCustomAttributes(true) do
    Console.WriteLine(a.ToString());
  
  P.FirstName := ’Peter’;
  P.LastName := ’Morris’;
  ShowGetValueByIndexResult(P as ILoopBack2, 0);
  ShowGetValueByIndexResult(P as ILoopBack2, 1);
  DoSetValueByIndex(P as ILoopBack2, 0, ’Hello’);
  DoSetValueByIndex(P as ILoopBack2, 1, ’There’);
  ShowGetValueByIndexResult(P as ILoopBack2, 0);
  ShowGetValueByIndexResult(P as ILoopBack2, 1);

  Console.ReadLine();
end;

class method ConsoleApp.ShowGetValueByIndexResult(Obj: ILoopBack2; I: Integer);
begin
  Console.WriteLine(Obj.GetValueByIndex(I).ToString());
end;

class method ConsoleApp.DoSetValueByIndex(Obj: ILoopBack2; I: Integer; Value: Object);
begin
  Obj.SetValueByIndex(I, Value);
end;


and finally, here is the output.

  Eco.UmlCodeAttributes.UmlMetaAttributeAttribute
  Peter
  Morris
  Hello
  There


Fantastic! Using a single line of code I am able to morph the Person class so that it acts as though I had written it like this (might not compile, I wrote this next source in notepad)

type
  Person = public class(Object, ILoopBack2)
  private
    FFirstName: String;
    FLastName: String;
  protected
    method GetValueByIndex(I: Integer): Object; virtual;
    method SetValueByIndex(I: Integer; Value: Object); virtual;
  public
    property FirstName : String read FFirstName write FFirstName;
    property LastName : String read FLastName write FLastName;
  end;

method Person.GetValueByIndex(I: Integer): Object;
begin
  case I of
    0: exit FirstName;
    1: exit LastName;
  end;
end;

method Person.SetValueByIndex(I: Integer; Value: Object): Object;
begin
  case I of
    0: FirstName := String(Value);
    1: LastName := String(Value);
  end;
end;


In addition my code will find a class DomainClasses.Package1 and add an attribute

type
  [Eco.UmlCodeAttributes.UmlMetaAttributeAttribute("ownedElement", typeof(Person)]
  Package1 = class
  end;


As you can see the implementation of the aspect "BusinessClass" is very specific to ECO and saves a lot of writing. What is good too is that I could easily remove the project’s reference to the BusinessClass aspect which generates ECO changes and replace it with a DLL which has a BusinessClass aspect for something else - maybe a blank one which does nothing so that you can use the same class definitions as data-transer-objects.

But my interest in this technology goes far beyond easily implementing an object relational mapper in an abstract way, supporting the ORM is only the first step towards achieving what I really want - archetypes. Once I have the experience to create all of the meta-information required for mapping I can start creating my models out of patterns. I do this a lot at the moment, but all manually. For example in one project I had to allow my Employee, Van, and VendingMachineColumn classes to all hold stock. Each of these would need to hold stock, record stock adjustments such a stock found/lost during stock checks, and also due to stock transfers.

It would be a bad design to descend all of these classes from a StockHolder class. Holding stock is something you DO, and not something you ARE, so inheritance here is wrong. What I would typically do here is

1: Create a StockHolder class which holds the stock + adjustment history.
2: Employee, Van, and VendingMachineColumn would all own an instance of StockHolder.
3: Each class would implement

public interface IStockHolder
{
  StockHolder GetStockHolder();
}


This is a one-way relationship, if I needed for example to find all stock holders with X amount of a certain stock item so that I could request a transfer this would not be sufficient. In which case I would introduce an abstract method to StockHolder

  object GetOwner();


Then I’d have a descendant of StockHolder for each owner. EmployeeStockHolder, VanStockHolder, VendingMachineColumnStockHolder; each would have an association back to their owning object (Employee, Van, VendingMachineColumn) which they would return by overriding GetOwner. Now this is not a lot of work, but it is repetitive. You see the pattern in the UML but do you instantly recognise it? Is it really self-descriptive?

My AOP goal is to be able to do something like this

type
  [aspect: BusinessClass(’MyNameSpace.Domain.MyPackageName’)]
  [aspect: StockHolder]
  Employee = class
  public
    ...
  end;

  [aspect: BusinessClass(’MyNameSpace.Domain.MyPackageName’)]
  [aspect: StockHolder]
  Van = class
  public
    ...
  end;

  [aspect: BusinessClass(’MyNameSpace.Domain.MyPackageName’)]
  [aspect: StockHolder]
  VendingMachineColumn = class
  public
    ...
  end;


The StockHolder aspect would create the descendant (TargetClassName)StockHolder with an association back to the target, and override GetOwner. The thing is, HOW the aspect is implemented is not relevant, all I am saying is "this holds stock". It is short, descriptive, and instantly understandable. It’s also only a few seconds of work to make a class hold stock.

type
  [aspect: BusinessClass(’MyNameSpace.Domain.MyPackageName’)]
  [aspect: StockHolder] //Holds stock
  [aspect: ContactDetailsHolder] //Has personal contact information
  [aspect: Auditable] //Can make employees subject to an internal audit
  [aspect: TaskAssignable] //Can assign tasks to this employee
  [aspect: CustomerRole] //Employees can purchase goods
  Employee = class
  public
    ...
  end;


That’s the kind of thing I’d like to end up with. Much more descriptive than UML I think :-)

Here is the code. It’s just proof of concept code at the moment. I’ve decided to prefix Type_ Method_ Property_ etc to the start of types, methods, and property definitions where they are referring to values from the model the aspect is being applied to; this was something I decided to do to help me to mentally split the "this code" scenario and "target code" scenario.


namespace EcoAspects;

interface

uses
  System.Collections.Generic,
  System.Linq,
  RemObjects.Oxygene.Cirrus,
  System.Text;

type
  BusinessClassAttribute = public class(System.Attribute, ITypeInterfaceDecorator)
  private
    FPackageName: String;
    method AddClassToPackage(Services: IServices; aType: ITypeDefinition);
    method AddILoopBack2Interface(Services: IServices; aType: ITypeDefinition);
    method AddILoopBack2GetValueByIndex(Services: IServices; aType: ITypeDefinition);
    method AddILoopBack2SetValueByIndex(Services: IServices; aType: ITypeDefinition);
    property PackageName: String read FPackageName;
  protected
  public
    constructor (PackageName: String);
    method HandleInterface(Services: IServices; aType: ITypeDefinition);
  end;

implementation

uses
  System.Windows.Forms,
  Eco.ObjectImplementation,
  RemObjects.Oxygene.Cirrus.Statements,
  RemObjects.Oxygene.Cirrus.Values;

constructor BusinessClassAttribute(PackageName: String);
begin
  FPackageName := PackageName;
end;

method BusinessClassAttribute.HandleInterface(Services: RemObjects.Oxygene.Cirrus.IServices; aType: RemObjects.Oxygene.Cirrus.ITypeDefinition);
begin
  AddClassToPackage(Services, aType);
  AddILoopBack2Interface(Services, aType);
end;

method BusinessClassAttribute.AddClassToPackage(Services: IServices; aType: ITypeDefinition);
var
  Type_PackageReference: ITypeReference;
  Type_PackageDefinition: ITypeDefinition;
  Type_UmlMetaAttributeAttribute: IAttributeDefinition;
begin
  Type_PackageReference := Services.FindType(PackageName);
  if (Type_PackageReference = nil) then
  begin
    Services.EmitError(’Package class not found: ’ + PackageName);
    exit;
  end;

  //If it is an ITypeDefinition that means it is declared as source in the current
  //binary and we can therefore modify it - so we can attach .NET attributes.
  //If it isn’t an ITypeDefinition but only an ITypeReference then it is immutible
  //and we cannot change it.
  Type_PackageDefinition := Type_PackageReference as ITypeDefinition;
  if (Type_PackageDefinition = nil) then
  begin
    Services.EmitError(’Package class cannot be modified, it is not part of the same project: ’ + PackageName);
    exit;
  end;

  Type_UmlMetaAttributeAttribute := Type_PackageDefinition.AddAttribute();
  Type_UmlMetaAttributeAttribute.Type := Services.FindType(’Eco.UmlCodeAttributes.UmlMetaAttributeAttribute’);
  Type_UmlMetaAttributeAttribute.AddParameter(’ownedElement’);

  //The value we use for Type must be a TypeOfValue based on aType.
  Type_UmlMetaAttributeAttribute.AddParameter(new TypeOfValue(aType));
end;

method BusinessClassAttribute.AddILoopBack2Interface(Services: IServices; aType: ITypeDefinition);
var
  Type_ILoopBack2: IType;
begin
  Type_ILoopBack2 := Services.FindType(’EcoSupport.ILoopBack2’);
  if (Type_ILoopBack2 = nil) then
  begin
    Services.EmitError(’EcoSupport.ILoopBack2 not found, are you missing an assembly reference?’);
    exit;
  end;

  aType.AddInterface(Type_ILoopBack2);
  AddILoopBack2GetValueByIndex(Services, aType);
  AddILoopBack2SetValueByIndex(Services, aType);
end;

method BusinessClassAttribute.AddILoopBack2GetValueByIndex(Services: IServices; aType: ITypeDefinition);
var
  Type_ILoopBack2: IType;
  Method_ILoopBack2_GetValueByIndex: IMethod;
  Method_GetValueByIndex: IMethodDefinition;
  Statement_CaseIndexOf: CaseStatement;
begin
  //Find ILoopBack2 and ILoopBack2.GetValueByIndex
  Type_ILoopBack2 := Services.FindType(’EcoSupport.ILoopBack2’);
  Method_ILoopBack2_GetValueByIndex := Type_ILoopBack2.GetMethods(’GetValueByIndex’)[0];
  
  //Implement GetValueByIndex on the target
  Method_GetValueByIndex := aType.AddMethod(’GetValueByIndex’, Services.GetType(’System.Object’), false);
  Method_GetValueByIndex.AddParameter(’I’, ParameterModifier.In, Services.GetType(’System.Int32’));
  Method_GetValueByIndex.Virtual := VirtualMode.Virtual;
  Method_GetValueByIndex.Visibility := Visibility.Protected;

  //Explicitly tie our GetValueByIndex to ILoopBack2.GetValueByIndex. This ensures they are linked
  //even though our method is protected. This hides the method when using code-completion on the
  //target (because it is protected), but exposes it via the interface - much cleaner!
  aType.AddImplements(Method_GetValueByIndex, Type_ILoopBack2, Method_ILoopBack2_GetValueByIndex);

  //Build case statement
  Statement_CaseIndexOf := new CaseStatement();
  Statement_CaseIndexOf.What := Method_GetValueByIndex.GetParameter(’I’);

  //As a CaseIndex for each property on the class
  var CaseIndex: Integer := 0;
  for PropertyIndex : Integer := 0 to aType.PropertyCount - 1 do
  begin
    var Prop : IProperty := aType.GetProperty(PropertyIndex);
    //Ignore properties which cannot be read
    //Ignore properties which take parameters (properties with indexers);
    if (Prop.ParameterCount = 0) and (Prop.ReadMethod <> nil) then
    begin
      //Create an expression which is basically Self.Property.Read
      var Property_Read : ProcValue := new ProcValue(new SelfValue(), Prop.ReadMethod);

      //Create an exit statement which is basically - exit Self.Property.Read
      //So that we exit the method, returning the result of reading the property value
      var Statement_Exit : ExitStatement := new ExitStatement(Property_Read);

      //Create the CaseIndex which consists merely of the Statement_Exit
      var CaseItem_Index : CaseItem := new CaseItem(Statement_Exit, CaseIndex);

      //Add the CaseIndex to the Statement_CaseIndexOf, and increment the case index
      Statement_CaseIndexOf.Items.Add(CaseItem_Index);
      CaseIndex := CaseIndex + 1;
    end;
  end;

  //Set the body of the GetValueByIndex method we created. Normally we can just write the exact
  //code we need between the begin/end identifiers, but in this case we have generated the statements
  //to execute dynamically, so we need to "unquote" them - which basically means "expand" or "compile".  
  Method_GetValueByIndex.SetBody(Services,
    method begin
      unquote(Statement_CaseIndexOf);
    end);
end;

//This method is very similar to GetValueByIndex, so I will only describe the setter
method BusinessClassAttribute.AddILoopBack2SetValueByIndex(Services: IServices; aType: ITypeDefinition);
var
  Type_ILoopBack2: IType;
  Method_ILoopBack2_SetValueByIndex: IMethod;
  Method_SetValueByIndex: IMethodDefinition;
  Statement_CaseIndexOf: CaseStatement;
begin
  Type_ILoopBack2 := Services.FindType(’EcoSupport.ILoopBack2’);
  Method_ILoopBack2_SetValueByIndex := Type_ILoopBack2.GetMethods(’SetValueByIndex’)[0];

  //SetValueByIndex
  Method_SetValueByIndex := aType.AddMethod(’SetValueByIndex’, nil, false);
  Method_SetValueByIndex.AddParameter(’I’, ParameterModifier.In, Services.GetType(’System.Int32’));
  Method_SetValueByIndex.AddParameter(’Value’, ParameterModifier.In, Services.GetType(’System.Object’));
  Method_SetValueByIndex.Virtual := VirtualMode.Virtual;
  Method_SetValueByIndex.Visibility := Visibility.Protected;

  //Make explicit interface
  aType.AddImplements(Method_SetValueByIndex, Type_ILoopBack2, Method_ILoopBack2_SetValueByIndex);

  //Build case statement
  Statement_CaseIndexOf := new CaseStatement();
  Statement_CaseIndexOf.What := Method_SetValueByIndex.GetParameter(’I’);

  var CaseIndex: Integer := 0;
  for PropertyIndex : Integer := 0 to aType.PropertyCount - 1 do
  begin
    var Prop : IProperty := aType.GetProperty(PropertyIndex);
    if (Prop.ParameterCount = 0) and (Prop.WriteMethod <> nil) then
    begin
      //Here we need 2 statements for every CaseItem. So we need a BeginStatement
      //which is basically a begin/end block
      var Statement_CaseItemBegin : BeginStatement := new BeginStatement();

      //Create an expression which is equivalent to Self.Property.Set(Value);
      var Property_Write : ProcValue := new ProcValue(new SelfValue(), Prop.WriteMethod, Method_SetValueByIndex.GetParameter(’Value’));

      //Create a statement based on this expression. We can use AssignementStatement without passing a value because we have
      //already specified the value in the previous expression.
      var Statement_SetPropertyValue : AssignmentStatement := new AssignmentStatement(Property_Write);

      //Add this assignment to the Begin/End block statement
      Statement_CaseItemBegin.Add(Statement_SetPropertyValue);

      //And add a plain "Exit" after it within the Begin/End block.
      var ExitMethod : ExitStatement := new ExitStatement();
      Statement_CaseItemBegin.Add(ExitMethod);

      //Craete a CaseItem for the current property index which will execute our Begin/End block.
      var CaseItem_Index : CaseItem := new CaseItem(Statement_CaseItemBegin, CaseIndex);

      //Add the Begin/End block statement to the Case statement.
      Statement_CaseIndexOf.Items.Add(CaseItem_Index);
      CaseIndex := CaseIndex + 1;
    end;
  end;

  //Set the "unquoted" statement block as the method’s body.  
  Method_SetValueByIndex.SetBody(Services,
    method begin
      unquote(Statement_CaseIndexOf);
    end);

end;

end.

2009-04-21

ECO – Ensure related objects

Sometimes you have a list of objects (say List<PurchaseOrder>) and you want to get all associated objects (say OrderLines) for all the orders in the list. There are 2 ways of doing this in ECO

  1. You call a PersistenceService method which takes the list of orders and a string identifying the association name. I don't like this approach because the name of the association may change during modelling and this wont be picked up as an error until runtime.
  2. You use the overloaded method which takes an IAssociationEnd. I don't like this either because you have to find the IAssociationEnd instance from the meta-model at runtime, and to do this you'd probably use a name anyway.

So, what's the alternative?



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


Now you can write code like this

EcoSpace.Persistence.EnsureRelatedObjects(orders, o => o.Lines);
EcoSpace.Persistence.EnsureRelatedObjects(orders, o => o.Customer);

And it will be checked at compile time too.

2009-04-20

Now PayPal is robbing us!

Here is a copy of a complaint I just filed with PayPal.

On April 4th someone made a fraudulent transaction on my account for $431.23 / £298.59 - This was reconciled as a refund and I received £298.59 back.

On Match 20th someone had made a fraudulent transaction on my account for $321.57 / £226.69 - After an investigation PayPal decided to give me my money back

*However* instead of a refund you have performed a "Correction" for $321.57

Today I went to WITHDRAW money back into my bank account from where it was stolen, but the amount you will let me withdraw is $307.60 / £204.47 - and this is £21.82 short of the amount stolen.

Are you really trying to tell me that you are going to charge me commission for getting back money which was stolen from me using your service?

I want the balance of £21.82 returned to my account please. At least if I am robbed in the street I don't pay commission!

2009-04-10

Prism AOP - 3

I am playing with Prism’s new Aspect Oriented Programming feature. As a learning exercise I am implementing some of the features ECO requires on a class. If all goes well I will end up with aspects I can apply to plain classes and have them run in ECO. One of those features is the ILoopBack2 interface.

In order to tackle this interface a small piece at a time I have created my own ILoopBack2 interface, so far with only one method.

ILoopBack2 = public interface
  function GetValueByIndex(I: Integer): System.Object;
end;


This method allows the ECO to evaluate OCL (Object Constraint Language) expressions such as "self.FirstName" and have them route via the class’s property, just in case there is any logic in the getter. By implementing an interface which uses an index to identify the property to read it is possible to avoid reflection.

So, getting on with it, here is my class definition

type
  [aspect: EcoAspects.BusinessClass(’DomainClasses.Package1’)]
  Person = public class
  private
    FFirstName: String;
    FLastName: String;
  protected
  public
    property FirstName : String read FFirstName write FFirstName;
    property LastName : String read FLastName write FLastName;
  end;


As you can see this is a very plain class, no magic at all except for the aspect I have applied to it. And now some code which uses the class

P := new Person();
P.FirstName := ’Peter’;
P.LastName := ’Morris’;
Console.WriteLine((P as ILoopBack2).GetValueByIndex(0).ToString());
Console.WriteLine((P as ILoopBack2).GetValueByIndex(1).ToString());


Output
  Peter
  Morris



From the example code it is evident that an instance of the Person class is castable to ILoopBack2 and also implements the GetValueByIndex method.

To achieve this the my needs to

1: Add the interface to the class
2: Generate a method
3: Implement the method.

The aspect class is a System.Attribute which implements ITypeInterfaceDecorator.

type
  BusinessClassAttribute = public class(System.Attribute, ITypeInterfaceDecorator)
    ...
  end;

procedure BusinessClassAttribute.HandleInterface(
  Services: RemObjects.Oxygene.Cirrus.IServices;
  aType: RemObjects.Oxygene.Cirrus.ITypeDefinition);
begin
  AddClassToPackage(Services, aType);
  AddILoopBack2Interface(Services, aType);
end;


The implementation for AddILoopBack2Interface consists of the following


procedure BusinessClassAttribute.AddILoopBack2Interface(Services: IServices; aType: ITypeDefinition);
var
  //The type which represents ILoopBack2
  ILoopBack2Type: IType;

  //The type which represents the method GetValueByIndex on ILoopBack2
  ILoopBack2_GetValueByIndex: IMethod;

  //The new method I will create on the Person class
  GetValueByIndexMethod: IMethodDefinition;

  //A case statement I will use in the method
  IndexCaseStatement: CaseStatement;
begin
  //Find the type for ILoopBack2
  ILoopBack2Type := Services.FindType(’EcoSupport.ILoopBack2’);

  //If not found then we are missing a reference, report a compiler error
  if (ILoopBack2Type = nil) then
  begin
    Services.EmitError(’EcoSupport.ILoopBack2 not found, are you missing an assembly reference?’);
    exit;
  end;

  //Get a method reference to ILoopBack2.GetValueByIndex
  ILoopBack2_GetValueByIndex := ILoopBack2Type.GetMethods(’GetValueByIndex’)[0];



This initial code just does some basic checking to ensure the assembly with the interface is referenced by the target assembly (the assembly which owns the Person class). To hide the implementation of GetValueByIndex I want to make the implementing method protected, In Prism you have to explicitly link the interface method to the implementation method if it is not public. For example


type
  IMyInterface = public interface
    procedure Method1();
    procedure Method2();
  end;

  SomeClass = class(System.Object, IMyInterface)
  private
    //Private, so much be explicit about what it implements
    procedure Method1; implements IMyInterface.Method1;
  public
    //Public, the link to the interface method is inferred
    procedure Method2;
  end;



Anyway, back to the important code. Here is the next part of the AddILoopBack2Interface method.

  //Add the interface to Person
  aType.AddInterface(ILoopBack2Type);

  //Create a method which returns a System.Object
  GetValueByIndexMethod := aType.AddMethod(’GetValueByIndex’, Services.GetType(’System.Object’), false);

  //Add a parameter "I: Integer"
  GetValueByIndexMethod.AddParameter(’I’, ParameterModifier.In, Services.GetType(’System.Int32’));

  //Make the method protected + virtual
  GetValueByIndexMethod.Virtual := VirtualMode.Virtual;
  GetValueByIndexMethod.Visibility := Visibility.Protected;

  //Explicitly link the method to ILoopBack2.GetValueByIndex, because the method is not public
  aType.AddImplements(GetValueByIndexMethod, ILoopBack2Type, ILoopBack2_GetValueByIndex);



The implementation of this method will basically be

  case I of
    0: exit FirstName;
    1: exit LastName;
  end;


To do this we need a case statement.

  //Build case statement
  IndexCaseStatement := new CaseStatement();
  IndexCaseStatement.What := GetValueByIndexMethod.GetParameter(’I’);


Next we need to loop through each property on Person and create the case item for it.

  //The case index.  We wont include array properties so this keeps
  //track of how many properties we have added to the case statement
  var CaseIndex: Integer := 0;

  //Loop through each property declared on the type Person
  for PropertyIndex : Integer := 0 to aType.PropertyCount - 1 do
  begin
    //Get a reference to the property
    var Prop : IProperty := aType.GetProperty(PropertyIndex);

    //Only continue if the property has no parameters (is not an array property)
    //and only if it is possible to read this property
    if (Prop.ParameterCount = 0) and (Prop.ReadMethod <> nil) then
    begin
      //Get the method which reads the property value
      var ReadProperty : ProcValue := new ProcValue(new SelfValue(), Prop.ReadMethod);

      //Construct an "exit" statement which returns the value of the property
      var ExitMethod : ExitStatement := new ExitStatement(ReadProperty);

      //Construct a "case item" which executes the exit statement for the
      //given index
      var IndexCaseItem : CaseItem := new CaseItem(ExitMethod, CaseIndex);

      //Add the case item to the case statement
      IndexCaseStatement.Items.Add(IndexCaseItem);

      //Increment the case index for the next property added
      CaseIndex := CaseIndex + 1;
    end;
  end;


Now I have a case statement which I have constructed dynamically I need to assign it as the body of the method which implements ILoopBack2.GetValueByIndex

  GetValueByIndexMethod.SetBody(Services, 
    method begin
      unquote(IndexCaseStatement);
    end);


The "unquote" statement tells the compiler to expand the statement into "real" code.

That’s the lot. This effectively take the following code

type
  [aspect: EcoAspects.BusinessClass(’DomainClasses.Package1’)]
  Person = public class
  private
    FFirstName: String;
    FLastName: String;
  protected
  public
    property FirstName : String read FFirstName write FFirstName;
    property LastName : String read FLastName write FLastName;
  end;


and compiles it as if I had written

type
  Person = public class(System.Object, ILoopBack2)
  private
    FFirstName: String;
    FLastName: String;
  protected
    function GetValueByIndex(I: Integer) : System.Object; virtual;
  public
    property FirstName : String read FFirstName write FFirstName;
    property LastName : String read FLastName write FLastName;
  end;

implementation

function Person.GetValueByIndex(I: Integer) : System.Object;
begin
  case Index of
    0 : exit FirstName;
    1 : exit LastName;
  end;
end;



I hope like me you see the benefits of this!

2009-04-08

We was robbed

I have no idea why this grammatically incorrect statement caused me great amusement in the past when I first read it, but since reading it I have always wanted to use it.

Well, now that it is time for me to use it I find it is nowhere near as amusing as it was the first time around.

Some how, and I have no idea how, someone has managed to spend nearly £600 on my PayPal account. How on Earth have they managed to do it? I don't know! All I know is that I am hoping the PayPal resolution services is both swift and fair. It's not nice to budget yourself very carefully and yet find yourself overdrawn only 8 days after receiving your monthly wages!

AOP 2

This is fun :-) I’ve created an aspect like so

type
  BusinessClassAttribute = public class(System.Attribute, ITypeInterfaceDecorator)
  private
    FPackageName: String;
    property PackageName: String read FPackageName;
  protected
  public
    constructor (PackageName: String);
    method HandleInterface(Services: RemObjects.Oxygene.Cirrus.IServices; aType: RemObjects.Oxygene.Cirrus.ITypeDefinition);
  end;


I can now decorator my class like so

type
  [aspect: BusinessClass(’DomainClasses.Package1’)]
  Person = class
  end;

I can ensure this class  exists in the aspect implementation method.

constructor BusinessClassAttribute(PackageName: String);
begin
  FPackageName := PackageName;
end;

method BusinessClassAttribute.HandleInterface(Services: RemObjects.Oxygene.Cirrus.IServices; aType: RemObjects.Oxygene.Cirrus.ITypeDefinition);
var
  PackageType: ITypeReference;
begin
  PackageType := Services.FindType(PackageName);
  if (PackageType = nil) then
    Services.EmitError(’Package class not found: ’ + PackageName);
end;


Now when I compile my code I get the following compiler error:

Error  1  (ASPE) Package class not found: DomainClasses.Package1

Adding a class named Package1 to the source makes the error go away. Okay, maybe I am getting a little too excited about this, I am a nerd :-)

My first AOP

Here’s what I did.

01: I created a support project which just has a logger class in it.

namespace EcoSupport.pas;
interface
uses
  System.Collections.Generic,
  System.Linq,
  System.Text;

type
  Logger = public class
  private
  protected
  public
    class procedure Log(Message : String);
  end;

implementation

class procedure Logger.Log(Message : String);
begin
  System.Diagnostics.Debug.WriteLine(’Log: ’ + Message);
end;
end.


02: I created an Aspect which decorates methods, giving me the opportunity to intercept all method calls on the class it decorates.

namespace EcoAspects;

interface

uses
  System.Collections.Generic,
  System.Linq,
  System.Text,
  RemObjects.Oxygene.Cirrus,
  EcoSupport;

type
  [AttributeUsage(AttributeTargets.Class)]
  LogAspect = public class(System.Attribute, RemObjects.Oxygene.Cirrus.IMethodImplementationDecorator)
  private
  protected
  public
    method HandleImplementation(Services: IServices; aMethod: IMethodDefinition);
  end;

implementation

method LogAspect.HandleImplementation(Services: IServices; aMethod: IMethodDefinition);
var
  Name : String;
begin
  Name := aMethod.Name;
  aMethod.SetBody(Services,
    method begin
      EcoSupport.pas.Logger.Log(’Entering ’ + Aspects.MethodName);
      try
        Aspects.OriginalBody; //Call the original method body
      finally
        EcoSupport.pas.Logger.Log(’Leaving ’ + Aspects.MethodName);
      end;
    end
    );
end;
end.


03: Now my aspect is ready I can apply it to a class.

namespace PrismConsoleApplication1;

interface

uses
  System.Collections.Generic,
  System.Linq,
  EcoSupport,
  System.Text;

type
  [Aspect:EcoAspects.LogAspect]
  TestSubject = public class
  private
  protected
  public
    method DoSomething();
  end;

implementation

method TestSubject.DoSomething();
begin
  System.Diagnostics.Debug.WriteLine("DoSomething");
end;
end.


04: Finally a test

namespace PrismConsoleApplication1;

interface
uses
  System.Linq;

type
  ConsoleApp = class
  public
    class method Main;
  end;

implementation

class method ConsoleApp.Main;
var
  TS: TestSubject;
begin
  TS := new TestSubject();
  TS.DoSomething();
end;
end.


The output from this is.....

Log: Entering .ctor
Log: Leaving .ctor
Log: Entering .ctor
Log: Leaving .ctor
Log: Entering DoSomething
DoSomething
Log: Leaving DoSomething

Now so far this is all stuff you can do with tools such as PostSharp. The problem with PostSharp is that it is a post-compile processor so the aspects are applied after compilation is complete, which means that we can’t use any of the aspect introduced features within the same library without comprimising compile-time checking. For example in PostSharp if an aspect introduces an interface with a LogMessage method we can’t do this...

[Logger]
public class Person
{
}

var p = new Person();
p.LogMessage(“Hello”);

Because the Person class doesn’t have a LogMessage method until after it has been compiled. To get around this PostSharp users will typically do something like this

var p = new Person();
((object)p as ILogger).LogMessage(“Hello”);

What I don’t like is
1.  More typing
2.  It’s not compile-safe. If I remove the [Logger] attribute from Person it will still compile and I wont know about the problem until runtime.

This is exactly why I like the look of Prism, take a look at this.

type
  [AttributeUsage(AttributeTargets.Class)]
  LogAspect = public class(System.Attribute, RemObjects.Oxygene.Cirrus.IMethodImplementationDecorator)
  private
  protected
  public
    [Aspect:AutoInjectIntoTargetAttribute]
    class method LogMessage(aName: String);
  end;

The AutoInjectIntoTargetAttribute tells the compiler to add that method to the target.

type
  [Aspect:EcoAspects.LogAspect]
  TestSubject = public class
  private
  protected
  public
  end;

p := new TestSubject();
p.LogMessage(‘Hello’);

This compiles even in the same library, because the aspects are able to modify the project structure before the compile process completes, so TestSubject really does have the LogMessage method. Pre-compile AOP, how cool is that?