Showing posts with label WCF. Show all posts
Showing posts with label WCF. Show all posts

Tuesday, October 28, 2014

WCF + LightInject + NHibernate (part 2 of 2)

On the part 1 of this post, I showed you how to integrate WCF and LightInject together. On this last part, I'll show you how to integrate NHibernate to them


This has 12 steps, let's begin


1. To continue, let's add a DTO project using Class Library project, name it Dtos. Delete Class1.cs

Add reference to System.Runtime.Serialization assembly

Add a DTO class, name it PersonDto.Member.cs. Static classes enforce schema-like organization of domain models / DTOs

using System.Runtime.Serialization;
 
namespace Dtos
{
    public static partial class PersonDto
    {
        [DataContract]
        public class Member
        {
            [DataMember]
            public int    MemberId   { get; set; }
            [DataMember]
            public string MemberName { get; set; }
        }
    }
}


2. On ServiceContracts project, add reference to Dtos project

Then add PersonContract.IMemberService.cs interface:
using Dtos;
using System.Collections.Generic;
using System.ServiceModel;
 
namespace ServiceContracts
{
    public static partial class PersonContract
    {
 
        [ServiceContract]
        public interface IMemberService
        {
            [OperationContract]
            string GetMembership();
 
 
            [OperationContract]
            IEnumerable<PersonDto.Member> GetMembers();
        }
    }
}


3. On ServiceImplementations project, add reference to Dtos project

Then add implementation for PersonContract.IMemberService interface, name it PersonImplementation.MemberService.cs, use the following code:
using System.Collections.Generic;
 
namespace ServiceImplementations
{
    public static partial class PersonImplementation
    {
        [System.ServiceModel.ServiceBehavior(InstanceContextMode = System.ServiceModel.InstanceContextMode.PerCall)]
        public class MemberService : ServiceContracts.PersonContract.IMemberService
        {
 
            IEnumerable<Dtos.PersonDto.Member> ServiceContracts.PersonContract.IMemberService.GetMembers()
            {
                yield return new Dtos.PersonDto.Member { MemberId = 1, MemberName = "John" };
                yield return new Dtos.PersonDto.Member { MemberId = 2, MemberName = "Paul" };
                yield return new Dtos.PersonDto.Member { MemberId = 3, MemberName = "George" };
                yield return new Dtos.PersonDto.Member { MemberId = 4, MemberName = "Ringo" };
            }
 
            string ServiceContracts.PersonContract.IMemberService.GetMembership()
            {
                return "Avengers";
            }
        }
    }
}


4. On AppService, open ServiceHostingEnvironment.tt, then save it even there's no changes on its code. Saving the text template will trigger the regeneration of the ServiceHostingEnvironment.config. The updated ServiceHostingEnvironment.config shall look like this:

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
    <serviceActivations>
 
        <add relativeAddress="PersonImplementation.MemberService.svc" service="ServiceImplementations.PersonImplementation+MemberService" factory="AppService.LightInjectServiceHostFactory"/>
        <add relativeAddress="SampleService.svc" service="ServiceImplementations.SampleService" factory="AppService.LightInjectServiceHostFactory"/>
       
    </serviceActivations>
</serviceHostingEnvironment>


5. Run AppService

To test, run WcfTestCLient, path is: "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\WcfTestClient.exe"

Add Service on My Service Projects, then use this endpoint address: http://localhost:50549/PersonImplementation.MemberService.svc

Invoke GetMembers(), you shall see this output:




6. On NHibernate integration, step 0, run this DDL script:
/*

drop table Person.Member;
drop schema Person;

*/

create schema Person;

go


create table Person.Member
(
MemberId int not null identity(1,1),
MemberName nvarchar(100) not null,


constraint pk_Person_Member primary key(MemberId)
);

go


insert into Person.Member(MemberName) values
('Ironman'),
('Captain America'),
('Thor'),
('Hulk');

go




7. NHibernate's Linq is using extension method, and as such is virtually impossible to mock properly. With that said, we need to add a thin layer for accessing NHibernate, which can be mocked if we need to unit test the rich domain model's behavior

For that thin layer, create a class library project, name it UnitTestFriendlyDal

Add NHibernate from NuGet Packages

Create a class file, name it DomainAccess.cs. Then use this code on DomainAccess.cs:

using System;
using System.Linq;

using NHibernate.Linq;

namespace UnitTestFriendlyDal
{

    public interface IDomainAccessFactory
    {
        IDomainAccess OpenDomainAccess();
    }

    public class DomainAccessFactory : IDomainAccessFactory
    {
        NHibernate.ISessionFactory _sessionFactory;

        public DomainAccessFactory(NHibernate.ISessionFactory sessionFactory)
        {
            _sessionFactory = sessionFactory;
        }

        public IDomainAccess OpenDomainAccess()
        {
            return new DomainAccess(_sessionFactory);
        }
    }


    public interface IDomainAccess : IDisposable
    {
        IQueryable<T> Query<T>();
        T Get<T>(object id);
        T Load<T>(object id);
        object Save(object transientObject);
        void Evict<T>(object id);
    }

    class DomainAccess : IDomainAccess
    {

        NHibernate.ISessionFactory _sessionFactory;
        NHibernate.ISession _session;
        NHibernate.ITransaction _transaction;




        public DomainAccess(NHibernate.ISessionFactory sessionFactory)
        {
            _sessionFactory = sessionFactory;
            _session = _sessionFactory.OpenSession();
            _transaction = _session.BeginTransaction();
        }


        IQueryable<T> IDomainAccess.Query<T>()
        {
            return _session.Query<T>();
        }


        T IDomainAccess.Get<T>(object id)
        {
            return _session.Get<T>(id);
        }

        T IDomainAccess.Load<T>(object id)
        {
            return _session.Load<T>(id);
        }



        void IDomainAccess.Evict<T>(object id)
        {
            _sessionFactory.Evict(typeof(T), id);
        }



        object IDomainAccess.Save(object transientObject)
        {
            return _session.Save(transientObject);
        }


        // Because transaction is a cross-cutting concern. It should be automated
        void IDisposable.Dispose()
        {
            // http://www.hibernatingrhinos.com/products/nhprof/learn/alert/donotuseimplicittransactions

            _transaction.Commit();
            _transaction.Dispose();
            _session.Dispose();
        }


    }//DomainAccess


    public static class LinqExtensionMethods
    {
        public static IQueryable<T> GetPage<T>(this IQueryable<T> query, int pageLimit, int pageNumber)
        {
            var paged = query.Take(pageLimit).Skip(pageLimit * (pageNumber - 1));

            return paged;
        }
    }


    /// <summary>
    /// cross-cutting concern   
    /// MakeCacheable replaces Cacheable, so IQueryable detection provider can be done here
    /// Can't use NHibernate's built-in .Cacheable on non-NHibernate IQueryable, it will throw an error   
    /// </summary>
    public static class NHibernateLinqExtensionMethods
    {
        public static IQueryable<T> MakeCacheable<T>(this IQueryable<T> query)
        {
            if (query.Provider.GetType() == typeof(NHibernate.Linq.DefaultQueryProvider))
                query = query.Cacheable();

            return query;
        }


    }

}


8. Create a Class Library project. Name it RichDomainModels. Then delete Class1.cs

Then add reference to Dtos and UnitTestFriendlyDal

Add Virtuosity.Fody to the project, this AOP component saves us the need of adding virtual to every members of the domain model. All members of the domain models are required to be virtual by Nhibernate. With Virtuosity.Fody, all members are automagically made virtual for us, so we don't have to type virtual on each members anymore



Add a class file, name it PersonDomain.Member.cs, then use the folllowing code. Notice that we don't add virtual to Member's MemberId and MemberName, that's courtesy of Virtuosity.Fody


As for using static partial class (see PersonDomain on following code) instead of using namespace, we use static class to enforce schema-like way to group related domain models, namespace could be bypassed. Think of AdventureWorks database grouping related tables into their own schema


using Dtos;
using UnitTestFriendlyDal;

using System.Linq;
using System.Collections.Generic;


namespace RichDomainModels
{
    public static partial class PersonDomain
    {
        public class Member
        {
            public int    MemberId   { get; set; }
            public string MemberName { get; set; }


            public static IEnumerable<PersonDto.Member> GetMembers(IDomainAccess da)
            {
                var list = da.Query<PersonDomain.Member>().MakeCacheable().ToList()
                    .Select(x =>
                        new PersonDto.Member
                        {
                            MemberId = x.MemberId,
                            MemberName = x.MemberName
                        });

                return list;
            }
        }

    }
}

9. Add a Class Library project, name it RichDomainModelsMapping. Then delete Class1.cs

Add NHibernate and NHibernate.Caches.SysCache to the project:



Add reference to RichDomainModels

Add a class file, name it Mapper.cs, then use the following code:
using NHibernate.Cfg; // .DatabaseIntegration extension method

using System.Linq;


namespace RichDomainModelsMapping
{
    public static class Mapper
    {

        static NHibernate.ISessionFactory _sessionFactory = Mapper.BuildSessionFactory();


        // call this on production
        public static NHibernate.ISessionFactory SessionFactory
        {
            get { return _sessionFactory; }
        }


