Sep 042015
 

AMQP 1.0 is “broker model independent” meaning there are no protocol requirements related to the internals of the broker. It complies with the International Standard ISO/IEC 19464. It is the only standard version of AMQP. As of last month, QPid Proton, SwftMQ, RabbitMQ, ActiveMQ, and MQLite are all supporting this relatively new standard.

Microsoft is currently working to retire their .NET-centric proprietary protocol, SBMP, over the next few months/ This will leave only the AMQP protocol when working with Azure Service Bus. The AMQP protocol backs the .NET SDK, which still has support for WCF with NetMessagingBinding. Developers may also choose to use AMQPNet Lite, which is an open source library for .NET and UWP (store apps). It is a lighter library exposing more of the primitives of AMQP. Another alternative is SBLite, which is a wrapper around AMQPNet Lite to abstract the low level details of AMQP.

Download Qpid Proton 0.10
https://qpid.apache.org/releases/qpid-proton-0.10

Download AMQPNet Lite
https://github.com/Azure/amqpnetlite

Download SBLite
https://github.com/ppatierno/azuresblite

image

Sep 152014
 

I had a great time at Jax Code Impact over the weekend. Many thanks to Bayer and Brandy for putting together a free, enjoyable, and educational Saturday conference. As a first year conference, it was impressive to see more than 300 people registered for six tracks of Microsoft-focused presentations. Kevin Wolf was a big hit with his quadcopters, 3-D printer, and oculus rift. I personally enjoyed two separate talks on Redis by Henry Lee and Steve Danielson.

I talked about the Windows Azure Service Bus offerings, and hit on topics such as Relays, Queues, Topics, WCF bindings, Pub/Sub, AMQP, and of course some Cloud Design Patterns. 

Here are the slides and code demos from my talk:

Code Impact Service Bus Presentation – Slides

Code Impact Service Bus Presentation – Demo Code

Mar 312014
 

I had a great time at Global Windows Azure Bootcamp (GWAB) in Jacksonville, FL. I got to meet a bunch of cool people and discuss Azure topics all day. Free food, Bold Bean coffee, and beer helped to create the perfect geekfest atmosphere. I can’t wait for the next Azure event!

I talked about Windows Azure Data Services, and hit on topics such as Tables, Blobs, Queues, Windows Azure SQL Database, and some Cloud Design Patterns. The links below are the slides and code demos from my talk.

GWAB Azure Storage Presentation – Slides

GWAB Azure Storage Presentation – Demo Code

Feb 162014
 

Before Windows Azure Storage Client Library (SCL) 2.1, any entity that we wanted to put in Azure Table Storage (ATS) had to derive from the TableServiceEntity class. For me that meant maintaining a ATS-specific entity just to get the PartitionKey (PK), RowKey (RK), Timestamp, and ETag. I also had to maintain a DTO or POCO to be used by the rest of the application, and also maintain logic to marshal values between them to facilitate the common CRUD work.

In the RTM announcement for Windows Azure Storage Client Library 2.1, Microsoft announced that they are now exposing the serialization/deserialization logic for any CLR type. This makes it possible for us to store and retrieve entities without needing to maintain two entity types: the DTO and another class that derives from TableEntity. It also makes it possible to store entities in ATS for which you do not own/maintain the code. We still have the same data type restrictions (e.g. subset of OData Protocol Specifications) so that will restrict how many of those “not owned/maintained” classes can exist in ATS.

In the old days of 2013…

Back in my day, we had to use TableServiceEntity. We’d create generic TableServiceDataModel, TableServiceContext, and TableServiceDataSource classes that would get the connection established and serve up table entities as IQueryables. Inserts, Updates, and Deletes were called and then a call to .SaveChanges(). It had an Entity Framework feel to it, which gave a warm fuzzy feeling that we weren’t clueless.

An Azure adapter layer was full of TableServiceDataModel classes and the necessary infrastructure to interact with ATS:

