As the ObjectSpaces are running late, the development of o/r mapping tools and persistence frameworks seems to be the popular thing to do among .NET developers. Somebody counted over 30 projects and the number is daily increasing. (Ok. I admit it. I wrote one too. But I have an excuse :). I am playing with EOS (an aspect-oriented extension for C#) at the moment, and I thought I could show a quick implementation of a persistence aspect.
Let's take a look at the following example:
using System;
using ObjectStorage;
using PersistentObjects;
namespace PersistentObjects
{
class Cat
{
string _name;
int _lives;
public string Name
{
get {return _name;}
set {_name = value;}
}
public int Lives
{
get {return _lives;}
set {_lives = value;}
}
}
class Dog
{
string _name;
public string Name
{
get {return _name;}
set {_name = value;}
}
}
}
namespace Main
{
class MainClass
{
[STAThread]
static public void Main (string[] args)
{
Cat cat = new Cat ();
cat.Name = "Garfield";
cat.Lives = 9;
Dog dog = new Dog ();
dog.Name = "Oddie";
ObjectSpace.Dump ();
ObjectSpace.Store ();
}
}
}
I would like the classes Cat and Dog from the namespace PersistentObjects to register themselves "magically" in the ObjectSpace so that they can be stored as soon as they change. I also want to assign a unique id to every instance, in order to be able to find them later. This is not easily accomplished in standard CSharp, especially if you don't want to touch the original code. Some persistence tools modify the IL-Code (code enhancement) in order to intercept the field gets and sets. In Java AOP tools like AspectJ we would define a field pointcut to intercept the field calls, and the unique ID would be introduced. This can be done in EOS as well:
using System;
using System.Collections;
using System.IO;
using System.Reflection;
namespace ObjectStorage
{
public aspect ObjectSpace
{
static BindingFlags InstanceFields = BindingFlags.Default |
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
static Hashtable changedObjects = new Hashtable ();
// Every object in a object space should have a unique id
introduce in PersistentObjects.any
{
private Guid _guid = System.Guid.NewGuid();
public Guid ID
{
get {return _guid;}
set {_guid = value;}
}
}
// keep track of changed objects from the persistenceobjects namespace
after():fset (any PersistentObjects.any.any)
{
changedObjects[thisJoinPoint.getTarget()] = "changed";
}
// dump changed objects
public static void Dump ()
{
foreach (DictionaryEntry entry in changedObjects)
{
Console.WriteLine ("{0}, {1}", entry.Key.ToString (), entry.Value.ToString());
}
}
// store all changed objects to persistence.
public static void Store ()
{
#region this code writes the changed objects to xml file
StreamWriter writer = File.CreateText ("persistence.xml");
using (writer)
{
writer.WriteLine ("<Persistence>");
foreach (DictionaryEntry entry in changedObjects)
{
writer.WriteLine ("<" + entry.Key.GetType () + ">");
foreach (FieldInfo fieldInfo in entry.Key.GetType ().GetFields (InstanceFields))
{
writer.WriteLine ("<Field Name=\"" + fieldInfo.Name + "\" type =\"" + fieldInfo.FieldType +"\">");
writer.WriteLine (fieldInfo.GetValue (entry.Key));
writer.WriteLine ("</Field>");
}
writer.WriteLine ("</" + entry.Key.GetType () + ">");
}
writer.WriteLine ("</Persistence>");
}
#endregion
}
}
}
If you run this code after compiling it with eos compiler (note that we are not in csharp-pure world anymore!) you will get a file persistence.xml with stored cats and dogs, each having its own id:
<Persistence>
<PersistentObjects.Dog>
<Field Name="_name_Eos_Original" type ="System.String">
Oddie
</Field>
<Field Name="_guid_Eos_Original" type ="System.Guid">
99571520-e2b3-48a4-8ccc-90cd55d212fd
</Field>
</PersistentObjects.Dog>
<PersistentObjects.Cat>
<Field Name="_name_Eos_Original" type ="System.String">
Garfield
</Field>
<Field Name="_lives_Eos_Original" type ="System.Int32">
9
</Field>
<Field Name="_guid_Eos_Original" type ="System.Guid">
801f3149-b825-4ff1-88bd-cb80a107ee21
</Field>
</PersistentObjects.Cat>
</Persistence>
I hope that this (rather simple) example gives you the feeling what it is like to work with an aop tool with a rich pointcut model in programming language. It would be nice to see eos in a more complete (almost no documentation at all) and open source version soon, or even better to get something like AspectJ or eos in .NET framework itself.
Thursday, September 23, 2004 11:48 AM