Friday, August 17, 2012

.NET Framework 4.5

I've been doing some reading on what's new with the .NET 4.5 framework.  It looks like there are plenty of items to dive into with details in the near future.  I found this poster on a blog outlining some of the items at a very high level.


I am going to dig in deeper on some of these C# 5.0 and WCF changes to see if there are some things worth carrying over into standards and practices.  If there is anything on this poster that you would like to see a deep-dive on, leave a comment or let me know via the contact page and I will try to put something specific together.

Monday, August 13, 2012

Multi-tenant WCF Services

Hosting a multi-tenant application using WCF
While doing some long-term architecture design, the task was given to work through a cloud-based application.  Our business requirements have resulted in the need for a multi-tenant application built on WCF services that can share physical instances for multiple tenants. The first thing that jumps out in this framework for me is that these services must not contain state level variables that would directly relate to any single tenant. In this model, for instance, a BLL type service should not contain a static list of 'droplist' items to be consumed by the software unless the listing was tenant/customer agnostic. This breakdown gives us the ability to scale at any point in the SOA, based on load or any other need that may present itself. The gain is that you are not locked into a 1:1 tenant:service instance relationship which allows for more host cost effective scaling. In our model, based on the type of data stored, we have decided to go with a separate catalog for each tenant. This point works for our model and while I know there are other models that work effectively, this is really not the target of the current post.

Building the flow and responsibilities 
The first obstacle presented was how to point a tenant at the corresponding catalog without having the SOA be inherently aware of the tenant. For our model, we have the user authenticating against a centralized database and service purposed exclusively for housing tenant information. This enables us to assume that a client will be self-aware and could be provided with their tenant information to some degree prior to engaging the full SOA of the application. The idea being that if we can have the service dynamically recognize the tenant based on the tenant self-identifying at the time of messaging, we can achieve a loosely coupled environment for services to perform with a disregard for the actual tenant entity.

Communicating tenant information
While thinking through a means to provide tenant information to the services, there are a couple of possibilities that come to mind. One could simply provide all of the required information as parameters to each service call.  Would it work? Yes. Is it a good idea? No. There are many reasons this is not a good solution. One concern is the added difficulty each client application would encounter trying to consume the SOA. Plus, the SOA would be confusing, difficult to implement and not very readable as a programmer. To me, building your SOA must always consider the consuming applications and the ease with which the services can be consumed as a high priority. Those alone point us directly to the messaging interface for the service. My initial thought was to create custom incoming and outgoing message header properties to allow C# windows clients to attach the properties directly via their client proxy and then dynamically read them out in the service layer. This allows all additional difficulty to be encapsulated within the client proxy/channel factory classes. This also allows non-windows users to dynamically build message headers and attach them to the calls coming into WCF while allowing the service to leverage a single implementation to pull the information out of the message. I needed to create a proof of concept application to illustrate this. I was wrapped up in another project at the time, so ToddM took the project on and delivered an illustrative project. (Thanks, Todd). An overview of the successful prototype is as follows. First, you have to attach the outgoing behavior message header at the channel factory level in C#. This requires the creation of CustomServiceOutgoingBehvaior and CustomServiceIncomingBehvaior classes based on IEndpointBehavior and Attribute, IServiceBehavior respectively. These are then attached to the DataChannel and inspected on either side of the communication. In an effort to make this post shorter and more readable, I have removed code that doesn't directly support the main idea. To be clear, these snippets on their own are not enough.

public class DatabaseInfo
{
  public String Catalog { get; set; }
  public String Instance{ get; set; }
}
public static class Tenant
{
    public static String TenantID { get; set; }
    public static String AuthenticatedUser { get; set; }
}

public class CustomClientOutgoingBehvaior : IEndpointBehavior
{
   void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint,
              ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new CustomClientOutgoingMessageInspector());
    }

}

public class CustomServiceOutgoingBehvaior : IEndpointBehavior
{
    DatabaseInfo info = null;