public class ProductCommentModel : TableServiceDataModel
{
	public const string PartitionKeyName = "ProductComment";

	public ProductCommentModel()
		: base(PartitionKeyName, Guid.NewGuid().ToString())
	{ }

	public string ProductId { get; set; }
	public string Commenter { get; set; }
	public string Comment { get; set; }
}

public class TableServiceDataModel : TableServiceEntity
{
	public TableServiceDataModel(string partitionKey, string rowKey)
		: base(partitionKey, rowKey)
	{ }
}

public class TableServiceContext<TModel> : TableServiceContext where TModel : TableServiceEntity
{
	public TableServiceContext(string tableName, string baseAddress, StorageCredentials credentials)
		: base(baseAddress, credentials)
	{
		TableName = tableName;
	}

	public string TableName { get; set; }

	public IQueryable<TModel> Table
	{
		get
		{
			return this.CreateQuery<TModel>(TableName);
		}
	}
}

public class TableServiceDataSource<TModel> where TModel : TableServiceEntity
{
	private string m_TableName;
	private TableServiceContext<TModel> m_ServiceContext;
	private CloudStorageAccount m_StorageAccount;

	protected CloudStorageAccount StorageAccount
	{
		get
		{
			if (m_StorageAccount == null)
			{
				m_StorageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
			}
			return m_StorageAccount;
		}
	}

	protected TableServiceContext<TModel> ServiceContext
	{
		get
		{
			if (m_ServiceContext == null)
			{
				m_ServiceContext = new TableServiceContext<TModel>(m_TableName, StorageAccount.TableEndpoint.ToString(), StorageAccount.Credentials);
			}
			return m_ServiceContext;
		}
	}

	public TableServiceDataSource(string tableName)
	{
		m_TableName = tableName;
		StorageAccount.CreateCloudTableClient().CreateTableIfNotExist(m_TableName);
	}

	public IEnumerable<TModel> Select()
	{
		var results = from c in ServiceContext.Table
						select c;

		var query = results.AsTableServiceQuery<TModel>();
		var queryResults = query.Execute();

		return queryResults;
	}

	public IEnumerable<TModel> Select(Expression<Func<TModel, bool>> predicate)
	{
		CloudTableQuery<TModel> query = ServiceContext
			.CreateQuery<TModel>(ServiceContext.TableName)
			.Where(predicate)
			.AsTableServiceQuery<TModel>();

		var queryResults = query.Execute();
		return queryResults;
	}

	public void Delete(TModel itemToDelete)
	{
		ServiceContext.DeleteObject(itemToDelete);
		ServiceContext.SaveChanges();
	}

	public void Update(TModel itemToUpdate)
	{
		ServiceContext.UpdateObject(itemToUpdate);
		ServiceContext.SaveChanges();
	}

	public void Update(TModel itemToUpdate, SaveChangesOptions saveOptions)
	{
		ServiceContext.UpdateObject(itemToUpdate);
		ServiceContext.SaveChanges(saveOptions);
	}

	public void Insert(TModel newItem)
	{
		ServiceContext.AddObject(m_TableName, newItem);
		ServiceContext.SaveChanges();
	}

	public void InsertToBatch(TModel newitem)
	{
		ServiceContext.AddObject(m_TableName, newitem);
	}

	public void SaveBatch()
	{
		ServiceContext.SaveChangesWithRetries(SaveChangesOptions.Batch);
	}
}

Data Access Layer ended up looking much cleaner than the Azure Documentation… something like this:

public void AddComment(ProductCommentModel model)
{
	TableServiceDataSource<ProductCommentModel> dataSource = new TableServiceDataSource<ProductCommentModel>("ProductComments");
	dataSource.Insert(model);
}

