Monday, 30 April 2012

Configurable OData Service - Part 5 – End To End Example

This is the final post in the series on the configurable OData service. In this post I will cover an end to end example of how the Domain Model Service and be configured and used by an OData client. For the sake of simplicity I am going to expose a simple database with only one table, the table is called Employees. The script below shows the layout of the table.

CREATE TABLE [dbo].[Employees](
    [EmployeeID] [uniqueidentifier] NOT NULL,
    [LastName] [nvarchar](20) NOT NULL,
    [FirstName] [nvarchar](10) NOT NULL,
    [Title] [nvarchar](30) NULL,
    [BirthDate] [datetime] NULL,
    [HireDate] [datetime] NULL,
    [Address] [nvarchar](60) NULL,
    [City] [nvarchar](15) NULL,
    [Region] [nvarchar](15) NULL,
    [PostalCode] [nvarchar](10) NULL,
    [Country] [nvarchar](15) NULL,
    [HomePhone] [nvarchar](24) NULL,
    [Extension] [nvarchar](4) NULL
 CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED 
(
    [EmployeeID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]


GO

The Domain Model Service is deployed to IIS via its WIX based installer which takes care of configuring the service to execute under IIS.

DomainModelIIS

There are two services exposed from the Domain Model Service

  • DefinitionService.svc
    • Implements the functionality to define the shape and structure of the exposed Domain Model Service
  • ManagementService.svc
    • Implements functionality to start and stop instances of the Domain Model Service

Before jumping into the client console application I will just mention the configuration settings that are required to configure the service.

<?xml version="1.0" encoding="utf-8"?>
<domainModel 
  tablePrefixFilter="" 
  connectionStringName="DomainModelMetadata" 
  targetStoreConnectionStringName="TargetDatabase" 
  domainModelServiceBaseAddress="http://127.000.000.001:8080"/>

The previous configuration fragment contains the following elements:
  • tablePrefixFilter
    • A prefix that is applied by the service when enumerating tables from the database
  • connectionStringName
    • The connection string element name for the store that contains the Domain Model metadata
  • targetStoreConnectionStringName
    • The connection string element name for the target database that will be exposes as an OData service
  • domainModelServiceBaseAddress
    • The base address that a Domain Model Service will be exposed relative too. Notice that the service base address is http:// not https:// more on this later
Listing the Target Schema

After creating the proxy to the service. The first step is to retrieve the table metadata from the Domain Model Service, this is used as reference information to define a domain model.
DefinitionServiceClient client = new DefinitionServiceClient();


client.ClientCredentials.UserName.UserName = "xxxxxx";
client.ClientCredentials.UserName.Password = "xxxxxx";
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;


client.Open();


var tableMetadata = client.GetTableMetadata();


foreach (var item in tableMetadata)
{
    Console.WriteLine(item.Name);


    foreach (var column in item.Columns)
    {
        Console.WriteLine("\t" + column.Name);
    }
}

For the example database this produces the following output:

schemalist

We now have the list of the tables and columns from the target database. This information is intended for consumption in a designer that will allow for a Domain Model Service to be defined by an end user.

Defining and Activating Domain Model Service

Now that we can query the table metadata the next step is to create a Domain Model Definition. This is achieved by creating a proxy to the definition service and calling the CreateDomainModelDefinition
DefinitionServiceClient client = new DefinitionServiceClient();


client.Open();



...


domainModelDefinition = client.CreateDomainModelDefinition(domainModelDefinition);

Then the Domain Model needs to be activated. This is achieved by calling the ActivateDomainModel method of the management service proxy.

ManagementServiceClient managmentServiceClient = new ManagementServiceClient();
managmentServiceClient.ActivateDomainModel(domainModelDefinition.Identifier);

The queried metadata is mapped to the entities that are to be exposed by instances of a Domain Model service. In this example I will expose two versioned “views” of the Employees entity. As an example of how the exposed Domain Model Service entity can diverge from the underlying database I will expose the Employees entity as an entity called People.

Version 1
DomainModelEntity domainModelEntity = new DomainModelEntity();
domainModelEntity.TableName = "Employees";
domainModelEntity.TypeName = "People";
domainModelEntity.Properties = new List<DomainModelProperty>();
domainModelEntity.Properties.Add(new DomainModelProperty()
    {
        ColumnName = "EmployeeID",
        PropertyName = "Identifier",
        IsIdentity = true
    });


domainModelEntity.Properties.Add(new DomainModelProperty()
{
    ColumnName = "FirstName",
    PropertyName = "Givename"
});


domainModelEntity.Properties.Add(new DomainModelProperty()
{
    ColumnName = "LastName",
    PropertyName = "Surname"
});


DomainModelDefinition domainModelDefinition = new DomainModelDefinition()
    {
        Entities = new List<DomainModelEntity>(),
        Comments = "Version 1"
    };


domainModelDefinition.Entities = new List<DomainModelEntity>();
domainModelDefinition.Entities.Add(domainModelEntity);

For version one  of the Domain Model the Employee table is exposed as an entity called People. The three required fields are exposed as aliases of the underlying columns. The Employee is mapped to a field called Identifier. After the definition is created we can query the service at the address “http://localhost:8081/V1/DomainModelService” which will describe the atom for the “Peoples” entity set.

version1

The metadata can be queried from this OData service at “http://localhost:8081/V1/DomainModelService/$metdata”, which shows only the three fields that where mapped.

version1metadata

Using the ODataExplorer tool we can perform some query and updates to the data exposed by the service.

OdataV1

Unfortunately the ODataExplorer does not allow you to insert entities, so for the example I pre seeded the table with a number of rows. And yes i am that unoriginal to come up with the names of people.

OdataV1Data

The data can be modified using the ODataExplorer

OdataV1DataChange

We can see the changes reflected in the database

RawDatachanged

Just to prove that this is a fully functioning OData service you can see that the data can also be queried.

OdataV1DataQuery

Version 2
For Version 2 of the Domain Model we can add a more of the underlying fields to the entities. For the sake of brevity I will add only one the BirthDate.

DomainModelEntity domainModelEntity = new DomainModelEntity();
domainModelEntity.TableName = "Employees";
domainModelEntity.TypeName = "People";
domainModelEntity.Properties = new List<DomainModelProperty>();
domainModelEntity.Properties.Add(new DomainModelProperty()
    {
        ColumnName = "EmployeeID",
        PropertyName = "Identifier",
        IsIdentity = true
    });


domainModelEntity.Properties.Add(new DomainModelProperty()
{
    ColumnName = "FirstName",
    PropertyName = "Firstname"
});


domainModelEntity.Properties.Add(new DomainModelProperty()
{
    ColumnName = "LastName",
    PropertyName = "Lastname"
});


domainModelEntity.Properties.Add(new DomainModelProperty()
{
    ColumnName = "BirthDate",
    PropertyName = "DateOfBirth"
});


DomainModelDefinition domainModelDefinition = new DomainModelDefinition()
    {
        Entities = new List<DomainModelEntity>(),
        Comments = "Version 2"
    };


domainModelDefinition.Entities = new List<DomainModelEntity>();
domainModelDefinition.Entities.Add(domainModelEntity);


Again the metadata can be queried from the instance of the service that exposes this version of the Domain Model. For the second version the Url is “http://localhost:8081/V2/DomainModelService/$metdata”.

version2metadata

Again the ODataExplorer tool can be used to query data.

OdataV2Data

We can also update the entities to change the value of the underlying data in the database.

OdataV2DataChange

RawDatachangedV2

Multiple versions of Domain Models can be exposed using this service, they can contain multiple entities.

Further Improvements

This version of the Domain Model service satisfies the initial requirements but there are a number of areas in which improvements and enhancements can be made. The following is a brief discussion of these enhancements:
  • Entity Identity
    • At the moment the implementation makes the assumption that all entities exposed from the Domain Model have an identifier of type of System.Guid. This is fine for the intended target schema that this component will expose, but it is not a truly flexible approach. A better approach would be to allow for the data type  of the identity field to be specified when creating the Domain Model. The DynamicDataContext class would then use a different mechanism to generate and manipulate the  identity field.

  • Security
    • The DefinitionService and the ManagementService exposed by the Domain Model Service are secured using message level security. The Domain Model Service instances that are instantiated and exposed are not secure. They are exposed as http endpoints. With some additional configuration they can be exposed as https and use any of the following methods of security.

  • Relationships
    • The current implementation supports only exposing entities that do not have relationships to other entities defined in the domain model. It is possible to enhance the service to support relationships between entity types by extending the IDataServiceMetadataProvider and IDataServiceUpdateProvider derived classes.

Hopefully this series of posts has given a good overview of the implementation of a configurable OData service and given a good example of how a Custom Data provider can be implemented to source data.

Tuesday, 24 April 2012

Configurable OData Service - Part 4 – WCF Service Host

In the last post in this series, Part 3 – Custom Data Service Provider, I described the implementation of the Custom Data Service Provider. At this point we now have an OData Service that can be configured via metadata to expose an underlying data store. The next challenge is now to get an instance of this custom OData service up and running. This post will delve into the details on how this is achieved.

Overview

ServiceHost
The core idea is to host the OData services from within a WCF service. In the above diagram each of the Domain Model Service instances, in green, are listening at an address relative to a configured base address. For example http://localhost:port/Vx/DomainModelService, where Vx refers to the version of the Domain Model that the service is exposing, for example V1.

WCF hosting challenge


Each instance of a DomainModelService type that is to be exposed from the WCF service host, expects an instance of a DomainModelDescriptor  to be passed to the constructor.
/// <summary>
/// Initializes a new instance of the <see cref="DomainModelService"/> class.
/// </summary>
/// <param name="domainDescriptor">The <see="DomainModelDescriptor"> that this service has to represent.</param>
public DomainModelService(DomainModelDescriptor domainDescriptor) : this(domainDescriptor, DynamicTypeGenerator.Instance)
{
}

This non default constructor presents a challenge when using the WebServiceHost class to host an instance/instances of the DomainModelService. The WebServiceHost class is used, as opposed to the ServiceHost class, is due to the fact that the DomainModelService is an OData service and cannot be hosted using the ServiceHost class. The WebServiceHost class supports two modes of instantiation.
//
// Summary:
//     Initializes a new instance of the System.ServiceModel.Web.WebServiceHost
//     class with the specified singleton server instance and base address.
//
// Parameters:
//   singletonInstance:
//     A service instance to be used as the singleton instance.
//
//   baseAddresses:
//     The base address of the service.
public WebServiceHost(object singletonInstance, params Uri[] baseAddresses);
//
// Summary:
//     Initializes a new instance of the System.ServiceModel.Web.WebServiceHost
//     class with the specified service type and base address.
//
// Parameters:
//   serviceType:
//     The service type.
//
//   baseAddresses:
//     The base address of the service.
public WebServiceHost(Type serviceType, params Uri[] baseAddresses);

The constructor public WeServiceHost(Type serviceType, params Uri[] baseAddresses) cannot be used due to the fact that the DomainModelService does not have a non default constructor. Invoking this constructor will throw the following exception:

System.InvalidOperationException: The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.

The second constructor public WebServiceHost(object singletonInstance, params Uri[] baseAddresses) can be used to host a instance of a DomainModelService. There is however one drawback to using this method of instantiation and that is the instance of the hosted service is a singleton. Having one instance to service all requests for data could potentially be a bottleneck, where by requests queue on the server side.

Solution


The solution to enabling the WCF framework to instance DomainModelService types is to control the instancing behaviour of the framework ourselves. This is done by implementing an IInstanceProvider derived type and an IServiceBehavior derived type to apply the IInstanceProvider.

The implementation of the IInstanceProvider derived DomainModelInstanceProvider, below, is pretty straight forward. It is passed an instance of an IDomainModelServiceMetadataProvider derived type. invokes the GetDomainModelMetadata method and stores the result. When the WCF framework invokes one of the GetInstance method overloads the DomainModelInstanceProvider constructs an instance of an DomainModelService passing the DomainModelDescriptor to the constructor.
/// <summary>
/// The instance provider for instances of <see cref="DomainModelServices"/>
/// </summary>
public class DomainModelInstanceProvider : IInstanceProvider
{
    private DomainModelDescriptor _domainModelDescriptor;
    
    /// <summary>
    /// Constructor
    /// </summary>
    /// <remarks>Retrieves an instance of a <see cref="DomainModelDescriptor"/> from the <see cref="IDomainModelServiceMetadataProvider"/> and stores it.</remarks>
    /// <param name="domainModelServiceMetadataProvider">The <see cref="IDomainModelServiceMetadataProvider"/> to retrieve a <see cref="DomainModelDescriptor"/> from</param>
    public DomainModelInstanceProvider(IDomainModelServiceMetadataProvider domainModelServiceMetadataProvider)
    {
        _domainModelDescriptor = domainModelServiceMetadataProvider.GetDomainModelMetadata(DomainModelServiceSettings.Current.DomainModelServiceActivationTimeout);
    }


    /// <summary>
    /// Get an instance of a <see cref="DomainModelService"/>
    /// </summary>
    /// <param name="instanceContext">The context for the instance</param>
    /// <param name="message">The message</param>
    /// <returns>An instance of a <see cref="DomainModelService"/></returns>
    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return new DomainModelService(_domainModelDescriptor);
    }
    /// <summary>
    /// Get an instance of a <see cref="DomainModelService"/>
    /// </summary>
    /// <param name="instanceContext">The context for the instance</param>
    /// <returns>An instance of a <see cref="DomainModelService"/></returns>
    public object GetInstance(InstanceContext instanceContext)
    {
        return GetInstance(instanceContext, null);
    }
    /// <summary>
    /// Release instance.
    /// <remarks>
    /// Nothing for this provider to do for this type
    /// </remarks>
    /// </summary>
    /// <param name="instanceContext"></param>
    /// <param name="instance"></param>
    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        return;
    }
}


