Register WCF Service components in Castle Windsor

pfluggs11 picture pfluggs11 · Nov 8, 2012 · Viewed 7.1k times · Source

I haven't found anyone else with this issue so I'm assuming it is my own fault that this is happening. I very green when it comes to WCF and Castle Windsor IoC container so that would probably be the first reason why this is happening. Anyways, I have been struggling with this for several days and haven't found the reason for this yet.

First, I registered my services using Castle Windsor.

var baseUri = new Uri("http://localhost:49246");

_container = new WindsorContainer();

var returnFaults = new ServiceDebugBehavior
{
    IncludeExceptionDetailInFaults = true,
    HttpHelpPageEnabled = true
};
var metadata = new ServiceMetadataBehavior { HttpGetEnabled = true };

_container.Register(
    Component.For<IServiceBehavior>().Instance(returnFaults),
    Component.For<IServiceBehavior>().Instance(metadata));

var baseUri = new Uri("http://localhost:49246");

_container = new WindsorContainer();

var returnFaults = new ServiceDebugBehavior
{
    IncludeExceptionDetailInFaults = true,
    HttpHelpPageEnabled = true
};
var metadata = new ServiceMetadataBehavior { HttpGetEnabled = true };

_container.Register(
    Component.For<IServiceBehavior>().Instance(returnFaults),
    Component.For<IServiceBehavior>().Instance(metadata));

_container
    .Register(
        Component
            .For<IAccountDataAccessor>()
                .ImplementedBy<AccountDataAccessor>(),
        Component
            .For<IInstitutionDataAccessor>()
                .ImplementedBy<InstitutionDataAccessor>(),
        Component
            .For<IRelationshipDataAccessor>()
                .ImplementedBy<RelationshipDataAccessor>(),
        Component
            .For<ITransactionDataAccessor>()
                .ImplementedBy<TransactionDataAccessor>()

    );

_container.AddFacility<WcfFacility>(f => { f.CloseTimeout = TimeSpan.Zero; });
_container.Register(
    Component
        .For<IAccountDataService>()
        .ImplementedBy<AccountDataService>()
        .Named("AccountDataService")
        .LifeStyle.Is(Castle.Core.LifestyleType.Singleton)
        .AsWcfService(new DefaultServiceModel()
            .AddBaseAddresses(baseUri)
            .AddEndpoints(WcfEndpoint
                .ForContract(typeof(IAccountDataService))
                .BoundTo(new WSHttpBinding()))),
    Component
        .For<IInstitutionDataService>()
        .ImplementedBy<InstitutionDataService>()
        .Named("InstitutionDataService")
        .LifeStyle.Is(Castle.Core.LifestyleType.Singleton)
        .AsWcfService(new DefaultServiceModel()
            .AddBaseAddresses(baseUri)    
            .AddEndpoints(WcfEndpoint
                .ForContract(typeof(IInstitutionDataService))
                .BoundTo(new WSHttpBinding()))),
    Component
        .For<IRelationshipDataService>()
        .ImplementedBy<RelationshipDataService>()
        .Named("RelationshipDataService")
        .LifeStyle.Is(Castle.Core.LifestyleType.Singleton)
        .AsWcfService(new DefaultServiceModel()
            .AddBaseAddresses(baseUri)
            .AddEndpoints(WcfEndpoint
                .ForContract(typeof(IRelationshipDataService))
                .BoundTo(new WSHttpBinding()))),
    Component
        .For<ITransactionDataService>()
        .ImplementedBy<TransactionDataService>()
        .Named("TransactionDataService")
        .LifeStyle.Is(Castle.Core.LifestyleType.Singleton)
        .AsWcfService(new DefaultServiceModel()
            .AddBaseAddresses(baseUri)    
            .AddEndpoints(WcfEndpoint
                .ForContract(typeof(ITransactionDataService))
                .BoundTo(new WSHttpBinding()))));

Now when I try and get a service reference for AccountDataService, it works fine, no errors. But when I try and add a service reference to InstitutionDataService it throws an exception saying:

    Could not find a component with name InstitutionDataService, did you forget to register it?

This has been quite frustrating and I can't seem to find a straight answer in the Castle Windsor docs (or lack there of). And I have attempted to implement the solution suggested in this SO question:

SO Question

But this also did not work. None of the components appeared to be registered. Also, if it helps, this is my svc markup:

    <%@ ServiceHost Language="C#" Debug="true" Service="AccountDataService"
Factory="Castle.Facilities.WcfIntegration.DefaultServiceHostFactory, Castle.Facilities.WcfIntegration" %>

And it is the same for all services.

So, my question is, why aren't my components being registered along with the AccountDataService?

Answer

TylerOhlsen picture TylerOhlsen · Nov 8, 2012

Here's how I got it working configuring through the code with no app.config file.

One difference is I'm using the PublishMetadata method on DefaultServiceModel instead of registering that with the IServiceBehavior. To be honest, I'm not sure how Windsor would behave if the same interface is registered twice and it's not a decorator pattern (see this post about Windsor and decorators).

The other difference is I'm specifying a unique address per service in the AddBaseAddress method. Each service needs a unique address, but you can expose multiple unique endpoints relative to that base address per service using the At() method on WcfEndpoint. (i.e. if you wanted to expose a BasicHttpEndpoint and a WSHttpEndpoint for the same service)

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using Castle.Facilities.WcfIntegration;
using Castle.MicroKernel.Registration;
using Castle.Windsor;

namespace DISandbox
{
    public class Program
    {
        private static WindsorContainer _container;

        public static void Main(string[] args)
        {
            _container = new WindsorContainer();
            _container.AddFacility<WcfFacility>();

            ServiceDebugBehavior returnFaults = new ServiceDebugBehavior
            {
                IncludeExceptionDetailInFaults = true,
                HttpHelpPageEnabled = true,
            };

            _container.Register(
                Component.For<IServiceBehavior>().Instance(returnFaults));

            string baseAddress = "http://localhost:49246/";

            _container.Register(
                Component
                    .For<IAccountDataService>()
                    .ImplementedBy<AccountDataService>()
                    .AsWcfService(new DefaultServiceModel()
                        .PublishMetadata(mex => mex.EnableHttpGet())
                        .AddBaseAddresses(baseAddress + "AccountDataService")
                        .AddEndpoints(WcfEndpoint
                            .ForContract<IAccountDataService>()
                            .BoundTo(new WSHttpBinding()))),
                Component
                    .For<IInstitutionDataService>()
                    .ImplementedBy<InstitutionDataService>()
                    .AsWcfService(new DefaultServiceModel()
                        .PublishMetadata(mex => mex.EnableHttpGet())
                        .AddBaseAddresses(baseAddress + "InstitutionDataService")
                        .AddEndpoints(WcfEndpoint
                            .ForContract<IInstitutionDataService>()
                            .BoundTo(new WSHttpBinding()))));

            Console.WriteLine("Press any key to exit...");
            Console.ReadLine();
        }
    }

    [ServiceContract]
    public interface IAccountDataService
    {
        [OperationContract]
        void DoStuff();
    }

    public class AccountDataService : IAccountDataService
    {
        public void DoStuff() { }
    }

    [ServiceContract]
    public interface IInstitutionDataService
    {
        [OperationContract]
        void DoStuff();
    }

    public class InstitutionDataService : IInstitutionDataService
    {
        public void DoStuff() { }
    }
}