public IEnumerable<ProductCommentModel> GetComments(string productId)
{
	TableServiceDataSource<ProductCommentModel> dataSource = new TableServiceDataSource<ProductCommentModel>("ProductComments");
	var comments = dataSource.Select().Where(p => p.PartitionKey == ProductCommentModel.PartitionKeyName && p.ProductId == productId).OrderByDescending(comment => comment.Timestamp);
		return comments;
}

public void DeleteComment(string commentid)
{
	TableServiceDataSource<ProductCommentModel> dataSource = new TableServiceDataSource<ProductCommentModel>("ProductComments");
	var comment = dataSource.Select().Where(p => p.PartitionKey == ProductCommentModel.PartitionKeyName && p.RowKey == commentid);
	if (comment.Count() > 0)
	{
		dataSource.Delete(comment.First());
	}
}

With that adapter layer we thought we had it made. The data access layer looks cleaner than most SQL implementations. Still, we had too much Azure code and terminology too far away from the Azure calls. It was a small price to pay I suppose.

Enter the EntityAdapter

The RTM announcement showed an example of what is possible with access to the serialization/deserialization logic. Their sample showed a class named EntityAdapter. Rory Primrose has made some great improvements on EntityAdapter. I took this same class and made just a few modifications to support my use cases. Primarily, the examples had no support for ETags which are critically important in some scenarios. Here is my current version of EntityAdapter:

internal abstract class EntityAdapter<T> : ITableEntity where T : class, new()
{
    private string m_PartitionKey;

    private string m_RowKey;

    private string m_ETag;

    private T m_Value;

    protected EntityAdapter()
        : this(new T())
    { }

    protected EntityAdapter(T value)
    {
        if (value == null)
        {
            throw new ArgumentNullException("value", "EntityAdapter cannot be constructed from a null value");
        }

        m_Value = value;
    }

    public void ReadEntity(IDictionary<string, EntityProperty> properties, OperationContext operationContext)
    {
        m_Value = new T();

        TableEntity.ReadUserObject(m_Value, properties, operationContext);

        ReadValues(properties, operationContext);
    }

    public IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
    {
        var properties = TableEntity.WriteUserObject(Value, operationContext);

        WriteValues(properties, operationContext);

        return properties;
    }

    protected abstract string BuildPartitionKey();

    protected abstract string BuildRowKey();

    protected virtual void ReadValues(
        IDictionary<string, EntityProperty> properties,
        OperationContext operationContext)
    { }

    protected virtual void WriteValues(
        IDictionary<string, EntityProperty> properties,
        OperationContext operationContext)
    { }

    protected virtual void SetETagValue(string eTag)
    { }

    public string ETag
    {
        get
        {
            return this.m_ETag;
        }
        set
        {
            this.m_ETag = value;
            SetETagValue(value);
        }
    }

    public string PartitionKey
    {
        get
        {
            if (m_PartitionKey == null)
            {
                m_PartitionKey = BuildPartitionKey();
            }

            return m_PartitionKey;
        }
        set
        {
            m_PartitionKey = value;
        }
    }

    public string RowKey
    {
        get
        {
            if (m_RowKey == null)
            {
                m_RowKey = BuildRowKey();
            }
            return m_RowKey;
        }
        set
        {
            m_RowKey = value;
        }
    }

    public DateTimeOffset Timestamp { get; set; }

    public T Value
    {
        get
        {
            return m_Value;
        }
    }
}

To use EntityAdapter with a DTO/POCO (e.g. Racer), you write an adapter (e.g. RacerAdapter):

public class Racer
{
    [Display(Name = "Driver")]
    public string Name { get; set; }

    [Display(Name = "Car Number")]
    public string CarNumber { get; set; }

    [Display(Name = "Race")]
    public string RaceName { get; set; }

    public DateTime? DateOfBirth { get; set; }

    [Display(Name = "Last Win")]
    public string LastWin { get; set; }

    public string ETag { get; set; }

    public bool HasWon
    {
        get
        {
            return !String.IsNullOrEmpty(this.LastWin);
        }
    }