/// <summary>
/// Defines methods for retrieving <see cref="DomainModelDescriptor"/> metadata
/// </summary>
public interface IDomainModelServiceMetadataProvider
{
    /// <summary>
    /// Retrieve an instance of a <see cref="DomainModelDescriptor"/>
    /// </summary>
    /// <param name="timeout">The time out for retreiving the metadata.</param>
    /// <returns>An instance of a <see cref="DomainModelDescriptor"/></returns>
    DomainModelDescriptor GetDomainModelMetadata(TimeSpan timeout);
}



/// <summary>
/// A custom instancing behaviour to apply to a service.
/// </summary>
public class DomainModelServiceInstancingBehaviour : IServiceBehavior
{
    private DomainModelInstanceProvider _instanceProvider;


    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="domainModelServiceMetadataProvider">An instance of an <see cref="IDomainModelServiceMetadataProvider"/></param>
    public DomainModelServiceInstancingBehaviour(IDomainModelServiceMetadataProvider domainModelServiceMetadataProvider)
    {
        _instanceProvider = new DomainModelInstanceProvider(domainModelServiceMetadataProvider);
    }


    /// <summary>
    /// Provides the ability to pass custom data to binding elements to support the contract implementation.
    /// </summary>
    /// <param name="serviceDescription">The service description of the service.</param>
    /// <param name="serviceHostBase">The host of the service.</param>
    /// <param name="endpoints">The service endpoints.</param>
    /// <param name="bindingParameters">Custom objects to which binding elements have access.</param>
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
        return;
    }


    /// <summary>
    /// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
    /// </summary>
    /// <remarks>
    /// This <see cref="IServiceBehavior"/> type applies an instance of an <see cref="DomainModelInstanceProvider"/> to each of the <see cref="EndpointDispatcher"/> for the service host.
    /// </remarks>
    /// <param name="serviceDescription">The service description.</param>
    /// <param name="serviceHostBase">The host that is currently being built.</param>
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
        {
            ChannelDispatcher cd = cdb as ChannelDispatcher;
            if (cd != null)
            {
                foreach (EndpointDispatcher ed in cd.Endpoints)
                {
                    ed.DispatchRuntime.InstanceProvider = _instanceProvider;
                }
            }
        }
    }


    /// <summary>
    /// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
    /// </summary>
    /// <param name="serviceDescription">The service description.</param>
    /// <param name="serviceHostBase">The service host that is currently being constructed.</param>
    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        return;
    }
}