        public static NHibernate.ISessionFactory BuildSessionFactory(bool useUnitTest = false)
        {

            var mapper = new NHibernate.Mapping.ByCode.ConventionModelMapper();
            mapper.IsEntity((t, declared) => t.Namespace == "RichDomainModels");

            mapper.BeforeMapClass += mapper_BeforeMapClass;
            mapper.BeforeMapProperty += mapper_BeforeMapProperty;
            mapper.BeforeMapManyToOne += mapper_BeforeMapManyToOne;
            mapper.BeforeMapBag += mapper_BeforeMapBag;




            var cfg = new NHibernate.Cfg.Configuration();



            // .DatabaseIntegration! Y U EXTENSION METHOD?!
            cfg.DataBaseIntegration(c =>
            {
                // SQL Server
                c.Driver<NHibernate.Driver.SqlClientDriver>();
                c.Dialect<NHibernate.Dialect.MsSql2008Dialect>();
                c.ConnectionString = "Server=.;Database=PlayTheWcfServiceApp;Trusted_Connection=True";


                if (useUnitTest)
                {
                    c.LogSqlInConsole = true;
                    c.LogFormattedSql = true;
                }
            });



            System.Collections.Generic.IEnumerable<System.Type> entities =
                typeof(RichDomainModels.PersonDomain.Member).Assembly.GetExportedTypes()

                    // exclude static classes (the schema name)
                // this will still include the non-static class inside of static classes, i.e., the domain models
                    .Where(x => !(x.IsAbstract && x.IsSealed));

            NHibernate.Cfg.MappingSchema.HbmMapping mapping = mapper.CompileMappingFor(entities);


            cfg.AddMapping(mapping);

            cfg.Cache(x =>
            {
                // SysCache is not stable on unit testing
                if (!useUnitTest)
                {
                    x.Provider<NHibernate.Caches.SysCache.SysCacheProvider>();

                    // I don't know why SysCacheProvider is not stable on simultaneous unit testing,
                    // might be SysCacheProvider is just giving one session factory, so simultaneous test see each other caches
                    // This solution doesn't work: http://stackoverflow.com/questions/700043/mstest-executing-all-my-tests-simultaneously-breaks-tests-what-to-do                   
                }
                else
                {
                    // This is more stable in unit testing
                    x.Provider<NHibernate.Cache.HashtableCacheProvider>();
                }


                // http://stackoverflow.com/questions/2365234/how-does-query-caching-improves-performance-in-nhibernate

                // Need to be explicitly turned on so the .Cacheable directive on Linq will work:                   
                x.UseQueryCache = true;
            });


#if DEBUG
            if (useUnitTest)
                cfg.SetInterceptor(new NHSQLInterceptor());
#endif



            //new NHibernate.Tool.hbm2ddl.SchemaUpdate(cfg).Execute(useStdOut: false, doUpdate: true);


            //using (var file = new System.IO.FileStream(@"c:\x\ddl.txt",
            //       System.IO.FileMode.Create,
            //       System.IO.FileAccess.ReadWrite))
            //using (var sw = new System.IO.StreamWriter(file))
            //{
            //    new SchemaUpdate(cfg)
            //        .Execute(sw.Write, false);
            //}


            NHibernate.ISessionFactory sf = cfg.BuildSessionFactory();


            return sf;
        }

#if DEBUG
        class NHSQLInterceptor : NHibernate.EmptyInterceptor
        {
            // http://stackoverflow.com/questions/2134565/how-to-configure-fluent-nhibernate-to-output-queries-to-trace-or-debug-instead-o
            public override NHibernate.SqlCommand.SqlString OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
            {

                Mapper.NHibernateSQL = sql.ToString();
                return sql;
            }

        }

        public static string NHibernateSQL { get; set; }
#endif



        static void mapper_BeforeMapBag(
            NHibernate.Mapping.ByCode.IModelInspector modelInspector,
            NHibernate.Mapping.ByCode.PropertyPath member,
            NHibernate.Mapping.ByCode.IBagPropertiesMapper propertyCustomizer)
        {

            propertyCustomizer.Cache(cacheMapping => cacheMapping.Usage(NHibernate.Mapping.ByCode.CacheUsage.ReadWrite));

            propertyCustomizer.Lazy(NHibernate.Mapping.ByCode.CollectionLazy.Extra);

            /*
             * class Person
             * {
             *      IList<Hobby> Hobbies
             * }
             *
             *
             */

            string parentEntity = member.LocalMember.DeclaringType.Name; // this gets the Person
            string foreignKey = parentEntity + "Id";
            propertyCustomizer.Key(keyMapping => keyMapping.Column(foreignKey));


            // http://www.ienablemuch.com/2014/10/inverse-cascade-variations-on-nhibernate.html
            // best persistence approach: Inverse+CascadeAll
            propertyCustomizer.Inverse(true);
            propertyCustomizer.Cascade(NHibernate.Mapping.ByCode.Cascade.All);

        }



        static void mapper_BeforeMapManyToOne(
            NHibernate.Mapping.ByCode.IModelInspector modelInspector,
            NHibernate.Mapping.ByCode.PropertyPath member,
            NHibernate.Mapping.ByCode.IManyToOneMapper propertyCustomizer)
        {
            /*
            
                public class Product
                {
                    protected internal  int                                ProductId       { get; set; }

                    public              ProductionDomain.ProductCategory   ProductCategory { get; protected internal set; }
                    public              string                             ProductName     { get; protected internal set; }
                }
            
             */

            // ProductCategory property name maps to ProductCategoryId column name
            propertyCustomizer.Column(member.ToColumnName() + "Id");
        }

        static void mapper_BeforeMapProperty(
            NHibernate.Mapping.ByCode.IModelInspector modelInspector,
            NHibernate.Mapping.ByCode.PropertyPath member,
            NHibernate.Mapping.ByCode.IPropertyMapper propertyCustomizer)
        {
            //string postgresFriendlyName = member.ToColumnName().ToLowercaseNamingConvention();
            //propertyCustomizer.Column(postgresFriendlyName);    

        }

        static void mapper_BeforeMapClass(NHibernate.Mapping.ByCode.IModelInspector modelInspector,
            System.Type type,
            NHibernate.Mapping.ByCode.IClassAttributesMapper classCustomizer)
        {

            classCustomizer.Cache(cacheMapping => cacheMapping.Usage(NHibernate.Mapping.ByCode.CacheUsage.ReadWrite));

            string fullName = type.FullName; // example: RichDomainModels.ProductionDomain+Product

            string[] fullnameSplit = fullName.Split('+');

            string schemaName;
            string className;
            if (fullnameSplit.Length == 2) // The domain model is in a schema (e.g., ProductionDomain)
            {
                schemaName = fullnameSplit[0].Split('.').Last();
                schemaName = schemaName.Substring(0, schemaName.Length - "Domain".Length); // removes the suffix Domain
                className = fullnameSplit[1];
            }
            else // domain model is not inside schema
            {
                schemaName = "dbo";
                className = fullnameSplit[0].Split('.').Last();
            }

            // Last() skips the other namespace(s). 3 skips the word The


            string tableFullname = schemaName + "." + className;
            classCustomizer.Table(tableFullname);



            System.Reflection.MemberInfo mi;

            System.Reflection.MemberInfo[] memberInfos = type.GetMember(className + "Id",
                System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);


            if (memberInfos.Length == 1)
            {
                mi = memberInfos[0];
            }
            else
            {

                System.Reflection.MemberInfo[] defaultIdNames = type.GetMember("Id",
                    System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);




                if (defaultIdNames.Length == 0)
                    throw new System.Exception("Impossible. Should have an ID");
                else if (defaultIdNames.Length == 1)
                    mi = defaultIdNames[0];
                else
                    throw new System.Exception("Houston we have a problem. Why there's multiple ID property?");

            }

            classCustomizer.Id(mi,
                idMapper =>
                {
                    idMapper.Column(mi.Name);
                    idMapper.Generator(NHibernate.Mapping.ByCode.Generators.Identity);
                });


        }

    } // Mapper
}



10. On AppService

Add NHibernate to the project, and add reference to RichDomainModelsMapping and UnitTestFriendlyDal

On Global.asax.cs, add the wiring of NHibernate ISessionFactory and Domain Access Layer, line 38 and 39:
using System;
using System.Linq;
using UnitTestFriendlyDal;


namespace AppService
{
    public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {
            DependencyFactory.Container = new LightInject.ServiceContainer();


            RegisterIocs();
        }

        static void RegisterIocs()
        {

            //// Instead of manually adding each implementation:
            //DependencyFactory.Container.Register<ServiceImplementations.ProductImplementation.ProductService>(new LightInject.PerRequestLifeTime());
            //DependencyFactory.Container.Register<ServiceImplementations.PersonImplementation.MemberService>(new LightInject.PerRequestLifeTime());

            // Just do this:
            var serviceImplementations = typeof(ServiceImplementations.SampleService).Assembly.GetTypes()
                .Where(t => t.GetInterfaces()
                    .Any(i => i.GetCustomAttributes(false)
                        .Any(a => a.GetType() == typeof(System.ServiceModel.ServiceContractAttribute))));

            foreach (var item in serviceImplementations)
            {
                DependencyFactory.Container.Register(item, new LightInject.PerRequestLifeTime());
            }


            DependencyFactory.Container.Register<IDomainAccessFactory, DomainAccessFactory>(new LightInject.PerContainerLifetime());
            DependencyFactory.Container.RegisterInstance<NHibernate.ISessionFactory>(RichDomainModelsMapping.Mapper.SessionFactory);
        }
    }
}


11. On ServiceImplementations, add reference to UnitTestFriendlyDal and RichDomainModels

Then change the PersonImplementation.MemberService to this code:
using System.Collections.Generic;
using UnitTestFriendlyDal;