    public List<string> Validate()
    {
        List<string> validationErrors = new List<string>();

        //TODO: Write validation logic

        return validationErrors;
    }
}

internal class RacerAdapter : EntityAdapter<Racer>
{
    public RacerAdapter()
    { }

    public RacerAdapter(Racer racer)
        : base(racer)
    {
        this.ETag = racer.ETag;
    }

    protected override string BuildPartitionKey()
    {
        return Value.RaceName;
    }

    protected override string BuildRowKey()
    {
        return Value.CarNumber;
    }

    protected override void ReadValues(
        IDictionary<string, EntityProperty> properties,
        OperationContext operationContext)
    {

        this.Value.RaceName = this.PartitionKey;
        this.Value.CarNumber = this.RowKey;
    }

    protected override void WriteValues(
        IDictionary<string, EntityProperty> properties,
        OperationContext operationContext)
    {
        properties.Remove("CarNumber");
        properties.Remove("RaceName");
    }

    protected override void SetETagValue(string eTag)
    {
        this.Value.ETag = eTag;
    }
}

Now we have everything we need to make our data access layer more simplified and domain-focused instead of table-entity-focused.

// Using TableEntity-derived class requires front-facing layers to deal with partition/row keys instead of domain-specific identifiers
public void AddRacer(RacerEntity racer)
{
    CloudTable table = GetRacerTable();

    TableOperation upsertOperation = TableOperation.InsertOrReplace(racer);
    table.Execute(upsertOperation);
}

// Using a DTO with the EntityAdapter
public void AddRacer(Racer racer)
{
    CloudTable table = GetRacerTable();

    var adapter = new RacerAdapter(racer);
    var upsertOperation = TableOperation.InsertOrReplace(adapter);

    table.Execute(upsertOperation);
}

With or without EntityAdapter, SCL 2.1 gave us TableEntity, TableOperation, etc. that really simplify our code. EntityAdapter is icing on the cake, and really helps to simplify Azure-hosted web APIs.

Apr 222010
 

The sole 2010 offering in the USA of IDesign‘s Architect’s Master Class conducted by the man himself, Juval Lowy, is only a few weeks away. I checked in at the IDesign web site, and found some updates the world needs to see.

If you want to learn something new every day, start at the top of the IDesign Code Library and step through one example each day. Be careful, you might need to re-write every line of code you’ve ever written.

Oct 142009
 

Web services are just the tip of the iceberg in WCFI was privileged to attend the IDesign WCF Master Class last week. It only comes to the USA one time each year, and is presented by the one and only Juval Lowy. The class is held at the training center on the Microsoft Silicon Valley campus in Mountain View, CA. Five very intense days of WCF covering all aspects of WCF from essentials like the ABCs to the most intricate details about advanced topics like concurrency, security, transactions, and the service bus.

What we’ve been told sold about WCF from Microsoft is truly just the tip of the iceberg. Juval presents countless examples that prove WCF is not just about web services. WCF is the evolution of .NET, providing world-class features that no class should ever be without.

Demos, samples, and labs are presented using .NET 3.5 and 4.0 with an emphasis on the new features and functionality in 4.0. Discovery and announcements are the most underrated and unknown new features of WCF 4.0. After seeing Juval’s demos on discovery and announcement, I can’t imagine creating services without them.

More than all of the WCF content, the class gives you a lot to think about regarding architecture, the framework, and engineering principles. Juval’s mastery of .NET is evident in his ServiceModelEx library that extends almost all aspects of WCF and the service bus. His “one line of code” motto makes it possible for all of us to configure our WCF services with ease. The ServiceModelEx library is a good example for all developers to know and understand how to “do .NET” the right way. It exemplifies the best of what .NET and WCF have to offer.

Check out the IDesign website to get the WCF Resource CD (containing many of the examples and demos from the class). Also note the next class dates and sign up for the IDesign newsletter.