    public CustomServiceOutgoingBehvaior(DatabaseInfo info)
    {
        this.info = info;
    }

  
    void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint 
              endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new CustomServiceOutgoingMessageInspector(info));
    }
}

public class CustomServiceIncomingBehavior : Attribute, IServiceBehavior
{
    void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription
           serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher channelDispatcher in 
         serviceHostBase.ChannelDispatchers)
        {
            foreach (var endpointDispatcher in channelDispatcher.Endpoints)
            {
                endpointDispatcher.DispatchRuntime.
                     MessageInspectors.Add(new 
                     CustomServiceIncomingMessageInspector());
            }
        }
    }
}
public class CustomClientOutgoingMessageInspector : IClientMessageInspector
{
   object IClientMessageInspector.BeforeSendRequest(ref Message 
     request, IClientChannel channel)
    {
        var messageHeader = new MessageHeader<String>(Tenant.TenantID);
        var untypedMessageHeader = 
               messageHeader.GetUntypedHeader("TenantID", "Namespace.Shared");
        request.Headers.Add(untypedMessageHeader);

        messageHeader = new MessageHeader<String>(Tenant.AuthenticatedUser);
        untypedMessageHeader = 
              messageHeader.GetUntypedHeader("AuthenticatedUser", "Namespace.Shared");
        request.Headers.Add(untypedMessageHeader);

        return null;
    }
}

public class CustomServiceOutgoingMessageInspector : IClientMessageInspector
{
    public DatabaseInfo DatabaseInfo { get; set; }

    public CustomServiceOutgoingMessageInspector(DatabaseInfo info)
    {
        DatabaseInfo = info;
    }
  
    object IClientMessageInspector.BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        if (DatabaseInfo == null)
            return null;

        var messageHeader = new MessageHeader<String>(DatabaseInfo.Catalog);
        var untypedMessageHeader = 
            messageHeader.GetUntypedHeader("Catalog", "Namespace.Shared");
        request.Headers.Add(untypedMessageHeader);

        messageHeader = new MessageHeader<String>(DatabaseInfo.Instance);
        untypedMessageHeader = 
            messageHeader.GetUntypedHeader("Instance", "Namespace.Shared");
        request.Headers.Add(untypedMessageHeader);

        return null;
    }
}
 
Then, with a simple function added to a base class for your WCF services, you will be able to pull this information back out as follows:

protected Tuple<DatabaseInfo, Tenent> GetInfoFromHeader()
{
    DatabaseInfo dbInfo = new DatabaseInfo();
    Tenant tenantInfo = new Tenant();

    Int32 i = OperationContext.Current.IncomingMessageHeaders.FindHeader("Catalog", "Namespace.Shared");

    if (i != -1)
        dbInfo.Catalog = OperationContext.Current.IncomingMessageHeaders.GetHeader<String>(i);

    i = OperationContext.Current.IncomingMessageHeaders.FindHeader("Instance", "Namespace.Shared");

    if (i != -1)
        dbInfo.Instance = OperationContext.Current.IncomingMessageHeaders.GetHeader<String>(i);

    i = OperationContext.Current.IncomingMessageHeaders.FindHeader("TenantID", "Namespace.Shared");

    if (i != -1)
        tenantInfo.TenantID = OperationContext.Current.IncomingMessageHeaders.GetHeade<String>(i);

    i = OperationContext.Current.IncomingMessageHeaders.FindHeader("AuthenticatedUser", "Namespace.Shared");

    if (i != -1)
        tenantInfo.AuthenticatedUser = OperationContext.Current.IncomingMessageHeaders.GetHeader<String>(i);


    return new Tuple<DatabaseInfo, Tenent>(dbInfo,tenantInfo);
}
 
At this point, you will be able to pull out information from the message header and perform many routines based on a tenant entity without adding state to the server. With this information, you can build connection strings for each DAL method pointing to the correct catalog and instance based on tenant.  You can also perform user security functions based on the AuthenticatedUser passed from the client.  As you can see, this is very flexible and would scale out to any values that needed to be communicated along with the messages to the service.