This is definitely possible: The goal is to have my Model not derive from TableEntity
, that too with very minimal code.
This can be done by using adapter pattern.
Create a Base class from which your Model classes derive from:
public class StorageTableEntityBase
{
public string ETag { get; set; }
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public DateTimeOffset Timestamp { get; set; }
#region ctor
public StorageTableEntityBase()
{
}
public StorageTableEntityBase(string partitionKey, string rowKey)
{
PartitionKey = partitionKey;
RowKey = rowKey;
}
#endregion
}
Create an adapter class which does the reading and writing part:
internal class AzStorageEntityAdapter<T> : ITableEntity where T : StorageTableEntityBase, new()
{
#region Properties
/// <summary>
/// Gets or sets the entity's partition key
/// </summary>
public string PartitionKey
{
get { return InnerObject.PartitionKey; }
set { InnerObject.PartitionKey = value; }
}
/// <summary>
/// Gets or sets the entity's row key.
/// </summary>
public string RowKey
{
get { return InnerObject.RowKey; }
set { InnerObject.RowKey = value; }
}
/// <summary>
/// Gets or sets the entity's Timestamp.
/// </summary>
public DateTimeOffset Timestamp
{
get { return InnerObject.Timestamp; }
set { InnerObject.Timestamp = value; }
}
/// <summary>
/// Gets or sets the entity's current ETag.
/// Set this value to '*' in order to blindly overwrite an entity as part of an update operation.
/// </summary>
public string ETag
{
get { return InnerObject.ETag; }
set { InnerObject.ETag = value; }
}
/// <summary>
/// Place holder for the original entity
/// </summary>
public T InnerObject { get; set; }
#endregion
#region Ctor
public AzStorageEntityAdapter()
{
// If you would like to work with objects that do not have a default Ctor you can use (T)Activator.CreateInstance(typeof(T));
this.InnerObject = new T();
}
public AzStorageEntityAdapter(T innerObject)
{
this.InnerObject = innerObject;
}
#endregion
#region Methods
public virtual void ReadEntity(IDictionary<string, EntityProperty> properties, OperationContext operationContext)
{
TableEntity.ReadUserObject(this.InnerObject, properties, operationContext);
}
public virtual IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
{
return TableEntity.WriteUserObject(this.InnerObject, operationContext);
}
#endregion
}
I mark this class as internal, so exposing Azure Storage Library publicly can be avoided. And this is all the code required.
Usage:
Define a Model class:
public class UserEntity : StorageTableEntityBase
{
public string UserName { get; set; }
public string Email { get; set; }
}
Retrieving from the storage table:
public T RetrieveEntity<T>(string tableName, string partitionKey, string rowKey)
where T : StorageTableEntityBase, new()
{
CloudTable table = TableClient.GetTableReference(tableName);
TableResult tableResult = table.Execute(TableOperation.Retrieve<AzStorageEntityAdapter<T>>(partitionKey, rowKey));
if (tableResult.Result != null)
{
return ((AzStorageEntityAdapter<T>)tableResult.Result).InnerObject;
}
return default(T);
}