Setting Up WCF with Multiple Services and Multiple Databases Using NHibernate and Ninject

This is a tutorial on how to setup WCF using NHibernate and Ninject with multiple databases and services. I’ve seen many examples of how to setup WCF with NHibernate, or Ninject, but not with both. None of the examples I’ve seen have used multiple services or multiples databases either. I had to do this, so I thought I’d share. We’ll setup this code as if we’re creating some sort of blog application.

First, create an assembly for each of the following: business logic, services, service contracts, and the service host.

We want to separate all these into different assemblies so we don’t have issues with circular dependencies. The other option is to have everything in a single assembly, but that usually isn’t an option, or a very good one for that matter.

Contracts

The contract assembly is where we keep all the contracts, or interfaces, that are used in our services. This makes it so that the services can reference and use the contracts, and so can a client to the services. If the service is visible to projects outside of yours, you can just give them the contract assembly.

Let’s create a service contract, and some data contracts for our blog application. I put them into folders to separate the service contracts from the data contracts.

using System.Collections.Generic;
using System.ServiceModel;
using Wnn.Service.Contract.DataContracts;
namespace Wnn.Service.Contract.ServiceContracts
{
[ServiceContract]
public interface IBlogService
{
[OperationContract]
Post GetPostById( int id );
[OperationContract]
User GetUserById( int id );
[OperationContract]
User GetUserByUserNameAndPassword( string userName, string password );
[OperationContract]
List<Post> GetPosts();
[OperationContract]
List<Post> GetPostsFromAuthor( string userName );
}
}
view raw IBlogService.cs hosted with ❤ by GitHub
Post.cs
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Wnn.Service.Contract.DataContracts
{
[DataContract]
public class Post
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Title { get; set; }
[DataMember]
public string Content { get; set; }
[DataMember]
public DateTime Created { get; set; }
[DataMember]
public string Author { get; set; }
[DataMember]
public List<string> Tags { get; set; }
}
}
view raw Post.cs hosted with ❤ by GitHub
using System.Runtime.Serialization;
namespace Wnn.Service.Contract.DataContracts
{
[DataContract]
public class User
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string UserName { get; set; }
[DataMember]
public string Email { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
}
view raw User.cs hosted with ❤ by GitHub

Business Logic

Let’s create our business objects, repositories to access our database, and all the NHibernate guts. We don’t have a data layer here because NHibernate IS our data layer. There could still be a use of separating some stuff out into a data layer, but I will not be doing that. References will need to be added to NHibernate.dll, FluentNHibernate.dll, NHibernate.ByteCode.LinFu.dll, and NHibernate.Linq.dll.

Let’s create our POCO (plain old CLR object) business objects. All members need to be virtual so NHibernate can use them.

In the password property we want to encrypt any password that gets set.

using System;
using System.Security.Cryptography;
using System.Text;
namespace Wnn.Business.Objects
{
public class User
{
private string password;
public virtual int Id { get; protected set; }
public virtual string UserName { get; set; }
public virtual string Email { get; set; }
public virtual string Password
{
get { return password; }
set
{
var md5 = MD5.Create();
var passwordBytes = Encoding.UTF8.GetBytes( value );
md5.ComputeHash( passwordBytes );
password = Convert.ToBase64String( md5.Hash );
}
}
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
}
view raw User2.cs hosted with ❤ by GitHub
namespace Wnn.Business.Objects
{
public class Tag
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual Post Post { get; set; }
}
}
view raw Tag.cs hosted with ❤ by GitHub
using System;
using System.Collections.Generic;
namespace Wnn.Business.Objects
{
public class Post
{
public virtual int Id { get; protected set; }
public virtual string Title { get; set; }
public virtual string Content { get; set; }
public virtual DateTime Created { get; set; }
public virtual User Author { get; set; }
public virtual List<Tag> Tags { get; set; }
public virtual void AddTag( Tag tag )
{
tag.Post = this;
Tags.Add( tag );
}
}
}
view raw Post2.cs hosted with ❤ by GitHub

The AddTag method is used to set both sides of the object association. Otherwise NHibernate will not save them properly if a tag is added to the tags list.

Let’s create our NHibernate mapping files now. I use FluentNHibernate to do this.

Since we’re using a backing field for the Password property, we need to have NHibernate use that instead. We don’t want NHibernate to use the property because the stored hash would get hashed again when set.

using FluentNHibernate.Mapping;
using Wnn.Business.Objects;
namespace Wnn.Business.Mappings
{
public class UserMap : ClassMap<User>
{
public UserMap()
{
SetupMapping();
}
private void SetupMapping()
{
Table( "Users" );
Id( m => m.Id );
Map( m => m.Email );
Map( m => m.FirstName );
Map( m => m.LastName );
Map( m => m.Password ).Access.CamelCaseField();
Map( m => m.UserName );
}
}
}
view raw UserMap.cs hosted with ❤ by GitHub
using FluentNHibernate.Mapping;
using Wnn.Business.Objects;
namespace Wnn.Business.Mappings
{
public class TagMap : ClassMap<Tag>
{
public TagMap()
{
SetupMapping();
}
private void SetupMapping()
{
Table( "Tags" );
Id( m => m.Id );
Map( m => m.Name );
References( m => m.Post, "PostId" );
}
}
}
view raw TagMap.cs hosted with ❤ by GitHub
using FluentNHibernate.Mapping;
using Wnn.Business.Objects;
namespace Wnn.Business.Mappings
{
public class PostMap : ClassMap<Post>
{
public PostMap()
{
SetupMapping();
}
private void SetupMapping()
{
Table( "Posts" );
Id( m => m.Id );
Map( m => m.Content );
Map( m => m.Title );
References( m => m.Author, "UserId" );
HasMany( m => m.Tags )
.Inverse()
.Cascade.AllDeleteOrphan()
.KeyColumns.Add( "PostId" )
.Fetch.Subselect();
}
}
}
view raw PostMap.cs hosted with ❤ by GitHub

Next we’re going to setup our repositories. This is where we access our data. Before we can do that though, we need to create a SessionManager object. SessionManager is just a wrapper around the NHibernate ISession object to allow for easier unit testing. We’re not hiding the fact that we’re using NHibernate though. The SessionManager wrapper still returns NHibernate objects. This could be completely abstracted so it didn’t matter which ORM or data layer is being used.

using System;
using System.Data;
using System.Linq;
using NHibernate;
using NHibernate.Linq;
namespace Wnn.Business
{
public class SessionManager
{
private readonly ISession session;
public SessionManager( ISession session )
{
if( session == null )
{
throw new ArgumentNullException( "session" );
}
this.session = session;
}
public virtual IQueryable<T> Linq<T>()
{
return session.Linq<T>();
}
public virtual T Get<T>( int id )
{
return session.Get<T>( id );
}
public virtual ISQLQuery CreateSqlQuery( string queryString )
{
return session.CreateSQLQuery( queryString );
}
public virtual void SaveOrUpdate( object obj )
{
session.SaveOrUpdate( obj );
}
public virtual void Delete( object obj )
{
session.Delete( obj );
}
public virtual ITransaction BeginTransaction()
{
return session.BeginTransaction();
}
public virtual IDbConnection Close()
{
return session.Close();
}
public virtual void Dispose()
{
session.Dispose();
}
}
}
view raw SessionManager.cs hosted with ❤ by GitHub

Now we can create the repositories and a base repository class for common methods.

We put any common methods in the base class.

using System.Collections.Generic;
using System.Linq;
namespace Wnn.Business.Repositories
{
public abstract class RepositoryBase<T>
{
protected SessionManager SessionManager { get; set; }
protected RepositoryBase( SessionManager sessionManager )
{
SessionManager = sessionManager;
}
public T Get( int id )
{
return SessionManager.Get<T>( id );
}
public List<T> GetAll()
{
return SessionManager.Linq<T>().ToList();
}
public void SaveOrUpdate( T obj )
{
SessionManager.SaveOrUpdate( obj );
}
public void Delete( T obj )
{
SessionManager.Delete( obj );
}
}
}
view raw RepositoryBase.cs hosted with ❤ by GitHub
using System.Linq;
using Wnn.Business.Objects;
namespace Wnn.Business.Repositories
{
public class UserRepository : RepositoryBase<User>
{
public UserRepository( SessionManager sessionManager ) : base( sessionManager ){}
public User GetByUserNameAndPassword( string userName, string password )
{
return ( from u in SessionManager.Linq<User>()
where u.UserName == userName && password == u.Password
select u ).SingleOrDefault();
}
}
}
view raw UserRepository.cs hosted with ❤ by GitHub
using Wnn.Business.Objects;
namespace Wnn.Business.Repositories
{
public class TagRepository : RepositoryBase<Tag>
{
public TagRepository( SessionManager sessionManager ) : base( sessionManager ){}
}
}
view raw TagRepository.cs hosted with ❤ by GitHub
using System.Collections.Generic;
using System.Linq;
using Wnn.Business.Objects;
namespace Wnn.Business.Repositories
{
public class PostRepository : RepositoryBase<Post>
{
public PostRepository( SessionManager sessionManager ) : base( sessionManager ){}
public List<Post> GetAllForUser( string userName )
{
return ( from p in SessionManager.Linq<Post>()
where p.Author.UserName == userName
select p ).ToList();
}
}
}
view raw PostRepository.cs hosted with ❤ by GitHub

Services

Now that we have our repositories setup, we can implement the services that will use the repositories.

We need to set the InstanceContextMode to PerCall. This makes it so WCF will create a new context instance per service call. This sets up NHibernate nicely because we can then start a transaction at the beginning of every service method call, and end the transaction at the end of the call, rolling back if an error occurred. More on that later.

using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using Wnn.Business.Repositories;
using Wnn.Service.Contract.DataContracts;
using Wnn.Service.Contract.ServiceContracts;
namespace Wnn.Service
{
[ServiceBehavior( InstanceContextMode = InstanceContextMode.PerCall )]
public class BlogService : IBlogService
{
private readonly UserRepository userRepository;
private readonly PostRepository postRepository;
private readonly TagRepository tagRepository;
public BlogService( UserRepository userRepository, PostRepository postRepository, TagRepository tagRepository )
{
this.userRepository = userRepository;
this.postRepository = postRepository;
this.tagRepository = tagRepository;
}
public Post GetPostById( int id )
{
return ConvertPost( postRepository.Get( id ) );
}
public User GetUserById( int id )
{
return ConvertUser( userRepository.Get( id ) );
}
public User GetUserByUserNameAndPassword( string userName, string password )
{
return ConvertUser( userRepository.GetByUserNameAndPassword( userName, password ) );
}
public List<Post> GetPosts()
{
return ( from p in postRepository.GetAll()
select ConvertPost( p ) ).ToList();
}
public List<Post> GetPostsFromAuthor( string userName )
{
return ( from p in postRepository.GetAllForUser( userName )
select ConvertPost( p ) ).ToList();
}
private static User ConvertUser( Business.Objects.User userData )
{
User user = null;
if( userData != null )
{
user = new User
{
Email = userData.Email,
FirstName = userData.FirstName,
Id = userData.Id,
LastName = userData.LastName,
UserName = userData.UserName,
};
}
return user;
}
private static Post ConvertPost( Business.Objects.Post postData )
{
Post post = null;
if( postData != null )
{
post = new Post
{
Author = postData.Author.UserName,
Content = postData.Content,
Created = postData.Created,
Id = postData.Id,
Title = postData.Title,
Tags = ( from t in postData.Tags
select t.Name ).ToList()
};
}
return post;
}
}
}
view raw BlogService.cs hosted with ❤ by GitHub

Service Host

The service host is what actually starts up all of our services. In this case, just one service, but this is being created so we can have as many as we want.

using System.Collections.Generic;
using System.ServiceModel;
namespace Wnn.Service.Host
{
public class ServiceHostEngine
{
private readonly List<ServiceHost> serviceHosts = new List<ServiceHost>();
public ServiceHostEngine()
{
// We can have multiple services listed here.
// We could actually have a list of services in a config
// file that is dynamically loaded here also.
serviceHosts.Add( new ServiceHost( typeof( BlogService ) ) );
}
public void Start()
{
foreach( var serviceHost in serviceHosts )
{
serviceHost.Open();
}
}
public void Stop()
{
foreach( var serviceHost in serviceHosts )
{
serviceHost.Close();
}
}
}
}
view raw ServiceHostEngine.cs hosted with ❤ by GitHub

We are now to a part where Ninject is used to create the NHibernate session instances. We need to create a factory for creating NHibernate SessionFactories. We need a factory for every database that is being used. So, we need a SessionFactoryFactory. This will be back in our business layer.

using System.Collections.Generic;
using FluentNHibernate.Cfg;
using NHibernate;
namespace Wnn.Business
{
public static class SessionFactoryFactory
{
private static readonly object locker = new object();
private static readonly Dictionary<string, ISessionFactory> sessionFactories = new Dictionary<string, ISessionFactory>();
public static ISessionFactory GetSessionFactory( string connectionStringKey )
{
ISessionFactory sessionFactory;
sessionFactories.TryGetValue( connectionStringKey, out sessionFactory );
// Double check locking is used here because
// WCF could have many instances accessing this
// code at the same time.
if( sessionFactory == null )
{
lock( locker )
{
sessionFactories.TryGetValue( connectionStringKey, out sessionFactory );
if( sessionFactory == null )
{
sessionFactory = Fluently.Configure()
.Database(
FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2005.ConnectionString(
c => c.FromConnectionStringWithKey( connectionStringKey ) )
.ProxyFactoryFactory( "NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu" ) )
.Mappings( m => m.FluentMappings.AddFromAssembly( typeof( SessionFactoryFactory ).Assembly ) )
.ExposeConfiguration( cfg => cfg.SetProperty( "generate_statistics", "true" ) )
.ExposeConfiguration( cfg => cfg.SetProperty( "adonet.batch_size", "10" ) )
.BuildSessionFactory();
sessionFactories[connectionStringKey] = sessionFactory;
}
}
}
return sessionFactory;
}
}
}

Now that we are able to create NHibernate sessions, let’s setup Ninject to create the ISession an SessionManager objects for us. We need to pass the connection string in to our Ninject setup so the SessionFactoryFactory can use the correct database connection. We will be creating a new Ninject kernel each WCF call, passing in the connection string.

using NHibernate;
using Ninject.Modules;
using Wnn.Business;
namespace Wnn.Service.Host
{
public class NinjectSetup : NinjectModule
{
private readonly string connectionStringKey;
public NinjectSetup( string connectionStringKey )
{
this.connectionStringKey = connectionStringKey;
}
public override void Load()
{
Bind<ISession>()
.ToMethod( ctx => SessionFactoryFactory.GetSessionFactory( connectionStringKey ).OpenSession() )
.InSingletonScope();
Bind<SessionManager>()
.ToSelf()
.InSingletonScope();
}
}
}
view raw NinjectSetup.cs hosted with ❤ by GitHub

Now we can create our instance provider. The provider is what creates the instances that WCF uses. This gets called for every service method call because we used InstanceContextMode.PerCall for our services.

In the GetInstance method (which again happens per call), we create a Ninject kernel that we can use to create our objects for us. A SessionManager is created and a transaction is started. A transaction is meant to be used for a unit of work. Our unit of work here is a single service method call. A new service instance is then created by Ninject. This means that all our repositories in the constructor of the services will be injected for us.

The ReleaseInstance method is then called when the request is complete. In here we commit our transaction. If there are any errors, they are rolled back. The session is then closed.

using System;
using System.Runtime.Remoting.Messaging;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using NHibernate;
using Ninject;
using Wnn.Business;
namespace Wnn.Service.Host
{
public class NHibernateInstanceProvider : IInstanceProvider
{
private const string sessionKey = "ThreadSession";
private const string transactionKey = "ThreadTransaction";
private readonly Type serviceType;
private static SessionManager SessionManager
{
get { return CallContext.GetData( sessionKey ) as SessionManager; }
set { CallContext.SetData( sessionKey, value ); }
}
private static ITransaction Transaction
{
get { return CallContext.GetData( transactionKey ) as ITransaction; }
set { CallContext.SetData( transactionKey, value ); }
}
public NHibernateInstanceProvider( Type serviceType )
{
this.serviceType = serviceType;
}
public object GetInstance( InstanceContext instanceContext )
{
return GetInstance( instanceContext, null );
}
public object GetInstance( InstanceContext instanceContext, Message message )
{
var connectionStringKey = GetConnectionStringKey( serviceType );
var kernel = new StandardKernel( new NinjectSetup( connectionStringKey ) );
SessionManager = kernel.Get<SessionManager>();
Transaction = SessionManager.BeginTransaction();
var instance = kernel.Get( serviceType );
return instance;
}
public void ReleaseInstance( InstanceContext instanceContext, object instance )
{
try
{
Transaction.Commit();
}
catch
{
Transaction.Rollback();
}
finally
{
SessionManager.Close();
SessionManager.Dispose();
}
}
private static string GetConnectionStringKey( Type serviceType )
{
// All of our database connections should
// go here. We could also put all of these types
// into a config file and load them dynamically.
string connectionStringKey;
if( serviceType == typeof( BlogService ) )
{
connectionStringKey = "Dashboard";
}
else
{
throw new Exception( "Service type '{0}' does not match any database connection." );
}
return connectionStringKey;
}
}
}

Now you may be wondering how all of this is setup. How do we let WCF know that it should use our instance provider instead? There are a couple custom configuration classes we need to create and we need to add our provider to the WCF configuration. Whatever app is running the WCF service; console app, windows service, or web app; a custom behavior needs to be added to it. In the services section, we setup our service with our custom element.

<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="nhibernateServiceBehavior" type="Wnn.Service.Host.NHibernateBehaviorExtensionElement, Wnn.Service.Host, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior name="nhibernateServiceBehavior">
<nhibernateServiceBehavior />
<serviceMetadata />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="HttpEnableBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="Wnn.Service.BlogService" behaviorConfiguration="nhibernateServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8761/Services/" />
</baseAddresses>
</host>
<endpoint address="Blog" binding="netTcpBinding" contract="Wnn.Service.Contract.ServiceContracts.IBlogService" />
</service>
</services>
</system.serviceModel>
view raw web.config.xml hosted with ❤ by GitHub

The custom element returns a new instance of our service behavior.

using System;
using System.ServiceModel.Configuration;
namespace Wnn.Service.Host
{
public class NHibernateBehaviorExtensionElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new NHibernateServiceBehavior();
}
public override Type BehaviorType
{
get { return typeof( NHibernateServiceBehavior ); }
}
}
}