The DomainModelInstanceProvider is applied to the service through the use of a behaviour. Again the behaviour is pretty straight forward implementation. It constructs and instance of an DomainModelInstanceProvider type and applies this instance to the EndpointDispatcher .DispatchRuntime.InstanceProvider property for each of the endpoints.
/// <summary>
/// A custom instancing behaviour to apply to a service.
/// </summary>
public class DomainModelServiceInstancingBehaviour : IServiceBehavior
{
    private DomainModelInstanceProvider _instanceProvider;


    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="domainModelServiceMetadataProvider">An instance of an <see cref="IDomainModelServiceMetadataProvider"/></param>
    public DomainModelServiceInstancingBehaviour(IDomainModelServiceMetadataProvider domainModelServiceMetadataProvider)
    {
        _instanceProvider = new DomainModelInstanceProvider(domainModelServiceMetadataProvider);
    }


    /// <summary>
    /// Provides the ability to pass custom data to binding elements to support the contract implementation.
    /// </summary>
    /// <param name="serviceDescription">The service description of the service.</param>
    /// <param name="serviceHostBase">The host of the service.</param>
    /// <param name="endpoints">The service endpoints.</param>
    /// <param name="bindingParameters">Custom objects to which binding elements have access.</param>
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
        return;
    }


    /// <summary>
    /// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
    /// </summary>
    /// <remarks>
    /// This <see cref="IServiceBehavior"/> type applies an instance of an <see cref="DomainModelInstanceProvider"/> to each of the <see cref="EndpointDispatcher"/> for the service host.
    /// </remarks>
    /// <param name="serviceDescription">The service description.</param>
    /// <param name="serviceHostBase">The host that is currently being built.</param>
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
        {
            ChannelDispatcher cd = cdb as ChannelDispatcher;
            if (cd != null)
            {
                foreach (EndpointDispatcher ed in cd.Endpoints)
                {
                    ed.DispatchRuntime.InstanceProvider = _instanceProvider;
                }
            }
        }
    }


    /// <summary>
    /// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
    /// </summary>
    /// <param name="serviceDescription">The service description.</param>
    /// <param name="serviceHostBase">The service host that is currently being constructed.</param>
    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        return;
    }
}

