When working with Azure Table Storage the recommended class to use is the provided TableServiceEntity class. This abstract class is designed to be inherited from in the data class which then requires the implementation of PartitionKey, RowKey and the addition of the attribute DataServiceKey which tells the underlying persistence system which properties map to those mandatory Azure Table Storage columns; like this:
[DataServiceKey(“PartitionKey”, “RowKey”)]
public class DomainClass : TableServiceEntity
{
public DomainClass(): base(“PartitionKey”, “1”)
{
}
public string PropertyOne { get; set; }
public string PropertyTwo { get; set; }
}
There are plenty of blogs on the .NET discussing this pattern and suggesting workarounds or better classes. My problem with this pattern is my carefully crafted business domain classes are polluted by the requirements of the storage system.
What I want is my business domain class to be independent of the storage system until the point I’m ready to persist it.
Attempt 1
The simplest possible solution is to handcraft a PersistClass for every DomainClass in your application; like this:
//Handcrafted PersistClass
[DataServiceKey(“PartitionKey”, “RowKey”)]
public class PersistClass : DomainClass
{
//Handcrated copy constructor
public PersistClass(DomainClass copyFrom)
{
this.PartitionKey = “Handcraft”;
this.RowKey = “1”;
//copy each one of these by hand
this.PropertyOne = copyFrom.PropertyOne;
this.PropertyTwo = copyFrom.PropertyTwo;
}
public virtual string PartitionKey { get; set; }
public virtual string RowKey { get; set; }
public DateTime Timestamp { get; set; }
}
Yes that is possible, but the code for generating the PartitionKey and RowKey will be repeated in every class. And every PersistClass will need an assignment operator or copy constructor to move data members from the DomainClass to the PersistClass
Fail.
Attempt 2
This situation is a perfect candidate for classic inheritance. Build a new parent class that inherits from the DomainClass and inherits from a shared class that implements the persistence properties; like this:
public class PersistClass : DomainClass, MyTableServiceEntityImplementation
The obvious problem with this method is multiple inheritance isn’t supported in .NET.
Fail.
Attempt 3
How about we build a templated class that inherits from the domain class and adds the persistence properties; like this:
//Templated PersistClass
[DataServiceKey(“PartitionKey”, “RowKey”)]
public class PersistClass<TDomain> : TDomain //templated base class not allowed
where TDomain:class
{
//Handcrated copy constructor
public PersistClass(TDomain copyFrom)
{
//using new extension method for copy members
this.MemberwiseCopy(copyFrom);
this.PartitionKey = “Templated”;
this.RowKey = “1”;
}
public virtual string PartitionKey { get; set; }
public virtual string RowKey { get; set; }
public DateTime Timestamp { get; set; }
}
To make such a generic solution work we need a way to copy from the parent class to this class. .NET provides a MemberwiseClone method but for some reason never got around to writing MemberwiseCopy. So that’s our first step. It’s relatively simple to create an extension method for classes which uses reflection to do a MemberwiseCopy
public static T1 MemberwiseCopy<T1, T2>(this T1 obj, T2 otherObject)
where T1 : class
where T2 : class
Now this is exactly what we need as the above template PersistClass can be instantiated like this:
Templated.PersistClass<DomainClass> templated = new Templated.PersistClass<DomainClass>(domain);
This is all very neat and actually intellisense has no problem with it, however the compiler does and returns the following error message
.NET you cannot inherit from a templated class so you can’t just create a generic inherited addition to a set of classes.
Fail.
Attempt 4
Now this is where it’s time to lean on friends who know the darker corners of .NET.
Q: “Why don’t you use CodeDom?”
A: “Huh?”
For me, borne of a strict C++ world, this is the spawn of the devil, the kind of syntactic aberration that would make Bjarne Stroustrup turn over in his sunbed. CodeDom takes System.Reflection and raises it with System.Reflection.Emit. This namespace lets you build classes at runtime into your .NET AppDomain and call them! As evil as it sounds, it’s exactly what we need to wrap any given DomainClass in a new derived class. To do this we have a new TableServiceEntity template class who’s job it is to create these new wrapping classes as required at runtime and adds a copy constructor and persistence properties. Calling it looks like this:
object dynamic = TableServiceEntity<DomainClass>.CreateInstance(domain, “Dynamic”, “1”);
At runtime a new class DomainClassPersist has been created and has a copy of the DomainClass properties and has an implementation of the required persistence properties.
All of the different methods result in the same data in Table Storage but with different levels of development effort and syntactic niceness, the dynamic method winning on both fronts.
Done.
Check out the code and samples
[office src=”https://skydrive.live.com/embed?cid=E7F2E3C8DA56735E&resid=E7F2E3C8DA56735E%213785&authkey=AFajK36kZMjeM78″ width=”98″ height=”120″]
Thanks again Dave for the heads up on CodeDom
Your account-name and key are still visible in the app.config!!!
In your “CreateInstance” you create the dynamic type, too.
you should create those dynamic-types only once per application-start and per domain type.
what about queries? (DataServiceContext.CreateQuery ?)
Ha, thanks, details removed now and keys rolled over.
Yes you could build a system that runs through your currently running assembly and looks for a [Persist] attribute and generates the required DomainClassPersist classes. However doing at runtime is just as good (the class generator has a check to stop recreation of the same class)
//see if it already has been built
Type dynamicType = modBuilder.GetType(entityTypeName);
I’ll drop some more blogs as I build other parts of the solution. The query side is an interesting question although I suspect it is better covered by the template classes in DataServiceContext. Can you explain what would be useful over those existing classes at querytime?
This solution is fair when used only to persist data. In case of retrieving objects form Azure Storage your queries should operate only on PartitionKey & RowKey. Unfortunately since those two are dynamically injected there is no way to write such a query easily. Consider following code:
var subjectQuery = (from entity in tableContext.CreateQuery(“TableName”)
where entity.PartitionKey.CompareTo(“MyPartitionKey”) == 0
select entity).AsTableServiceQuery().Execute();