The service behavior is what actually creates out custom instance provider, passing in the service that is being used.

using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace Wnn.Service.Host
{
public class NHibernateServiceBehavior : Attribute, IServiceBehavior
{
public void Validate( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase )
{
}
public void AddBindingParameters( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters )
{
}
public void ApplyDispatchBehavior( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase )
{
foreach( var channelDispatcherBase in serviceHostBase.ChannelDispatchers )
{
var channelDispatcher = channelDispatcherBase as ChannelDispatcher;
if( channelDispatcher == null )
{
continue;
}
foreach( var ed in channelDispatcher.Endpoints )
{
ed.DispatchRuntime.InstanceProvider = new NHibernateInstanceProvider( serviceDescription.ServiceType );
}
}
}
}
}

The WCF setup is now complete. We able to have multiple services running and use multiple databases. All the services objects are created by Ninject. Our WCF application is now a lot more unit testable that it was before. This may seem like a lot of work to setup, but once it’s done, everything else is a breeze. You will be able to add services and databases easily, and you don’t have to worry about creating your objects in your service.

The last step is to actually host this service in a console app, windows service or web app. You are limited to only using HTTP bindings if you choose to use an IIS hosted service. All you have to do then is to create an instance of the ServiceHostEngine, and start it.

Here is an example of a console app:

namespace Wnn.Service.Host.Console
{
class Program
{
static void Main( string[] args )
{
var service = new ServiceHostEngine();
service.Start();
System.Console.WriteLine( "Press any key to stop the services" );
System.Console.ReadKey();
}
}
}
view raw Program.cs hosted with ❤ by GitHub

I put the code up on github. http://github.com/JoshClose/WcfNhibernateNinjectExample

Leave a Reply