The behaviour is applied to the service host through the use of a WebServiceHostFactory derived type. The DomainModelServiceHostFactory type adds the DomainModelServiceInstancingBehaviour to the host. The DomainModelServiceHost type is derived from WebServiceHost and an interface IServiceHost, the interface is to abstract the underlying implementation to facilitate testing.
public class DomainModelServiceHostFactory : WebServiceHostFactory, IServiceHostFactory
{
    private readonly IDomainModelServiceMetadataProvider _domainModelServiceMetadataProvider;


    static DomainModelServiceHostFactory()
    {
        IoCContainer.Current = new IocContainerProxy();
    }


    /// <summary>
    /// Initializes a new instance of the <see cref="DomainModelServiceHostFactory"/> class.
    /// </summary>
    /// <param name="domainModelServiceMetadataProvider">The domain model service metadata provider.</param>
    public DomainModelServiceHostFactory(IDomainModelServiceMetadataProvider domainModelServiceMetadataProvider)
    {
        _domainModelServiceMetadataProvider = domainModelServiceMetadataProvider;
    }


    /// <summary>
    /// Creates an instance of the specified <see cref="T:System.ServiceModel.Web.WebServiceHost"/> derived class with the specified base addresses.
    /// </summary>
    /// <param name="serviceType">The type of service host to create.</param>
    /// <param name="baseAddresses">An array of base addresses for the service.</param>
    /// <returns>
    /// An instance of a <see cref="T:System.ServiceModel.ServiceHost"/> derived class.
    /// </returns>
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        var host = new DomainModelServiceHost(serviceType, baseAddresses);


        host.Description.Behaviors.Add(new DomainModelServiceInstancingBehaviour(_domainModelServiceMetadataProvider));


        return host;
    }


    /// <summary>
    /// Creates the service host.
    /// </summary>
    /// <param name="serviceType">Type of the service.</param>
    /// <param name="baseAddresses">The base addresses.</param>
    /// <returns></returns>
    IServiceHost IServiceHostFactory.CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return CreateServiceHost(serviceType, baseAddresses) as IServiceHost;
    }
}

At this point we now have the ability to host and instantiate instances of the DomainModelService. The final piece that is missing is a mechanism to orchestrate and manage the hosted DomainModelService instances. This responsibility falls to the DomainModelServiceManager class. It is responsible for the managing of the host instances and providing the metadata for each of the host instances that are started. It is not responsible for the validation and verification of the DomainModelMetadata, that is the responsibility of the containing WCF service.
public class DomainModelServiceManager : IDomainModelServiceManager, IDomainModelServiceMetadataProvider, IDisposable
{

….
}

The DomainModelServiceManager derives from the IDomainModelServiceManager and IDomainModelServiceMetadataProvider interfaces.