namespace ServiceImplementations
{

    public static partial class PersonImplementation
    {
        [System.ServiceModel.ServiceBehavior(InstanceContextMode = System.ServiceModel.InstanceContextMode.PerCall)]
        public class MemberService : ServiceContracts.PersonContract.IMemberService
        {
            IDomainAccessFactory _daf;

            public MemberService(IDomainAccessFactory daf)
            {
                _daf = daf;
            }


            IEnumerable<Dtos.PersonDto.Member> ServiceContracts.PersonContract.IMemberService.GetMembers()
            {
                using (var da = _daf.OpenDomainAccess())
                {
                    return RichDomainModels.PersonDomain.Member.GetMembers(da);
                }
            }

            string ServiceContracts.PersonContract.IMemberService.GetMembership()
            {
                return "Avengers";
            }
        }
    }
}

12. Make sure the connection string is correct on RichDomainModelsMapping

Run AppService

To test, run WcfTestClient and click Invoke on GetMembers, you shall see the following:





Complete code at: https://github.com/MichaelBuen/PlayWcfServiceApplication



Happy Coding!

WCF + LightInject + NHibernate (part 1 of 2)

On this post I'll how you how to integrate WCF + LightInject + NHibernate

The post is divided into two parts. The first post (this post) will be about WCF + LightInject only, so we can have basic infrastructure for WCF + IoC. We will tackle the domain model integration via NHibernate on the second part


These are the seven layers of a WCF Service Application:
* AppService
* Dtos
* RichDomainModels
* RichDomainModelsMapping
* ServiceContracts
* ServiceImplementations
* UnitTestFriendlyDal


AppService - WCF Service Application
    
    Using LightInject for IoC/DI:
        Wires NHibernate and UnitTestFriendlyDal
        Wires IDomainAccessFactory to ServiceImplementations
        
    Generates svc based on ServiceImplementations
    Wires svc to its implementation using AppService.LightInjectServiceHostFactory
        
        
Dtos - DTOs. References System.Runtime.Serialization only

    The data that travels across the wire
    
    
RichDomainModels - References Dtos and UnitTestFriendlyDal only


    Contains the nouns and verbs of a domain
    
    Has no virtual keyword on domain models' members even NHibernate need domain models' members to be virtual. Uses Virtuosity.Fody to automatically make the domain models' members be virtual

    ORM-agnostic, e.g., has no access to NHibernate. Has access to UnitTestFriendlyDal only
    
    Receives and returns DTOs to WCF consumer
    
    
    
RichDomainModelsMapping - References NHibernate and RichDomainModels only

    Maps relational entities to RichDomainModels
    
    
ServiceContracts - References Dtos and System.ServiceModel only

    interface for ServiceImplementations
    
ServiceImplementations - References Dtos, ServiceContract, RichDomainModels, UnitTestFriendlyDal and System.ServiceModel
        
    Just a thin layer for RichDomainModels, the main process are done on RichDomainModels
    
UnitTestFriendlyDal - Domain Access Layer. References NHibernate only

    Just a thin layer for ORM(NHibernate), NHibernate's .Query is an extension method, it can't be mocked properly, hence this interface is created
    


The part 1 of this how-to has three layers only; namely AppService, ServiceContracts and ServiceImplementations

The part 2 has the seven layers detailed above



Let's begin the part 1, this will just take us eight steps


1. Create WCF Service Application, not WCF Service Library, name it AppService



Delete IService1.svc and Service1.svc, we will move the contracts and implementation to their own assemblies




2. Add Class Library project, name it ServiceContracts

Add reference to System.ServiceModel assembly




Delete Class1.cs, then add ISampleService interface, make it public:

using System.ServiceModel;
 
namespace ServiceContracts
{
    [ServiceContract]
    public interface ISampleService
    {
        [OperationContract]
        string GetGreet();
    }
}


3. Add another Class Library project, name it ServiceImplementations, add reference to System.ServiceModel assembly too

Add reference to ServiceContracts project




Delete Class1.cs from ServiceImplementations project. Then add ISampleService implementation, name it SampleService, make it public:

using System.ServiceModel;

namespace ServiceImplementations
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class SampleService : ServiceContracts.ISampleService
    {
        string ServiceContracts.ISampleService.GetGreet()
        {
            return "Hey " + System.Guid.NewGuid();
        }
    }
}

Here's how interface and implementation should look like:





4. On AppService project, add LightInject from NuGet Packages:




Then add reference to System.ServiceModel.Activation assembly

Let's not make an apple pie from scratch, let's pattern our LightInject WCF dependency-injection from Jimmy Bogard's template for WCF+StructureMap integration. Create a new class file, name it Ioc.cs, then overwrite its content with these:

using System;
 
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Activation;
 
 
 
namespace AppService
{
    public static class DependencyFactory
    {
        public static LightInject.IServiceContainer Container { get; set; }
    }
 
    public class LightInjectInstanceProvider : IInstanceProvider
    {
        readonly Type _serviceType;
 
        public LightInjectInstanceProvider(Type serviceType)
        {
            _serviceType = serviceType;
 
        }
 
        object IInstanceProvider.GetInstance(InstanceContext instanceContext)
        {
            return Resolve();
        }
 
        object IInstanceProvider.GetInstance(InstanceContext instanceContext, Message message)
        {
            return Resolve();
        }
 
 
        object Resolve()
        {
            var instance = DependencyFactory.Container.GetInstance(_serviceType);
            return instance;
        }
 
 
        void IInstanceProvider.ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            var disposable = instance as IDisposable;
            if (disposable != null)
            {
                disposable.Dispose();
            }
        }
    }
 
    public class LightInjectServiceBehavior : IServiceBehavior
    {
        void IServiceBehavior.AddBindingParameters(
            ServiceDescription serviceDescription,
            ServiceHostBase serviceHostBase,
            System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,
            BindingParameterCollection bindingParameters)
        {
        }
 
        void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
            {
                var cd = cdb as ChannelDispatcher;
                if (cd != null)
                {
                    foreach (EndpointDispatcher ed in cd.Endpoints)
                    {
                        ed.DispatchRuntime.InstanceProvider = new LightInjectInstanceProvider(serviceDescription.ServiceType);
                    }
                }
            }
        }
 
        void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }
    }
 
    public class LightInjectServiceHost : ServiceHost
    {
 
        public LightInjectServiceHost(Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {
        }
 
        protected override void OnOpening()
        {
            Description.Behaviors.Add(new LightInjectServiceBehavior());
            base.OnOpening();
        }
 
    }
 
 
    public class LightInjectServiceHostFactory : ServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            return new LightInjectServiceHost(serviceType, baseAddresses);
        }
    }
 
}

5. Add Text Template to the project, name it ServiceHostingEnvironment.tt, then use the following:


<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.ServiceModel" #>
<#@ assembly name="$(SolutionDir)\ServiceContracts\bin\Debug\ServiceContracts.dll" #>
<#@ assembly name="$(SolutionDir)\ServiceImplementations\bin\Debug\ServiceImplementations.dll" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".config" #>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
    <serviceActivations>
<#
            var serviceImplementations = typeof(ServiceImplementations.SampleService).Assembly.GetTypes()
                .Where(t => t.GetInterfaces()
                    .Any(i => i.GetCustomAttributes(false)
                        .Any(a => a.GetType() == typeof(System.ServiceModel.ServiceContractAttribute))));
 
#>
 
