Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
172 lines (131 sloc) 6.96 KB

We moved this to a private TFS repository just after creating this - I've left this version behind for anyone who wants to get the drift of the original idea.

This is very much a work in progress - don't judge me, yet!

AzureTableFramework

AzureTableFramework is an object mapper that enables .NET developers to work with Azure Table data using domain-specific objects. It eliminates the need for most of the data-access code that developers usually need to write. (...or what I wanted EF7 to do for Azure Tables - In all fairness, Azure Tables are different enough from SQL Tables that it deserves its own library seperate from a SQL-based EF).

Written in .Net Core (compatible with the full framework), it utilizes the official WindowsAzure.Storage API > 6.2.2, Dependency Injection.

Features

  • Indexing / Azure Search Integration
  • Parent / Child Relationships
  • (Azure Storage) Blob & Queue Support
  • Backups
  • Encryption
  • Required / Unique / Field Validation

Getting Started

Download the nugetpackage (coming soon), define your classes and call them in context (in a familiar EF-like way).

Modeling

public class BloggingContext : AzureTablesContext
{
    public AzureTableDictionary<Blog> Blogs { get; set; }
    public AzureTableDictionary<Post> Posts { get; set; }
}

public class Blog : AzureTableEntity
{
    [PartitionKey]
    public string AuthorID { get; set; }
    //RowKey
    public string BlogID { get; set; }
    public string Url { get; set; }
    public Dictionary<string, Post> Posts { get; set; }
}

public class Post : AzureTableEntity
{
    [PartitionKey]
    public string BlogID { get; set; }
    //RowKey
    public string PostID { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}

Save

using (var DB = new BloggingContext())
{
    var BLOG = DB.Blogs.New();
	    BLOG.AuthorID = "HughesJeff";
	await DB.SaveChangesAsync();
}

Get

using (var DB = new BloggingContext())
{
    var B = await DB.Blogs.GetAsync("Blog1");
    Debug.WriteLine("B.Url  =  " + B?.Url);
}

Important Notes

There are a few key differences in using this library than you are probably used to;

If you don't read any other documentation, read this next section ...

Key Differences

Tables are created auto-magically, there are no database create/update migration tasks All you need are azure storage credentials. When "SaveChanges" is called, tables that don't exist will be created.

Tables are named after Classes (public class Blog {} => "Blog" Table ) If you change a class name (and please don't forget it's corresponding ID property name, too), a new table will be created the next time "SaveChanges" is called and previous data will remain in the old table with the old class name. There is no migration.

RowKeys are ClassNames + "ID" There must be a (string) property that matches the case-sensitive pattern Class Name + "ID" (e.g. class named Blog, must have a (string) property of "BlogID"). RowKeys are GUIDs by default, if you don't otherwise populate the property, a new guid (as string) will be created before saving.

public class Blog : AzureTableEntity
{
    ...
    //RowKey
    public string BlogID { get; set; }
    ...
}

Storage Classes must inherit : AzureTableEntity

  • RowKey, PartitionKey, and ETag come from the Official API : EntityTable.
  • LastUpdated and a few other goodies come from this library : AzureTableEntity (which inherits EntityTable).

Azure Tables require PartitionKeys to be defined! (Think of it as less of a burden and more like a free index) If you don't decorate one (string) property in each class with a [PartitionKey] attribute; an error will be thrown. I suggest using a foriegn key propery and situating it above the ID property. Results will eventually be returned sorted by PartitionKey, then RowKey alphanumerically.

Additional Differences

Code-First Only!

You probably won't be able to find the columns for Class + "ID" and whatevery property you decorated with [PartitionKey] in the storage table There's no reason to save the data twice. So, in the table, the data from the Class Name + "ID" property is removed and saved as the RowKey column and the data in the property decorated by [PartitionKey] is removed from that property and saved as the PartitionKey column. The data is restored to the appropriate field from storage.

    public class Blog : AzureTableEntity
    {
        [PartitionKey]
        public string AuthorID { get; set; }
        //RowKey
        public string BlogID { get; set; }
        public string Url { get; set; }
        public Dictionary<string, Post> Posts { get; set; }
    }

So, Blog Table will look like this:

PartitionKey RowKey Timestamp Url
HughesJeff Blog1 1 Jan 2016 2:30PM Url1
HughesJeff Blog2 1 Jan 2016 4:30PM Url2

instead of this:

PartitionKey AuthorID RowKey BlogID Timestamp Url
HughesJeff HughesJeff Blog1 Blog1 1 Jan 2016 2:30PM Url1
HughesJeff HughesJeff Blog2 Blog2 1 Jan 2016 4:30PM Url2
🔺 Duplicate 🔺 🔺 Duplicate 🔺

The library handles the deduping and reconstruction, behind the scenes.

AzureTableDictionary.Items stores a dictionary retreivable by ID -- Using Items.New() adds a GUID (as a string) automatically, and adds the item to the items list by that GUID. If you change the ID property value, it won't be automagically reflected in the Items keys list.

DB.Blogs.Items["Blog1"]

Key Similarities to EF

POCO defined data access

Why Azure Tables?

I LOVE AZURE TABLES!

For TCO (for most data needs), AzureTables beats every other database.

AzureTableFramework
Website http://AzureTableFramework.com
Documentation http://azuretableframework.rtfd.org Documentation Status
GitHub Repository https://github.com/JeffHughes/AzureTableFramework.git
Owner Jeff Hughes (me@jeffhughes.com)
First Version created 5 Jan 2016