/// <summary>

/// An interface to define the methods and properties for a domain model service manager

/// </summary>

public interface IDomainModelServiceManager

{

    /// <summary>

    /// The event that is raised when a <see cref="DoaminModelService"/> is opened

    /// </summary>

    event EventHandler<DomainModelServiceOpenedEventArgs> OnDomainModelServiceOpened;

    /// <summary>

    /// The event that is raised when a <see cref="DoaminModelService"/> is closed

    /// </summary>

    event EventHandler<DomainModelServiceClosedEventArgs> OnDomainModelServiceClosed;

    /// <summary>

    /// The event that is raised when a <see cref="DoaminModelService"/> is closed

    /// </summary>

    event EventHandler<DomainModelServiceFaultedEventArgs> OnDomainModelServiceFaulted;

    /// <summary>

    /// Deacivate a <see cref="DoaminModelService"/> for the given idenitfier.

    /// </summary>

    /// <param name="identifier">The identifier of the <see cref="DomainModelDescriptor"/></param>

    void DeactivateDomainModelService(Guid identifier);

    /// <summary>

    /// Activate a <see cref="DoaminModelService"/> for a <see="DomainModelDescriptor"/>

    /// </summary>

    /// <param name="domainModelDescriptor">The descriptor that describes the structure of the entites of the domain model</param>

    /// <param name="baseAddress">The base address that the hosted <see cref="DoaminModelService"/> should be bound too</param>

    /// <param name="servicePath">The relative service path for the bound address. For example baseaddress/servicePath</param>

    /// <param name="activationTimeout">The amount of time to wait for the service host to be activated.</param>

    void ActivateDomainModelService(DomainModelDescriptor domainModelDescriptor, Uri baseAddress, String servicePath, TimeSpan activationTimeout);

    /// <summary>

    /// Gets a list of active <see cref="DoaminModelService"/>

    /// </summary>

    /// <returns>A <see cref="IDictionary<Guid, Uri>"/> the <see cref="Guid"/> is a unique identifier for the instance. The <see cref="Uri"/> is the address that the </returns>

    IDictionary<Guid, Uri> GetActiveDomainModelServices();

    /// <summary>

    /// Indicates if this instance has been initalised.

    /// </summary>

    bool IsInitalised { get; }

    /// <summary>

    /// Initalise the <see cref="IDomainModelServiceManager"/> instance

    /// </summary>

    /// <param name="domainModelDescriptors">The list of <see cref="DoaminModelService"/> to initalise</param>

    /// <param name="baseAddress">The base address that the hosted <see cref="DoaminModelService"/> should be bound too</param>

    /// <param name="servicePath">The relative service path for the bound address. For example baseaddress/servicePath</param>

    /// <param name="activationTimeout">The amount of time to wait for a given service host to be activated</param>

    void Initalise(IList<DomainModelDescriptor> domainModelDescriptors, Uri baseAddress, String servicePath, TimeSpan activationTimeout);

}

The key part of the DomainModelServiceManager is the activation and deactivation of DomainModelService instances. There are other functions that the DomainModelServiceManager performs such as logging, handling faulted Service Host instances, but these are ancillary to its core function of managing instances of hosted DomainModelService instances. Discussion of these features are excluded for the sake of brevity. The following code snippets describe the activation process in more detail.
private BlockingCollection<DomainModelDescriptor> _activatableDomainModels;
private List<DomainModelServiceInstance> _activatedDomainModels;
private IServiceHostFactory _serviceHostFactory;


public DomainModelServiceManager()
{
    ......
    
    // the DomainModelServiceManager is an IDomainModelServiceMetadataProvider derived type and can provide the metadata
    _serviceHostFactory = new DomainModelServiceHostFactory(this);
}
        
/// <summary>
/// Activate a <see cref="DoaminModelService"/> for a <see="DomainModelDescriptor"/>
/// </summary>
/// <param name="domainModelDescriptor">The descriptor that describes the structure of the entites of the domain model</param>
/// <param name="baseAddress">The base address that the hosted <see cref="DoaminModelService"/> should be bound too</param>
/// <param name="servicePath">The relative service path for the bound address. For example baseaddress/servicePath</param>
/// <param name="activationTimeout">The amount of time to wait for the service host to be activated.</param>
public void ActivateDomainModelService(DomainModelDescriptor domainModelDescriptor, Uri baseAddress, String servicePath, TimeSpan activationTimeout)
{
    _activatableDomainModels.Add(domainModelDescriptor);


    lock (_activatedDomainModels)
    {
        var domainModelService = CreateDomainModelServiceInstance(baseAddress, servicePath, domainModelDescriptor);


        _activatedDomainModels.Add(domainModelService);


        _logger.Debug("Domain model activation started");


        domainModelService.ServiceHostInstance.Open(activationTimeout);
   }
}
/// <summary>
/// Create a hosted instance of a <see cref="DomainModelService"/>
/// </summary>
/// <param name="domainModelDescriptor">The descriptor that describes the structure of the entites of the domain model</param>
/// <param name="baseAddress">The base address that the hosted <see cref="DoaminModelService"/> should be bound too</param>
/// <param name="servicePath">The relative service path for the bound address. For example baseaddress/servicePath</param>
/// <returns>A <see cref="DomainModelServiceInstance"/></returns>
private DomainModelServiceInstance CreateDomainModelServiceInstance(Uri baseAddress, String servicePath, DomainModelDescriptor domainModelDescriptor)
{
    UriBuilder builder = new UriBuilder(baseAddress);
    builder.Path = String.Format("V{0}/{1}", domainModelDescriptor.Version, servicePath);
    
    // The service host factory adds the instanceing behavior
    var host = _serviceHostFactory.CreateServiceHost(typeof(DomainModelService), new Uri[] { builder.Uri });


    host.Faulted += HandleHostFaulted;
    host.Opened += HandleHostOpened;
    host.Closed += HandleHostClosed;


    DomainModelServiceInstance instance = new DomainModelServiceInstance(domainModelDescriptor, host);


    return instance;
}