<#  foreach (var item in serviceImplementations)
    { 
        
        string fullName = item.FullName; // example: ServiceImplementations.PersonImplementation+MemberService
 
        string[] fullnameSplit = fullName.Split('+');
 
        string schemaName;
        string className;
 
        string serviceFullname;
 
        if (fullnameSplit.Length == 2) // The model service is in a schema (e.g., PersonImplementation static class)
        {
            schemaName = fullnameSplit[0].Split('.').Last();
            className = fullnameSplit[1];
 
            serviceFullname = schemaName + "." + className;            
        }
        else // domain model is not inside schema
        {
            serviceFullname = item.Name;
        }
 
        
    
#>
        <add relativeAddress="<#=serviceFullname#>.svc" service="<#=item.FullName#>" factory="AppService.LightInjectServiceHostFactory"/>
<#  } #>       
    </serviceActivations>
</serviceHostingEnvironment>


Following is the output of Text Template code above. Filename output is ServiceHostingEnvironment.config:


<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
    <serviceActivations>
 
        <add relativeAddress="SampleService.svc" service="ServiceImplementations.SampleService" factory="AppService.LightInjectServiceHostFactory"/>
       
    </serviceActivations>
</serviceHostingEnvironment>

On web.config, remove the highlighted line..



..and replace it with:
<serviceHostingEnvironment configSource="ServiceHostingEnvironment.config"/>


You can ignore this warning in the project:



6. On AppService, add reference ServiceContracts and ServiceImplementations




Then add Global Application Class:



Use this in Global Application Class (Global.asax.cs):
using System;
using System.Linq;
 
 
namespace AppService
{
    public class Global : System.Web.HttpApplication
    {
 
        protected void Application_Start(object sender, EventArgs e)
        {
            DependencyFactory.Container = new LightInject.ServiceContainer();
 
 
            RegisterIocs();
        }
 
        static void RegisterIocs()
        {
 
            //// Instead of manually adding each implementation:
            //DependencyFactory.Container.Register<ServiceImplementations.ProductImplementation.ProductService>(new LightInject.PerRequestLifeTime());
            //DependencyFactory.Container.Register<ServiceImplementations.PersonImplementation.MemberService>(new LightInject.PerRequestLifeTime());
 
            // Just do this:
            var serviceImplementations = typeof(ServiceImplementations.SampleService).Assembly.GetTypes()
                .Where(t => t.GetInterfaces()
                    .Any(i => i.GetCustomAttributes(false)
                        .Any(a => a.GetType() == typeof(System.ServiceModel.ServiceContractAttribute))));
 
            foreach (var item in serviceImplementations)
            {
                DependencyFactory.Container.Register(item, new LightInject.PerRequestLifeTime());
            }
 
 
 
        }
    }
}


7. Run the AppService

You'll notice there is no .svc file(s) in the WCF Service application, it's served dynamically




To verify it exists, visit .svc on the browser: http://localhost:50549/SampleService.svc, here's the result:



8. Last step. To test, run WcfTestCLient, path is: "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\WcfTestClient.exe"



Click GetGreet, then click Invoke. You shall see the following output:




On second part of this post, I will show how to integrate NHibernate on this WCF Service application



Complete code at: https://github.com/MichaelBuen/PlayWcfServiceApplication




Happy Coding!

Monday, October 20, 2014

Registering runtime types on DryIoc

DryIoc is missing an API for registering a runtime type

Hence we can't automate this WCF registration..

_container.RegisterDelegate<TheServiceContracts.IProductService>(x => 
    (TheServiceContracts.IProductService) ServiceModelHelper.CreateService(
        typeof(TheServiceContracts.IProductService),  
        System.ServiceModel.BasicHttpBinding(), 
        new System.ServiceModel.EndpointAddress("http://localhost:1337/ProductService.svc")
    )
, Reuse.InResolutionScope);
    
_container.RegisterDelegate<TheServiceContracts.IMemberService>(x => 
    (TheServiceContracts.IMemberService) ServiceModelHelper.CreateService(
        typeof(TheServiceContracts.IMemberService), 
        new System.ServiceModel.BasicHttpBinding(), 
        new System.ServiceModel.EndpointAddress("http://localhost:1337/MemberService.svc")
    )
, Reuse.InResolutionScope);

_container.RegisterDelegate<TheServiceContracts.IOrderService>(x => 
    (TheServiceContracts.IOrderService) ServiceModelHelper.CreateService(
        typeof(TheServiceContracts.IOrderService), 
        new System.ServiceModel.BasicHttpBinding(), 
        new System.ServiceModel.EndpointAddress("http://localhost:1337/OrderService.svc")
    )
, Reuse.InResolutionScope);


..into this:

_container.Register<System.ServiceModel.BasicHttpBinding>(DryIoc.Reuse.Singleton, x => x.GetConstructor(new Type[] { }));

foreach (var contractType in typeof(TheServiceContracts.IMemberService).Assembly.GetTypes())
{
    _container.RegisterDelegate(contractType,
        r =>
        {
            var binding = r.Resolve<System.ServiceModel.BasicHttpBinding>();

            var svcAddress = "http://localhost:1337/" + contractType.Name.Substring(1) + ".svc";
            var endpointAddress = new System.ServiceModel.EndpointAddress(svcAddress);


            return ServiceModelHelper.CreateService(contractType, new System.ServiceModel.BasicHttpBinding(), endpointAddress);
        }, Reuse.InResolutionScope);

}   


And this available API in DryIoc can't be used, parameters in generics and casting must be determined at compile-time. Hence we can't pass the runtime type contractType variable as a parameter to generics and casting
_container.Register<System.ServiceModel.BasicHttpBinding>(DryIoc.Reuse.Singleton, x => x.GetConstructor(new Type[] { }));

foreach(var contractType in typeof(TheServiceContracts.IProductService).Assembly.GetTypes())
{
    var binding = new System.ServiceModel.BasicHttpBinding();
    var endpointAddress = new System.ServiceModel.EndpointAddress("http://localhost:1337/" + contractType.SubString(1) + ".svc"); // removes the first letter I
    _container.RegisterDelegate<contractType>(r => 
        {
            var binding = r.Resolve<System.ServiceModel.BasicHttpBinding>();

            var svcAddress = "http://localhost:1337/" + contractType.Name.Substring(1) + ".svc";
            var address = new System.ServiceModel.EndpointAddress(svcAddress);
            
            return (contractType) ServiceModelHelper.CreateService(contractType, binding, endpointAddress)
        }, Reuse.InResolutionScope);
}


To fix that, we need to simulate RegisterDelegate<T> during runtime


First step, we should design how the additional API should look like. It should feel as close as possible to the convention of the original API:
_container.RegisterDelegate<TheServiceContracts.IOrderService>(x => 
    (TheServiceContracts.IOrderService) ServiceModelHelper.CreateService(
        typeof(TheServiceContracts.IOrderService), 
        new System.ServiceModel.BasicHttpBinding(), 
        new System.ServiceModel.EndpointAddress("http://localhost:1337/OrderService.svc")
    )
, Reuse.InResolutionScope);


The additional API should be like this:
_container.RegisterDelegate(contractType, x => 
    ServiceModelHelper.CreateService(
        contractType, 
        new System.ServiceModel.BasicHttpBinding(), 
        new System.ServiceModel.EndpointAddress("http://localhost:1337/OrderService.svc")
    )
, Reuse.InResolutionScope);


Then our API should be an extension method of the DryIoc.IResolver:
public static class ResolverExtensionHelper
{
    public static void RegisterDelegate(this DryIoc.IResolver resolver,
         Type contractType,
         Func<DryIoc.IResolver, object> objectConstructor,
         DryIoc.IReuse reuse = null, DryIoc.FactorySetup setup = null, string named = null)
    {
        ResolverBuilder.Build(container, contractType, objectConstructor, reuse, setup, named);
    }
}


You maybe wondering why we need to use two classes, ResolverBuilder and ResolverExtensionHelper, why not just do everything in ResolverExtensionHelper?

We need to have the following code, we need to cast the objects generated(e.g., WCF proxy objects) in a delegate back to its dependency-injected type(e.g., is an interface) during runtime, which is not possible with the original RegisterDelegate<contractTypeHere>. So we need to have a mechanism that can cast runtime types:
TService ConstructObjectThenCast<TService>(IResolver resolver)
{
    var svc = (TService) this._objectConstructorDelegate(resolver);
    return svc;
}


And we cannot add lambda parameter (any parameter for that matter) on that method:
TService ConstructObjectThenCast<TService>(IResolver resolver, 
    Func<IResolver, object> objectConstructorDelegate)
{
    var svc = (TService) objectConstructorDelegate(resolver);
    return svc;
}


Adding that parameter would cause runtime error when doing a reflection call on original RegisterDelegate method. ConstructObjectThenCast method is the substitute method for the original RegisterDelegate's lambda parameter
Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.


Looking at the original RegisterDelegate method signature shows why:
public static void RegisterDelegate<TService>(
     this IRegistrator registrator, Func<IResolver, TService> lambda, 
     IReuse reuse = null, FactorySetup setup = null, string named = null);

The lambda delegate we want to hook on has one parameter only, IResolver only. Hence if Func<IResolver, object> objectConstructorDelegate is added to parameters of ConstructObjectThenCast, it results to the error above


However, since we cannot add another parameter on the substitute method(ConstructObjectThenCast) to the lambda, the only way to pass the user-defined lambda to the original RegisterDelegate is to create a class instance, which is not possible on static class, hence the need for two classes. So when the original RegisterDelegate method calls our substitute method for original RegisterDelegate's lambda, we can still call the user-defined lambda in turn. The user-defined lambda is stored in a non-static class, it's a field on that class: Func<DryIoc.IResolver,object> _objectConstructorDelegate;


ConstructObjectThenCast is the method that will cast an object to its dependency-injected type(e.g., in an interface), that's the method we will substitute to RegisterDelegate's lambda parameter. In order for that substitute method to be able to call our user-defined lambda, we will call the user-defined lamba from the class instance. this._objectConstructorDelegate is where the user-defined lambda is stored
TService ConstructObjectThenCast<TService>(IResolver resolver)
{
    var svc = (TService) this._objectConstructorDelegate(resolver);
    return svc;
}




Here's the complete code of the additional API. This also shows how to use DryIoc on ASP.NET MVC and WCF:
using System;
using System.Linq;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;

using DryIoc;
using AspNetMvcUseWcf.Controllers;

namespace AspNetMvcUseWcf
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            
            ControllerBuilder.Current.SetControllerFactory(new DryIocDependencyResolver());   
        }
    }

    class DryIocDependencyResolver : System.Web.Mvc.DefaultControllerFactory
    {
        DryIoc.Container _container;

        public DryIocDependencyResolver()
        {
            _container = new DryIoc.Container();
            RegisterTheIocs();
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            System.Web.Mvc.IController ic = controllerType == null
                ? null
                : (System.Web.Mvc.IController)_container.Resolve(controllerType);

            // _container.ResolvePropertiesAndFields(ic);  // uncomment this if you want to use DI on controller's properties

            return ic;
        }

        void RegisterTheIocs()
        {
            RegisterMvcControllers();
            RegisterWcfServices();
        }

        void RegisterWcfServices()
        {
            _container.Register<System.ServiceModel.BasicHttpBinding>(DryIoc.Reuse.Singleton, x => x.GetConstructor(new Type[] { }));


            foreach (var contractType in typeof(TheServiceContracts.IMemberService).Assembly.GetTypes())
            {                
                _container.RegisterDelegate(contractType,
                    r =>
                    {
                        var binding = r.Resolve<System.ServiceModel.BasicHttpBinding>();

                        var svcAddress = "http://localhost:1337/" + contractType.Name.Substring(1) + ".svc";
                        var endpointAddress = new System.ServiceModel.EndpointAddress(svcAddress);


                        return ServiceModelHelper.CreateService(contractType, binding, endpointAddress);
                    }, Reuse.InResolutionScope);

            }
        }

        void RegisterMvcControllers()
        {
            System.Reflection.Assembly assembly = typeof(HomeController).Assembly;

            foreach (var controller in assembly.GetTypes().Where(t => typeof(Controller).IsAssignableFrom(t)))
            {
                _container.Register(controller, DryIoc.Reuse.InResolutionScope);
            }
        }


    } // class DryIocDependencyResolver






    public static class ResolverExtensionHelper
    {
        public static void RegisterDelegate(this DryIoc.IResolver container,
             Type contractType, Func<DryIoc.IResolver, object> objectConstructorDelegate,
             DryIoc.IReuse reuse = null, DryIoc.FactorySetup setup = null, string named = null)
        {
            ResolverBuilder.Build(container, contractType, objectConstructorDelegate, reuse, setup, named);
        }
    }

    public class ResolverBuilder
    {
        
        Func<DryIoc.IResolver,object> _objectConstructorDelegate;


        public static void Build(DryIoc.IResolver container, Type contractType, Func<IResolver, object> objectConstructor, IReuse reuse, FactorySetup setup, string named)
        {
            new ResolverBuilder(container, contractType, objectConstructor, reuse, setup, named);
        }

        ResolverBuilder(
            DryIoc.IResolver resolver, Type contractType, Func<DryIoc.IResolver, object> objectConstructorDelegate,
            DryIoc.IReuse reuse = null, DryIoc.FactorySetup setup = null, string named = null
            )
        {
            _objectConstructorDelegate = objectConstructorDelegate;

            Delegate lambda;
            {
                System.Reflection.MethodInfo constructObjectThenCastMethodInfo = Create_ConstructObjectThenCast_MethodInfo_For_ContractType(contractType);
                lambda = Create_ConstructObjectThenCast_Lambda_For_ContractType(this, constructObjectThenCastMethodInfo, contractType);
            }


            // Invoke the original RegisterDelegate<TService>            
            {
                // public static void RegisterDelegate<TService>(this DryIoc.IRegistrator registrator, Func<DryIoc.IResolver, TService> lambda, 
                //                                                     DryIoc.IReuse reuse = null, DryIoc.FactorySetup setup = null, string named = null);

                // obj is null, means RegisterDelegate is a static method
                // resolver comes from _container
                // contractType is the TService in the original _container.RegisterDelegate<TService>
                System.Reflection.MethodInfo registerDelegateMethodInfo = typeof(DryIoc.Registrator).GetMethod("RegisterDelegate").MakeGenericMethod(contractType);
                registerDelegateMethodInfo.Invoke(/*obj*/ null, new object[] { resolver, lambda, reuse, setup, named });
            }
        }

        static System.Reflection.MethodInfo Create_ConstructObjectThenCast_MethodInfo_For_ContractType(Type contractType)
        {

            System.Reflection.MethodInfo constructObjectThenCastMethodInfo =
                // typeof(IResolver) is the resolver parameter of: TService ConstructObjectThenCast<TService>(IResolver resolver)
               typeof(ResolverBuilder).GetMethod(
                   name: "ConstructObjectThenCast",
                   types: new[] { typeof(IResolver) },
                   bindingAttr: System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance,
                   binder: null,
                   modifiers: null
                   )
                // contractType is the TService of ConstructObjectThenCast<TService>
               .MakeGenericMethod(contractType);
            return constructObjectThenCastMethodInfo;
        }


        // Create a lambda out of this class method: TService ConstructObjectThenCast<TService>(IResolver resolver)
        static Delegate Create_ConstructObjectThenCast_Lambda_For_ContractType(ResolverBuilder resolverBuilder,  System.Reflection.MethodInfo constructObjectThenCastMethodInfo, Type contractType)
        {
            // Create a Func<IResolver,TService> delegate type
            // This will be used as the lambda parameter on the original RegisterDelegate static method.                                          
            Type lambdaDelegateType = typeof(Func<,>).MakeGenericType(typeof(DryIoc.IResolver), contractType);
            // The above corresponds to this example: Func<IResolver, TheServiceContracts.IProductService>


            // Cannot use Activator.CreateInstance on delegate type as delegates don't have constructor
            // ConstructObjectThenCast method has a signature same as lambdaDelegateType 
            // Create a lambda out of ConstructObjectThenCast method info
            Delegate lambda = Delegate.CreateDelegate(lambdaDelegateType, resolverBuilder, constructObjectThenCastMethodInfo);

            return lambda;
        }

     
        TService ConstructObjectThenCast<TService>(IResolver resolver)
        {
            var svc = (TService) this._objectConstructorDelegate(resolver);
            return svc;
        }


    }// class ResolverBuilder


    public static class ServiceModelHelper
    {
        public static object CreateService(
            Type contractType,
            System.ServiceModel.BasicHttpBinding basicHttpBinding,
            System.ServiceModel.EndpointAddress endpointAddress
            )
        {
            var binding = new System.ServiceModel.BasicHttpBinding();
            //Get the address of the service from configuration or some other mechanism - Not shown here

            //dynamic factory generation
            object factory =
                Activator.CreateInstance(typeof(System.ServiceModel.ChannelFactory<>)
                .MakeGenericType(contractType), binding, endpointAddress);

            System.Reflection.MethodInfo createFactory = factory.GetType().GetMethod("CreateChannel", new Type[] { });
            //now dynamic proxy generation using reflection
            return createFactory.Invoke(factory, null);
        }
    }
    
}//namespace



