When designing your Service Layer you need to choose a communication mechanism according to your needs. Duplex, Fire and Forget, Reliable Sessions and Request Response are the most common messaging patterns.
With Request-response, the client expects to receive a response for every message that's been sent. You can design such a Service Layer using an ESB architecture if you need to connect different applications (even from different platforms). Though, if you need to write a typical client - back-end - database application you can choose not to go with an ESB (Rhino ServiceBus, NServiceBus) and use WCF.
When using NHibernate in an enterprise application the most common issue is how to automatically open and close a Session (per Request). With WCF you need to follow the steps described here. Also for each of your services you need to expose an ABC via app.config. On the other hand using Rhino ServiceBus and NServiceBus makes your life a lot easier.
Davy Brion mentions here with more detail what I've discussed above and he provides the Agatha project. With it, you define RequestHandlers (like Consumers in the ESB world) and Agatha infrastructure will handle the rest. Everything is done with a single endpoint which makes you forget about app.config and ABCs.
With Agatha NHibernate Session-per-Request is easy. You can use your favorite DI container and have a session provided through your handler's constructor. You can even abstract the session by following the steps described here.
With Agatha, you can host your services easily on IIS or in a Console Application using AlwaysUp. If you choose the latter there are a few things to do inside your Main() method.
Uri baseAddress = new Uri("<uriString goes here>"); ServiceHost host = new ServiceHost(typeof(WcfRequestProcessor), baseAddress); host.Open(); // Agatha's WcfRequestProcessor handles the requests. // Your service is up. There is really nothing else to do here. System.Console.ReadKey(false); host.Close();
Here I create an instance of the ServiceHost class, passing a WcfRequestProcessor type that represents the service type and the base address Uniform Resource Identifier (URI) to the ServiceHost.
Here is the generic app.config that Agatha needs:
<?xml version="1.0"?> <configuration> <system.serviceModel> <services> <service name="Agatha.ServiceLayer.WCF.WcfRequestProcessor" behaviorConfiguration="RequestProcessorBehavior"> <endpoint address="" contract="Agatha.Common.WCF.IWcfRequestProcessor" binding="basicHttpBinding" bindingConfiguration="RequestProcessorBinding"/> </service> </services> <bindings> <basicHttpBinding> <binding name="RequestProcessorBinding" maxReceivedMessageSize="2147483647" receiveTimeout="00:30:00" sendTimeout="00:30:00"> <readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647"/> <security mode="None" /> </binding> </basicHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="RequestProcessorBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="true"/> <dataContractSerializer maxItemsInObjectGraph="2147483647"/> <serviceThrottling maxConcurrentCalls="500" maxConcurrentInstances="500"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> <runtime> <gcServer enabled="true"/> </runtime> </configuration>
I included the <gcServer> element to instruct CLR to use server garbage collection. Inside your Main() method you can include the code below:
Console.WriteLine("{0} is {1}running with server GC.", Assembly.GetEntryAssembly().GetName().Name, GCSettings.IsServerGC == true ? string.Empty : "not ");
I would really like to share how it looks in Process Explorer when handling 6000 requests using Agatha.
Agatha 6000 request-response synchronous
Agatha 6000 request-response asynchronous
Why in the asynchronous case the memory consumption is a little higher? Well, this is something expected. Every asynchronous call creates an IAsyncResult object. So even that the calls are non-blocking we have an overhead of 6000 IAsyncResult objects on the heap (yes, on the heap since they are not value types). Though, as you can see from the screenshot, when the GC kicks in the memory is reclaimed.
Well that's it! Agatha works great, it's great, I really like it. Goodbye to the existing (and conservative, and painful) WCF usage.
The sample application can be found here.
Make sure you are running the application with elevated priviledges else a System.ServiceModel.AddressAccessDeniedException will be thrown indicating that your process does not have access rights to get registered at the base address were your service is hosted.