public DomainModelDescriptor GetDomainModelMetadata(TimeSpan timeout)
{
    DomainModelDescriptor descriptor = null;


    if(_activatableDomainModels.TryTake(out descriptor, timeout) == false)
    {
        throw new TimeoutException(String.Format("Unable to get domain model metadata within timeout '{0}'", timeout));
    }


    _logger.Debug("Retrived metadata for DomainModel {0} Version {1}", descriptor.Identifier, descriptor.Version);
    
    return descriptor;
}


/// <summary>
/// Handle the host opened event
/// </summary>
/// <remarks>
/// This removes the entry in the pending list and moves it to the activated list.
/// Rasies an event to signal that the open has occured
/// </remarks>
/// <param name="sender">The event sender</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected internal void HandleHostOpened(object sender, EventArgs e)
{
    IServiceHost serviceHost =  ((IServiceHost)sender);


    _logger.Debug("Service host started {0}", serviceHost.BaseAddresses[0]);


    DomainModelServiceInstance openedDomainModel = null;


    lock (_activatedDomainModels)
    {
        openedDomainModel = (from dm in _activatedDomainModels where dm.ServiceHostInstance == sender select dm).FirstOrDefault();
    }


    if (openedDomainModel != null)
    {
        EventHelpers.RaiseEvent<DomainModelServiceOpenedEventArgs>(
        new DomainModelServiceOpenedEventArgs()
        {
            DomainModel = openedDomainModel.DomainModelMetadata
        }, OnDomainModelServiceOpened, this);
    }
    else
    {
        _logger.Warn("Faulted {0} could not be found for address {1}", typeof(DomainModelServiceInstance), serviceHost.BaseAddresses[0]);
    }
}

Deactivation of a DomainModelService is a far less involved process. The host simply has to be closed and the event handlers cleaned up.
/// <summary>
/// Deacivate a <see cref="DoaminModelService"/> for the given idenitfier.
/// </summary>
/// <param name="identifier">The identifier of the <see cref="DomainModelDescriptor"/></param>
public void DeactivateDomainModelService(Guid identifier)
{
    DomainModelServiceInstance domainModelServiceInstance = null;


    lock (_activatedDomainModels)
    {
        domainModelServiceInstance = _activatedDomainModels.Find(
            delegate(DomainModelServiceInstance instance)
            {
                return instance.DomainModelMetadata.Identifier == identifier;
            });
    }
    if (domainModelServiceInstance != null)
    {
        // close the service and detach event handlers
        DeactivateDomainModelService(domainModelServiceInstance);


        lock (_activatedDomainModels)
        {
            _activatedDomainModels.Remove(domainModelServiceInstance);
        }
    }
}

In the next post in the series Part 5 - End To End Example I will put together a detailed end to end example of how all of these component pieces fit together. Hopefully it will aid in the solidification of how the various ideas and concepts hang together.

Wednesday, 4 April 2012

Configurable OData Service - Part 3 – Custom Data Service Provider

In this post I am going to delve into the implementation of the Custom Data Service Provider that will unify the Type metadata, Dynamic Type generation and WCF OData services.
For brevity of this post I am not going to cover all of the classes required for the implementation of the Custom Data Service Provider just those that are key to this implementation. Implementing a Custom Data Service Provider is actually  a relatively straight forward task once you get your head around the interfaces involved. The details behind the steps required in Creating a Data Service Provider has been covered in a series of excellent posts by Alex D James on his blog. I have based my implementation on some of the concepts expressed in those posts.

Class Model Overview
ClassDiagram1ClassDiagram2

There are a number of classes required to expose the underlying custom data source as a service. I will discuss each one in turn to give a simple overview.

DynamicDataService is an abstract base class that derives from DataService<DynamicDataContext> and the IServiceProvider interface. The purpose of this class is to and allow the WCF OData service infrastructure to instantiate the various custom data service provider types via the object GetService(Type serviceType) method of the IServiceProvider interface.

DynamicDataContext is the class that represents the underlying data source that will be exposed via an OData service. It derives from DataContext and the IDynamicDataContext interface.

The IDynamicDataContext interface is a custom interface to abstract the implementation so that the classes of the custom data provider can be unit tested. It derives from DataContext because it is a LINQ to SQL based data source, I will discuss this class in more detail later in this post.

DomainModelService inherits from the abstract base class DynamicDataService. It implements the two abstract methods from the base class:
public abstract IDynamicDataServiceMetadataProvider GetMetadataProvider();


public abstract IDataServiceQueryProvider GetQueryProvider(IDataServiceMetadataProvider metadata);

The DomainModelService generates types using the Data Type Generator to generate the Entity types based on metadata that is provided to the service when it is instantiated. The metadata is also used to create the DynamicDataServiceMetadataProvider instance, the DomainModelService class will be discussed in more detail later in this post.

