Showing posts with label LightInject. Show all posts
Showing posts with label LightInject. 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!