Question

I'm trying to create a generic CRUD service for Azure Table Storage.

In the past I have always used SQL with a repository/unit of work pattern with Entity Framework. I want to have the same thing with Azure Table Storage but all the examples I have seen require that my entities implement TableEntity from the Azure lib.

However to me this is in conflict with SOLID principals - in that my repository and my models should not need to know about Azure to work.

So what I want is a service that I pass an entity too, and for that service to alter the said class to make it implement the TableEntity and thus allow me to run the usual TableStorage CRUD operations, map it BACK to my entity class and return it.

Was it helpful?

Solution 2

I figured it out myself. I used a Dynamic Object that implemented the requirements for ITableEntity.

If anyone is interested I wrote a blog about how I did it here.

http://bretthargreaves.wordpress.com/2014/01/11/azure-table-storage-with-repository-pattern/

OTHER TIPS

I have written a generic API which does the conversion from any complex object to EntityProperty dictionary by flattening the complex object using recursive reflection. You can then write the entity property dictionary to Azure Table Storage as DynamicTableEntity.

When you Read the DynamicTableEntity, the API does the conversion back to the complex object.

The power of this API is that it works on any complex object with nested properties that themselves may be complex objects with other nested properties.

Feel free to have a look and use:) https://www.nuget.org/packages/ObjectFlattenerRecomposer/

Usage:

//Flatten object of type Order) and convert it to EntityProperty Dictionary
 Dictionary<string, EntityProperty> flattenedProperties = ObjectFlattenerRecomposer.Flatten(order);

// Create a DynamicTableEntity and set its PK and RK
DynamicTableEntity dynamicTableEntity = new DynamicTableEntity(partitionKey, rowKey);

dynamicTableEntity.Properties = flattenedProperties;

// Write the DynamicTableEntity to Azure Table Storage using client SDK


//Read the entity back from AzureTableStorage as DynamicTableEntity using the same PK and RK

DynamicTableEntity entity = [Read from Azure using the PK and RK];

//Convert the DynamicTableEntity back to original complex object.    
Order order = ObjectFlattenerRecomposer.ConvertBack<Order>(entity.Properties);

That's all:)

These is a much simpler approach, you create your model as you would plus the systemic columns PartitionKey, RowKey, Timestamp and ETag.

public class LogModel
{
    public string LogMessage { get; set; }

    public string PartitionKey { get;set;}
    public string RowKey { get; set; }
    public DateTimeOffset Timestamp { get; set; }
    public string ETag { get; set; }
}

This Model doesn't have any reference to AzureTables so we keep it SOLID.

Then we create the repository and inside it a LogEntityWrapper that will help up use the Azure Tables.

public class LogRepository : ILogRepository
{
    protected CloudTable table;
    //ctor that initialize the azure table
    public LogRepository(string tableName, string connectionString)
    {
        if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(connectionString))
            throw new ArgumentException()

        var storageAccount = CloudStorageAccount.Parse(connectionString);
        var tableClient = storageAccount.CreateCloudTableClient();

        table = tableClient.GetTableReference(tableName);
        table.CreateIfNotExists();
    }
    //read from table and Cast the rows to LogModel
    public IEnumerable<LogModel> GetLogs()
    {
        var query = new TableQuery<LogEntityWrapper>();
        var entities = table.ExecuteQuery(query);
        //The Cast doesn't break laziness
        return entities.Cast<LogModel>();
    }

    // A minimal wrapper, just the implementations of Read and Write Entity
    private class LogEntityWrapper : LogModel, ITableEntity
    {
        public void ReadEntity(IDictionary<string, EntityProperty> properties, OperationContext operationContext)
        {
            TableEntity.ReadUserObject(this, properties, operationContext);
        }

        public IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
        {
            return TableEntity.WriteUserObject(this, operationContext);
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top