DynamicDataServiceMetadataProvider derives from the IDynamicDataServiceMetadataProvider interface, the IDynamicDataServiceMetadataProvider interface is derived from the IDataServiceMetadataProvider interface, its purpose is to simply facilitate unit testing. The DynamicDataServiceMetadataProvider class is simply a container for the metadata for the OData service.

DynamicDataServiceQueryProvider derives from the IDataServiceQueryProvider interface and provides the implementation of the IQueryable GetQueryRootForResourceSet(ResourceSet resourceSet)

DynamicDataServiceUpdateProvider derives from IDataServiceUpdateProvider interface and provides the mechanisms by which the OData service framework can create update and delete the exposed data type entities.

In summary the key classes of the implementation are the DomainModelService and the DynamicDataContext. The other classes in reality are pretty much boiler plate and depend mainly on the implementation of the DynamicDataContext.

DomainModelService

The DomainModelService is the primary touch point for exposing the dynamically defined entities types via the WCF OData service framework.
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class DomainModelService : DynamicDataService
{
    private const String AssemblyName = "DomainModel";

    private Dictionary<EntityDescriptor, Type> _entityToTypeMap;
    private Dictionary<ResourceSet, Type> _resourceSetToTypeMap;
    private IDynamicTypeGenerator _dynamicTypeGenerator;
   
    /// <summary>
    /// Initializes a new instance of the <see cref="DomainModelService"/> class.
    /// </summary>
    /// <param name="domainDescriptor">The <see="DomainModelDescriptor"> that this service has to represent.</param>
    public DomainModelService(DomainModelDescriptor domainDescriptor) : this(domainDescriptor, DynamicTypeGenerator.Instance)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="DomainModelService"/> class.
    /// </summary>
    /// <param name="domainDescriptor">The <see="DomainModelDescriptor"> that this service has to represent.</param>
    /// <param name="dynamicTypeGenerator">The type genrator to use for generating the types</param>
    public DomainModelService(DomainModelDescriptor domainDescriptor, IDynamicTypeGenerator dynamicTypeGenerator)
    {
        _dynamicTypeGenerator = dynamicTypeGenerator;
        _entityToTypeMap = GenerateTypes(domainDescriptor);
    }

    /// <summary>
    /// Initializes the configuration for the service instance
    /// </summary>
    /// <param name="config">Configuration settings for the service.</param>
    public static void InitializeService(DataServiceConfiguration config)
    {
        config.SetEntitySetAccessRule("*", EntitySetRights.All);
        config.UseVerboseErrors = true;

        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
        config.DataServiceBehavior.AcceptProjectionRequests = true;
    }

    /// <summary>
    /// Gets instance of an <see cref="IDynamicDataServiceMetadataProvider"/> derived type for this odata service.
    /// </summary>
    /// <returns>An instance of an <see cref="DynamicDataServiceMetadataProvider"/></returns>
    public override IDynamicDataServiceMetadataProvider GetMetadataProvider()
    {
        DynamicDataServiceMetadataProvider metaDataProvider = new DynamicDataServiceMetadataProvider(AssemblyName, "DomainModelService");
        _resourceSetToTypeMap = new Dictionary<ResourceSet, Type>();

        foreach (var entity in _entityToTypeMap.Keys)
        {
            Type t = _entityToTypeMap[entity];
            var resourceType = new ResourceType(t, ResourceTypeKind.EntityType, null, AssemblyName, entity.EntityName, false);

            foreach (var property in entity.EntityProperties)
            {
                ResourcePropertyKind kind = ResourcePropertyKind.Primitive;

                if (property.IsIdentity)
                {
                    kind |= ResourcePropertyKind.Key;
                }
               
                var resourceProperty = new ResourceProperty(property.Name,
                    kind, ResourceType.GetPrimitiveResourceType(property.Type));

                resourceType.AddProperty(resourceProperty);
            }

            metaDataProvider.AddResourceType(resourceType);
            ResourceSet resourceSet = new ResourceSet(String.Format("{0}s",entity.EntityName), resourceType);
            metaDataProvider.AddResourceSet(resourceSet);
            _resourceSetToTypeMap.Add(resourceSet, t);
        }

        return metaDataProvider;
    }

    /// <summary>
    /// Gets instance of an <see cref="IDataServiceMetadataProvider"/> derived type for this odata service.
    /// </summary>
    /// <param name="metadataProvider">The <see cref="IDataServiceMetadataProvider"/></param>
    /// <returns>An instance of an <see cref="DynamicDataServiceQueryProvider"/></returns>
    public override IDataServiceQueryProvider GetQueryProvider(IDataServiceMetadataProvider metadataProvider)
    {
        return new DynamicDataServiceQueryProvider(metadataProvider);
    }

    /// <summary>
    /// Create the underlying data source for this odata service
    /// </summary>
    /// <returns>An instance of an <see cref="DynamicDataContext"/> class</returns>
    protected override DynamicDataContext CreateDataSource()
    {
        return new DynamicDataContext(_resourceSetToTypeMap,
            ConfigurationManager.ConnectionStrings[DomainModelServiceSettings.Current.TargetStoreConnectionStringName].ConnectionString);
    }

    /// <summary>
    /// Generates types dynamically that represent the <see cref="EntityDescriptor"/> entities of the <paramref name="domainDescriptor"/>
    /// </summary>
    /// <param name="domainDescriptor">The <see cref="DomainModelDescriptor"/> that the types are to be generated for.</param>
    /// <returns>A <see cref="Dictionary"/> that contains mapping between an <see cref="EntityDescriptor"/> and the generated type</returns>
    private Dictionary<EntityDescriptor, Type> GenerateTypes(DomainModelDescriptor domainDescriptor)
    {
        Dictionary<EntityDescriptor, Type> map = new Dictionary<EntityDescriptor, Type>();
        AssemblyBuilder assemblyBuilder = null;
        ModuleBuilder moduleBuilder = null;
       
        _dynamicTypeGenerator.CreateDynamicAssembly(AssemblyName, out assemblyBuilder, out moduleBuilder);

        foreach (var entity in domainDescriptor.Entities)
        {
            map.Add(entity, _dynamicTypeGenerator.CreateLinqToSqlType(AssemblyName, assemblyBuilder, moduleBuilder, entity));
        }

        if (DomainModelServiceSettings.Current.StoreGeneratedAssemblies)
        {
            assemblyBuilder.Save(String.Format("{1}.dll", domainDescriptor.Version, AssemblyName));
        }

        return map;
    }
}