Happy Coding!

Wednesday, March 13, 2013

Lest we forget, WCF is Windows Communication Foundation, not Web Communication Foundation. nod nod wink wink

Suffice to say, WCF was not created with web app in mind, try to interoperate WCF with a web front-end, I think (sorry I didn't do my homework) it's doable, but I would hazard a guess that a web-facing API fashioned out of WCF service will feel very byzantine, e.g. what is the DataMember attribute's business to a jQuery-driven or AngularJS-driven web app?



Yesterday, we have encountered a WCF limitation where we cannot transport a dictionary object from WCF to ASP.NET MVC, then from MVC we need to transport the dictionary object as JSON object. We are using ASP.NET MVC controller as a service for delivering JSON to a web front-end that needed the data, as ASP.NET MVC has a more natural affinity to web and JSON than a WCF to web and JSON is.



In a nutshell, every web application is really just one complex marshaling problem, we marshal the data from DB through WCF, then through ASP.NET MVC, then through JSON, then to the DOM, and then we do it all again in reverse order. If only the first gateway(e.g. WCF) can marshal (via JSON) our data directly to a web front-end, we won't even need ASP.NET MVC to carry out the last step, we can just directly return JSON data right there on the first gateway. Today we will explore that option, albeit without WCF nor ASP.NET MVC.





Here's the summary of steps

1. Create a web server. We can use anything here, for simplicity sake we will just use ASP.NET Web application

2. Create a ServiceStack. We replace both WCF and ASP.NET MVC JSON server with ServiceStack

3. From the web front-end(jQuery or AngularJS) use the JSON from ServiceStack

4. Profit!





Step 1. Create a new ASP.NET Web Application

Step 2. Get the "Starter ASP.NET Website Template - ServiceStack at /" from NuGet



Edit WebServiceExample.cs to this:

public Person Any(Hello request)
{
    return new Person { Lastname = "Torvalds", Firstname = "Linus", Age = 42 };
}


Note that you don't even need to explicitly serialize the object to Json on the service, here's a sample output via this url: http://localhost:62015/hello?format=json



Note that the above code and all the code that follows have this output:

{"Lastname":"Torvalds","Firstname":"Linus","Age":42}

You can even specify object as the returned type, and everything still works as expected:

public object Any(Hello request)
{
    return new Person { Lastname = "Torvalds", Firstname = "Linus", Age = 42 };
}


So what's the implication of being able to return just an object type? we can use anonymous type!

public object Any(Hello request)
{
    return new {Lastname = "Torvalds", Firstname = "Linus", Age = 43};
}


How about the humble dictionary that WCF is refusing to marshal? It Just Works on ServiceStack

public object Any(Hello request)
{            
    var d = new Dictionary<string, object>();
    d.Add("Lastname", "Torvalds");
    d.Add("Firstname", "Linus");
    d.Add("Age", 44);
    return d;
}


How about the dynamic object we originally intended to use on WCF which WCF is not capable of marshalling? Keep them coming, It Just Works on ServiceStack!

public object Any(Hello request)
{
        dynamic person = new System.Dynamic.ExpandoObject();
 
        person.Lastname = "Torvalds";
        person.Firstname = "Linus";
        person.Age = 45;
 
        return person;
}


And the kind of marshalling dynamic data we originally (dictionary is just a plan B, which doesn't work on WCF) intend to do on WCF which WCF is not capable of? Marshalling dynamic is perfectly fine on ServiceStack. FTW!

public object Any(Hello request)
{
        dynamic person = new System.Dynamic.ExpandoObject();
 
        person.Lastname = "Torvalds";
        person.Firstname = "Linus";
        person.Age = 46;
 
        var dictPerson = (IDictionary<string, object>)person;
 
        var r = new Random();
        for (char c = 'A'; c <= 'E'; c++)
        {
               dictPerson[c.ToString()] = r.Next(7);
        }
 
        return person;
}


Sample output:

{"Lastname":"Torvalds","Firstname":"Linus","Age":46,"A":5,"B":6,"C":2,"D":3,"E":5}


Step 3. From the web front-end(jQuery or AngularJS) use the JSON from ServiceStack. Left as an exercise to the reader

Step 4. Profit! Left as an exercise to the reader


ServiceStack is the best SOA framework your web front-end can communicate to


That's it folks, everything just works the way we intend things to!


Happy Coding! ツ


Saturday, June 16, 2012

Frictionless object graph n-tier using ServiceStack, DitTO and ToTheEfnhX

The subtitle is: It works on both Entity Framework and NHibernate!


Ok, there's too much plugging there :-) Though definitely you should be using ServiceStack and use whatever DTO mapper and repository pattern that suits your need. I highly recommend DitTO POCO-DTO mapper and ToTheEfnhX repository pattern ;-)


First of all, we start with the foundation. Do we really need to transport the domain objects as it is? The concensus is not. Whatever the entities of the business is, it should not spill to the consuming side(e.g. jQuery, Winforms, WPF, Silverlight, etc).


Then the second thing we need to consider, take the following classic example of Order and OrderLines object relationship. The OrderLine has a reference on its parent object, a circular reference.


public class Order
{
    public virtual int OrderId { get; set; }

    public virtual Customer Customer { get; set; }
    public virtual string OrderDescription { get; set; }
    public virtual DateTime OrderDate { get; set; }
   
    public virtual IList<OrderLine> OrderLines { get; set; }

    public virtual byte[] RowVersion { get; set; }
}

public class OrderLine
{
    public virtual Order Order { get; set; }

    public virtual int OrderLineId { get; set; }

    public virtual Product Product { get; set; }
    public virtual int Quantity { get; set; }
    public virtual decimal Price { get; set; }
    public virtual decimal Amount { get; set; }
    public virtual Product Freebie { get; set; }

    public virtual IList<Comment> Comments { get; set; }
}

public class Comment
{
    public virtual OrderLine OrderLine { get; set; }

    public virtual int CommentId { get; set; }
    public virtual string TheComment { get; set; }
}



Some services can not transport the Order when its OrderLine has a circular reference to the same Order object. Though you can solve it on services such as WCF by putting [DataContract(IsReference=true)] on your Order class, but recently I came to the conclusion that this property need not be present on the other side of the wire(client), especially on n-tier app. Object references (or Independent Association in Entity Framework's parlance) are just an ORM artifacts for persisting objects.



With that in mind, we can just return DTOs devoid of circular reference.


public class OrderDto
{
    public int OrderId { get; set; }

    public int CustomerId { get; set; }
    public string OrderDescription { get; set; }
    public DateTime? OrderDate { get; set; }
    
    public List<OrderLineDto> OrderLines { get; set; }

    public byte[] RowVersion { get; set; }
}


public class OrderLineDto
{
    public int OrderLineId { get; set; }

    public int ProductoId { get; set; }
    public string ProductDescription { get; set; }

    public int FreebieId { get; set; }

    public int Quantity { get; set; }
    public decimal Price { get; set; }
    public decimal Amount { get; set; }

    public List<CommentDto> Koments { get; set; }
}



public class CommentDto
{
    public int CommentId { get; set; }
    public string TheComment { get; set; }
}


Besides manually mapping all the corresponding selected values(ProductoId, yes it's ProductoId, later we will demonstrate how to override mapping with DitTO), we still have one hurdle to tackle before we can map back the DTO to its POCO counterpart. How can we map the OrderLine back to the parent?


If we will do the mapping manually, we shall do this:

public override object OnPost(OrderRequest request)
{
    Order o = new Order
    {
        OrderId = request.OrderDto.OrderId,
        Customer = LoadStub(request.OrderDto.CustomerId),
        OrderDescription = request.OrderDto.CustomerId,
        OrderDate = request.OrderDto.OrderDate,
        RowVersion = request.OrderDto.RowVersion
        OrderLines = new List<OrderLine>()
    };
    
    foreach(OrderLineDto olx in request.OrderDto.OrderLines)
    {
        var ol = new OrderLine
        {
            Order = o, // this link the OrderLine to its parent Order. So an ORM won't have a problem persisting the object
            
            Product = LoadStub(olx.ProductoId),
            Freebie = LoadStub(olx.FreebieId),            
            Quantity = olx.Quantity,
            Price = olx.Price,
            Amount = olx.Amount
        };
        
        o.OrderLines.Add(ol);
    }
    
    Repository.SaveGraph(o);        
}


We aren't expecting humans to type those things over and over, right? Besides, we also need to map the Comments on each OrderLine. In our model, an OrderLine is one-to-many with Comments, that's a contrived example, but will do for the purpose of demonstrating why we need a decent DTO mapper that can map the POCO to DTO and vice versa automatically, a mapping library that can map object graph birectionally.

Linking the object reference between collection and its parent is a very involved code too. Manual mapping is prone to error if you forgot what properties needed be assigned.


With this in mind, I've made an object-mapper that can spare the devs on the drudgeries of individually assigning properties and linking the child objects to its parent. And this DTO mapper works bidirectionally too, it can map back the DTO to POCO. Let's cut to the chase and see how the mapping looks like:


class OrderMapping : DtoPocoMap<OrderDto, Order>
{
    public override void Mapping()
    {
        MapKey(d => d.CustomerId, s => s.Customer.CustomerId);
                    
        MapList(d => d.OrderLines, s => s.OrderLines, r => r.Order);
    }
}

class OrderLineMapping : DtoPocoMap<OrderLineDto, OrderLine>
{
    public override void Mapping()
    {
        MapKey(d => d.ProductoId, s => s.Product.ProductId);
        Map(d => d.ProductDescription, s => s.Product.ProductDescription);
        MapKey(d => d.FreebieId, s => s.Freebie.ProductId);

        MapList(d => d.Koments, s => s.Comments, r => r.OrderLine);
    }
}


You might notice that we didn't include Price,Qty and Amount on OrderLineMapping. If the names matches, there's no need for us to override the mapping, it's automatically done when the name matches. Likewise on Order class, the OrderId, OrderDate and OrderDescription was not mapped manually. Only the CustomerId is mapped manually, as the CustomerId is not directly accessible from the Order object. Likewise with Comments class, there's no need to override mapping for the whole CommentDto class as all its properties' names exactly matches its corresponding POCO. The only explicit mappings needed be done are:

1) when the names doesn't matches

2) when the scalar value from POCO side is in another object reference, e.g. MapKey(x => x.CustomerId, y => y.Customer.CustomerId);

3) when you have a list to map, e.g. MapList(x => x.OrderLines, x => x.OrderLines, z => z.Order);


Explicit mapping of list is mandatory, as there are potential problems if the mapper try to infer the object reference that should be matched from child objects against the parent object. Explicit list mapping indicates what maps the child objects to their parent object, that indicator is the third parameter on MapList. e.g. MapList(x => x.OrderLines, x => x.OrderLines, z => z.Order);


To deliver a frictionless n-tier code, the mapping between POCO to DTO should be a simple undertaking, and a one-liner code at that. An example of getting the object graph:

public override object OnGet(CustomerRequest request)
{
    return new CustomerRequestResponse
    {
        CustomerDto = Ienablemuch.DitTO.Mapper.ToDto<Customer, CustomerDto>(Repository.Get(request.Id))
    };
}


An example of receiving back an object graph and mapping it back to POCO:

public override object OnPost(OrderRequest request)
{
    Order poco = Ienablemuch.DitTO.Mapper.ToPoco<OrderDto, Order>(request.OrderDto);
    // NHibernate don't have problem with stub references. Entity Framework stub object needed be wired to an object already in identity maps
    // http://en.wikipedia.org/wiki/Identity_map_pattern            
    Repository.AssignStub(poco);

    Repository.SaveGraph(poco);
    return new OrderRequestResponse { OrderDto = new OrderDto { OrderId = poco.OrderId, RowVersion = poco.RowVersion } };
}

Saving back the object is supposedly just a two-liner code, but there's something fundamentally different how Entity Framework's stub object operates. When an stub object identity matches an object that has the same id in Entity Framework's object state tracker, stub objects needed to reference those live objects instead, this is called Identity Map Pattern. So for that EF's eccentricity, we have to assign live object for stubs that matches the identity map before calling the Repository.SaveGraph, for this we use the AssignStub extension method. By the way, it's ok to keep the AssignStub code even we are using NHibernate, AssignStub will not do anything if we are using NHibernate, NHibernate automatically maps the stub objects to its identity maps.

So for saving objects, we have three steps.

First, we map back the DTO to a POCO object:

Order poco = Ienablemuch.DitTO.Mapper.ToPoco<OrderDto, Order>(request.OrderDto);

Second, we invoke AssignStub:

Repository.AssignStub(poco);

Then finally, we call the SaveGraph:

Repository.SaveGraph(poco);

Now on the client-side of things. We invoke ServiceStack JsonServiceClient's Post method when saving.

void uxSave_Click(object sender, EventArgs e)
{

    OrderRequestResponse r = null;
    try
    {
        OrderDto dto = (OrderDto)bdsOrder.Current;                               
        r = Client.Post<OrderRequestResponse>(address + "/order_request", new OrderRequest { OrderDto = dto });

        dto.OrderId = r.OrderDto.OrderId;
        dto.RowVersion = r.OrderDto.RowVersion;

        bdsOrder.ResetItem(0);
        MessageBox.Show("Saved.");
    }    
    catch (Exception ex)
    {
        if (r != null)
        {
            MessageBox.Show(r.ResponseStatus.StackTrace);
        }
        MessageBox.Show(ex.Message);
    }    
}


When opening, we use ServiceStack JsonServiceClient's Get method

private void uxOpen_Click(object sender, EventArgs e)
{
    try
    {
        int id = int.Parse(uxLoadId.Text);

        OpenOrder(id);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

void OpenOrder(int id)
{
    var r = Client.Get<OrderRequestResponse>(address + "order_request/" + id);
    bdsOrder.DataSource = r.OrderDto;
}


On infrastructure side of things, we put these routings on ServiceStack AppHost's Configure method:


Routes
    .Add<OrderRequest>("/order_request")
    .Add<OrderRequest>("/order_request/{Id}")
    .Add<OrderRequest>("/order_request/{Id}/{RowVersion}");

Routes
    .Add<CustomerRequest>("/customer_request")
    .Add<CustomerRequest>("/customer_request/{Id}");

Routes
    .Add<ProductRequest>("/product_request")
    .Add<ProductRequest>("/product_request/{Id}");


We also put this one-time initialization of DTO and POCO mappings:

Ienablemuch.DitTO.Mapper.FromAssemblyOf<TheServiceStack.DtoPocoMappings.OrderMapping>();


Then on dependency-injected repository objects, setup the following:


#if UseEF

    container.Register<System.Data.Entity.DbContext>(x => new TheServiceStack.DbMappings.EfDbMapper(connectionString)).ReusedWithin(Funq.ReuseScope.Container);

    
    container.Register<Ienablemuch.ToTheEfnhX.IRepository<TheEntities.Poco.Order>>(c =>
        new Ienablemuch.ToTheEfnhX.EntityFramework.Repository<TheEntities.Poco.Order>(
            c.Resolve<System.Data.Entity.DbContext>())).ReusedWithin(Funq.ReuseScope.None);
            
    container.Register<Ienablemuch.ToTheEfnhX.IRepository<TheEntities.Poco.Product>>(c =>
        new Ienablemuch.ToTheEfnhX.EntityFramework.Repository<TheEntities.Poco.Product>(
            c.Resolve<System.Data.Entity.DbContext>())).ReusedWithin(Funq.ReuseScope.None);
        
    container.Register<Ienablemuch.ToTheEfnhX.IRepository<TheEntities.Poco.Customer>>(c =>
        new Ienablemuch.ToTheEfnhX.EntityFramework.Repository<TheEntities.Poco.Customer>(
            c.Resolve<System.Data.Entity.DbContext>())).ReusedWithin(Funq.ReuseScope.None);

#else

    container.Register<NHibernate.ISession>(x => 
        TheServiceStack.DbMappings.NHMapping.GetSession(connectionString)).ReusedWithin(Funq.ReuseScope.Container);
    
    
    container.Register<Ienablemuch.ToTheEfnhX.IRepository<TheEntities.Poco.Order>>(c =>
        new Ienablemuch.ToTheEfnhX.NHibernate.Repository<TheEntities.Poco.Order>(
            c.Resolve<NHibernate.ISession>())).ReusedWithin(Funq.ReuseScope.None);            
            
    container.Register<Ienablemuch.ToTheEfnhX.IRepository<TheEntities.Poco.Product>>(c =>
        new Ienablemuch.ToTheEfnhX.NHibernate.Repository<TheEntities.Poco.Product>(
            c.Resolve<NHibernate.ISession>())).ReusedWithin(Funq.ReuseScope.None);
        
    container.Register<Ienablemuch.ToTheEfnhX.IRepository<TheEntities.Poco.Customer>>(c =>
        new Ienablemuch.ToTheEfnhX.NHibernate.Repository<TheEntities.Poco.Customer>(
            c.Resolve<NHibernate.ISession>())).ReusedWithin(Funq.ReuseScope.None);
#endif



We don't use singleton, there are funny things that are happening with singleton objects, though unfortunately is the Funq dependency injection's default object instance injection mechanism. We should get a new object everytime for repositories, for this we need to pass a parameter of Funq.ReuseScope.None to ReusedWithin. For ORM connection(Entity Framework's DbContext and NHibernate's ISession) we use Funq.ReuseScope.Container, this is not a singleton, you still get new ORM connection on every invocation of the service. If you use Funq.ReuseScope.None on ORM connection too, not only you get a new connection every service invocation, each repository will have their own ORM connection; however, that has a problem, that prevents joining queries manually from the different repositories. An example:


public class OrderRequestService : ServiceStack.ServiceInterface.RestServiceBase<OrderRequest>
{

    // dependency injected properties    
    public Ienablemuch.ToTheEfnhX.IRepository<Order> RepositoryO { get; set; }
    public Ienablemuch.ToTheEfnhX.IRepository<OrderLine> RepositoryOL { get; set; }
    

    OrderRequestResponse Search(OrderRequest request)
    {
        var query = RepositoryO.All;

        if (request.OrderDto.CustomerId != 0)
        {
            query = query.Where(x => x.Customer.CustomerId == request.OrderDto.CustomerId);
        }

        if (request.OrderDto.OrderDate != null)
        {
            query = query.Where(x => x.OrderDate >= request.OrderDto.OrderDate);
        }

        if (!string.IsNullOrEmpty(request.OrderDto.OrderDescription))
        {
            query = query.Where(x => x.OrderDescription.Contains(request.OrderDto.OrderDescription));
        }

        if (request.OrderDto.OrderLines.Any())
        {
            foreach (OrderLineDto o in request.OrderDto.OrderLines)
            {

                IQueryable<OrderLine> olFilter = RepositoryOL.All;

                if (o.ProductoId != 0)
                {
                    int pId = o.ProductoId;
                    olFilter = olFilter.Where(x => x.Product.ProductId == pId);
                }

                if (o.Quantity != 0)
                {
                    int qty = o.Quantity;
                    olFilter = olFilter.Where(x => x.Quantity == qty);
                }

                
                // This will have a runtime error if we use Funq.ReuseScope.None on ORM's connection. As the two IQueryable don't share the same connection.
                // To make the dependency injected repositories share the same connection, their ORM connection should come from one object reference only, 
                // for this we use Funq.ReuseScope.Container for the ORM's connection.
                query = query.Where(x => olFilter.Any(y => y.Order.OrderId == x.OrderId));
                
            }//foreach
        }//if
        
        ...
    }
}



So in order to allow sharing of connection between repositories, use Funq.ReuseScope.Container. Again, that is not singleton.


So that's all, to paraphrase jQuery mantra: "write less, do more."


The complete code can be found here: https://github.com/MichaelBuen/DemoServiceStackNtier

Another point of interest, ServiceStack fully advocates Martin Fowler DTO pattern: https://github.com/ServiceStack/ServiceStack/blob/master/README.md


If you don't recognize the importance of having a DTO pattern, consider the following flexible search screenshot. Yes that is a search functionality, the data entry doubles as a search filter (ala-Filemaker):





How would you name that search mechanism in WCF(which is an anti-pattern of DTO), with WCF you tend to name your search like this:

Order[] GetAllOrders();
Order GetOrderById(int id);
Order[] GetOrdersByCustomerId(int customerId);
Order[] GetOrdersByOrderDescription(string orderDescription);
Order[] GetOrdersByOrderDate(DateTime orderDate);
Order[] GetOrdersByCustomerAndOrderDate(int customerId, DateTime orderDate);
Order[] GetOrdersByCustomerAndOrderDescription(int customerId, string orderDescription, DateTime orderDate);
Order[] GetOrdersByCustomerAndOrderDescriptionAndOrderDate(int customerId, string orderDescription, DateTime orderDate);
Order[] GetOrdersByCustomerAndOrderDescriptionAndOrderDateAndProductAndQty(int customerId, string orderDescription, DateTime orderDate);
Order[] GetOrdersByCustomerAndOrderDateAndProductAndQty(int customerId, DateTime orderDate, int productId, int Qty);
Order[] GetOrdersByProductAndQty(int productId, int qty);
Order[] GetOrdersByQty(int qty);
Order[] GetOrdersByProduct(int productId);


Heaven forbid what WCF method name and parameters a dev will come up with the search screenshot above; and on top of that, the user might combine other filter(s) (he/she might add any or all of the Order Description filter, Date, Product, Quantity, Prduct+Quantity etc) on that search functionality, what method name a dev will come up that accepts different combinations of filters on WCF?


Life is short, use DTO, use a service stack that embraces it fully, use ServiceStack!


Source codes:


Here's a primer on setting up ServiceStack infrastructure under two minutes: http://www.youtube.com/watch?v=a3HzSvUCbeI

Complete demo code: https://github.com/MichaelBuen/DemoServiceStackNtier

Wednesday, May 30, 2012

Troubleshooting: There was no endpoint listening.

If you received this kind of error:


There was no endpoint listening at http://localhost:9384/Forumer.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.



Chances are your WCF is using a different port number (or it is Auto-assigned), right click your WCF project, then look at the Web tab, then select the Specific port, put a number there, e.g. 9384

Saturday, July 2, 2011

WCF error. The underlying connection was closed: The connection was closed unexpectedly.

To trace the root of cause of that generic error more easily, put this in your WCF's web.config

<system.diagnostics>
  <sources>
    <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
      <listeners>
        <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData= "c:\_Misc\traces.svclog" />
      </listeners>
    </source>
  </sources>
</system.diagnostics>

Then double-click the traces.svclog in C:\_Misc folder

Friday, May 27, 2011

WCF: "serviceActivations could not be found"

If you received this kind of error:

The type 'WcfService1.Service1', provided as the Service attribute value in the ServiceHost directive, or provided in the configuration element system.serviceModel/serviceHostingEnvironment/serviceActivations could not be found.

It's trying to find Wcf1Service1.Service1. To change to your current service name, example:

public class TestService : IService1
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }
    }

...from Solution Explorer, right-click Service1.svc, choose Open With..., select XML (Text) Editor, click OK, change this...

<%@ ServiceHost Language="C#" Debug="true" Service="WcfService1.Service1" CodeBehind="Service1.svc.cs" %>


...To:
<%@ ServiceHost Language="C#" Debug="true" Service="WcfService1.TestService" CodeBehind="Service1.svc.cs" %>

Thursday, January 13, 2011

NHibernate saves your whole object graph even on cross-process scenario. NHibernate+WCF

One of the nicest thing with NHibernate is that it let you concentrate on your problem domain instead of doing low-level plumbing of saving your table(s)


And this is your ever-efficient and succint data component in action:


public int SaveQuestion(Question q)
{
 using (var s = SessionFactory.OpenSession())
 using (var tx = s.BeginTransaction())
 {                
  var qx = (Question)s.Merge(q);
  tx.Commit();
  return qx.QuestionId;
 }
}


And that will work even your data relationships are two or more level deep and still maintain its five-lines-ness regardless of how deep the rabbit hole goes table relationships are. Here are the entities and values, visualize Stackoverflow's database design, a question has many comments, a question has many answers, a given answer has many comments:




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TheEntities;
using System.Runtime.Serialization;

namespace TheEntities
{

    [DataContract(IsReference=true)] 
    [KnownType(typeof(Question))]
    [KnownType(typeof(Answer))]
    [KnownType(typeof(QuestionComment))]
    public class Question
    {
        [DataMember] public virtual int QuestionId { get; set; }

        [DataMember] public virtual string TheQuestion { get; set; }
        [DataMember] public virtual string Poster { get; set; }

        [DataMember] public virtual IList<QuestionComment> Comments { get; set; }
        [DataMember] public virtual IList<Answer> Answers { get; set; }
    }

    [DataContract]    
    [KnownType(typeof(Question))]
    [KnownType(typeof(QuestionComment))]
    public class QuestionComment
    {

        [DataMember] public virtual Question Question { get; set; }

        [DataMember] public virtual int QuestionCommentId { get; set; }

        [DataMember] public virtual string TheQuestionComment { get; set; }
        [DataMember] public virtual string Poster { get; set; }
    }


    [DataContract(IsReference=true)]
    [KnownType(typeof(Question))]
    [KnownType(typeof(Answer))]
    [KnownType(typeof(AnswerComment))]
    public class Answer
    {
        [DataMember] public virtual Question Question { get; set; }

        [DataMember] public virtual int AnswerId { get; set; }

        [DataMember] public virtual string TheAnswer { get; set; }
        [DataMember] public virtual string Poster { get; set; }

        [DataMember] public virtual IList<AnswerComment> Comments { get; set; }
    }


    [DataContract]
    [KnownType(typeof(Answer))]
    [KnownType(typeof(AnswerComment))]
    public class AnswerComment
    {
        [DataMember] public virtual Answer Answer { get; set; }

        [DataMember] public virtual int AnswerCommentId { get; set; }

        [DataMember] public virtual string TheAnswerComment { get; set; }
        [DataMember] public virtual string Poster { get; set; }
    }


}


Following is the mapping (we used Fluent NHibernate instead of XML, so as not to muddle the main point of this topic ;-)



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentNHibernate.LowercaseSystem;
using FluentNHibernate.Mapping;

namespace TheEntities.Mapper
{
    public class QuestionMap : ClassMap<Question>
    {
        public QuestionMap()
        {

            Not.LazyLoad();
            Id(x => x.QuestionId).GeneratedBy.Sequence("question_seq");

            Map(x => x.TheQuestion).Not.Nullable();
            Map(x => x.Poster).Not.Nullable();

            HasMany(x => x.Comments).Inverse().Not.LazyLoad().Cascade.AllDeleteOrphan();
            HasMany(x => x.Answers).Inverse().Not.LazyLoad().Cascade.AllDeleteOrphan();
        }
    }

    public class QuestionCommentMap : ClassMap<QuestionComment>
    {
        public QuestionCommentMap()
        {
            Not.LazyLoad();
            References(x => x.Question);    

            Id(x => x.QuestionCommentId).GeneratedBy.Sequence("question_comment_seq");

            Map(x => x.TheQuestionComment).Not.Nullable();
            Map(x => x.Poster).Not.Nullable();
        }
    }

    public class AnswerMap : ClassMap<Answer>
    {
        public AnswerMap()
        {
            Not.LazyLoad();
            References(x => x.Question);

            Id(x => x.AnswerId).GeneratedBy.Sequence("answer_seq");

            Map(x => x.TheAnswer).Not.Nullable();
            Map(x => x.Poster).Not.Nullable();

            HasMany(x => x.Comments).Inverse().Not.LazyLoad().Cascade.AllDeleteOrphan();
        }
    }

    public class AnswerCommentMap : ClassMap<AnswerComment>
    {
        public AnswerCommentMap()
        {
            Not.LazyLoad();
            References(x => x.Answer).Not.Nullable();

            Id(x => x.AnswerCommentId).GeneratedBy.Sequence("answer_comment_seq");

            Map(x => x.TheAnswerComment).Not.Nullable();
            Map(x => x.Poster).Not.Nullable();
        }
    }

}


Everything are pretty much standard routine (tagging DataContract, KnownType, DataMember attributes) to adhere to when you need to transport your POCO across the wire, those attributes has no bearing on NHibernate. The only point of interest is the IsReference(works out-of-the-box on .NET 4.0 or 3.5 SP1) property of DataContract attribute; you need to use it when your child table need to reference the parent table, failing to do so will result to this error:



Object graph for type 'X.Y.Z' contains cycles and cannot be serialized if reference tracking is disabled


That attribute signals the WCF not to serialize the whole contents of the object referenced by the child table, instead it will persist those multiple object references on parent entity (as referenced from child entities) with internal id, and this internal id has no bearing on database id nor NHibernate.



Another point of interest is the way the WCF interpret your collection, which by default is set to System.Array, you need to change that to System.Collections.Generic.List so you can add and delete records. If you just keep it as System.Array, the only thing you can do with records is updating them, adding and deleting record would not be available. This can be done by selecting Configure Service Reference...; this is under the Service References of your project



To visualize the sample entity relationships we are persisting, here's the screenshot:








Here's your data structure:

create table question (question_id int4 not null, the_question varchar(255) not null, poster varchar(255) not null, primary key (question_id));

create table question_comment (question_comment_id int4 not null, the_question_comment varchar(255) not null, poster varchar(255) not null, question_id int4, primary key (question_comment_id));

create table answer (answer_id int4 not null, the_answer varchar(255) not null, poster varchar(255) not null, question_id int4, primary key (answer_id));

create table answer_comment (answer_comment_id int4 not null, the_answer_comment varchar(255) not null, poster varchar(255) not null, answer_id int4 not null,
primary key (answer_comment_id));

alter table question_comment add constraint FK83AC3D002500E3C7 foreign key (question_id) references question;

alter table answer add constraint FK77FA76182500E3C7 foreign key (question_id) references question;

alter table answer_comment add constraint FKD5BEEC96136C8DAF foreign key (answer_id) references answer;

create sequence question_seq;
create sequence question_comment_seq;
create sequence answer_seq;
create sequence answer_comment_seq


Sql Server-compatible:

create table question (question_id INT IDENTITY NOT NULL, the_question NVARCHAR(255) not null, poster NVARCHAR(255) not null, primary key (question_id));
create table question_comment (question_comment_id INT IDENTITY NOT NULL, the_question_comment NVARCHAR(255) not null, poster NVARCHAR(255) not null, question_id INT null, primary key (question_comment_id));
create table answer (answer_id INT IDENTITY NOT NULL, the_answer NVARCHAR(255) not null, poster NVARCHAR(255) not null, question_id INT null, primary key (answer_id));
create table answer_comment (answer_comment_id INT IDENTITY NOT NULL, the_answer_comment NVARCHAR(255) not null, poster NVARCHAR(255) not null, answer_id INT not null, primary key (answer_comment_id));
alter table question_comment add constraint FK83AC3D002500E3C7 foreign key (question_id) references question;
alter table answer add constraint FK77FA76182500E3C7 foreign key (question_id) references question;
alter table answer_comment add constraint FKD5BEEC96136C8DAF foreign key (answer_id) references answer


Download proof of concept for NHibernate WCF



An aside, now I feel dirty I handcrafted SqlDataAdapter, Command, Parameters etc on my projects before. The complexity of persisting your data using the traditional ADO.NET approach is directly proportional to the complexity and number of your tables and its relationships, though the code generator I made have alleviated those problems a bit; but still, the code quality feels brittle. I had a desktop application that has 200 lines of code just to persist a 4 level deep table relationships. With NHibernate, your persistence code can still maintain its five-lines-ness regardless of complexities of your table relationships. I should have used NHibernate(or any good ORM for that matter) long time ago