The service requires an instance of a DomainModelDescriptor class to be provided when the service is constructed. The DomainModelDescriptor defines the domain model entities and their properties that are to be exposed as an OData service. The constructor generates a set of types that map to the EntityDescriptor types that are contained within the DomainModelDescriptor instance.

metadata

When the OData framework invokes the on the object GetService(Type serviceType) method of the IServiceProvider interface this in turn invokes the two overridden methods GetMetadataProvider() and  GetQueryProvider(IDataServiceMetadataProvider metadata) methods.

Of the two methods only the GetMetadataProvider() is of interest. This method creates an instance of a DynamicDataServiceMetadataProvider type and populates it with the required ResourceSet, ResourceTypes, ResourceProperty values that will enable the OData framework to describe the underlying data source structure to an OData client.

Another key method is the CreateDataSource() method that creates the underlying data source for the OData service. In this implementation the data source is an instance of a DynamicDataContext. The DynamicDataServiceQueryProvider and DynamicDataServiceUpdateProvider types both leverage the functionality of the DynamicDataContext provider to perform their function.

DynamicDataContext

The DynamicDataContext class is a simple Linq To Sql DataContext derived class that is responsible for marshalling calls between the Custom Data Service Provider components and the underlying data store.
public class DynamicDataContext : DataContext, IDynamicDataContext
{
    private Dictionary<ResourceSet, Type> _resourceSetToTypeMap;


    /// <summary>
    /// Initializes a new instance of the <see cref="DynamicDataContext"/> class
    /// </summary>
    /// <param name="resourceSetToTypeMap">A <see cref="Dictionary"/> that contains a mapping from a <see cref="ResourceSet"/> to an underlying type</param>
    /// <param name="connection">The connection string for this <see cref="DataContext"/> derived type to use</param>
    public DynamicDataContext(Dictionary<ResourceSet, Type> resourceSetToTypeMap, String connection) : base(connection) 
    {
        _resourceSetToTypeMap = resourceSetToTypeMap;
    }


    /// <summary>
    /// Gets an instance of an <see cref="IQueryable"/> for the <paramref name="resourceSet"/>
    /// </summary>
    /// <param name="resourceSet">The resource set to retrieve the <see cref="IQueryable"/> instance for</param>
    /// <returns>
    /// An instance of an <see cref="IQueryable"/> type
    /// </returns>
    public IQueryable GetQueryableForResourceSet(ResourceSet resourceSet)
    {
        Type t = _resourceSetToTypeMap[resourceSet];


        return GetTableForType(t);
    }


    /// <summary>
    /// Add a resource to the underlying store
    /// </summary>
    /// <param name="resource">The resource to add to the store</param>
    public void AddResource(object resource)
    {
        ITable table = GetTableForType(resource.GetType());
        IEntity baseType = (IEntity) resource;


        if (baseType.Identifier == Guid.Empty)
        {
            baseType.Identifier = Guid.NewGuid();
        }


        table.Attach(resource);
    }


    /// <summary>
    /// Remove a resource from the underlying store
    /// </summary>
    /// <param name="resource">The resource to remove</param>
    public void RemoveResource(object resource)
    {
        ITable table = GetTableForType(resource.GetType());


        table.DeleteOnSubmit(resource);
    }


    /// <summary>
    /// Commit changes to the underlying storeB
    /// </summary>
    public void CommitChanges()
    {
        this.SubmitChanges();
    }


    private ITable GetTableForType(Type type)
    {
        MethodInfo method = typeof(DataContext).GetMethod("GetTable", Type.EmptyTypes);
        MethodInfo generic = method.MakeGenericMethod(type);
        return (ITable) generic.Invoke(this, null);
    }
}

As you can see there is nothing particularly complex about the DynamicDataContext. The GetQueryableForResourceSet simply maps ResourceSet to a type to provide an IQueryable object for querying data, which the OData framework will expose. When modifying or deleting entities in the data store the GetTableForType method uses some simple reflection to get the underlying ITable derived object to which changes are submitted.

Now we have a OData service that can be dynamically configured based on metadata. In the next post, Part 4 – WCF Service Host, I will cover the activation and instantiation of a DomainModelService.