3

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?

Community
  • 1
  • 1
pfluggs11
  • 166
  • 2
  • 9
  • The application that is hosting your WCF services: is that a desktop app, a windows service, or a web app? – TylerOhlsen Nov 08 '12 at 15:20
  • Ah I apologize for not including that, but it being hosted by a Windows Service. – pfluggs11 Nov 08 '12 at 15:41
  • 1
    Ok, this is important because you have two different concepts conflicting with each other. Your Component...AsWcfService self hosts the WCF service and your svc metadata file tells an external host how to instantiate your service. Usually you just do one or the other. – TylerOhlsen Nov 08 '12 at 15:49
  • Since you are self hosting in a windows service, you don't need the metadata file (.svc) at all. – TylerOhlsen Nov 08 '12 at 15:50
  • Do you want to use the standard configuration in your app.config file or do you want to configure your services in code (like you've demonstrated)? More info: http://msdn.microsoft.com/en-us/library/ms733932.aspx – TylerOhlsen Nov 08 '12 at 15:52
  • I did have the Web.config setup correctly previously that I can always revert back to but I would like to be able to configure this fluently and consume them in another WCF service. But without the .svc files, how do I add the reference to another project and consume the AccountDataService? – pfluggs11 Nov 08 '12 at 19:23
  • I'm a bit confused by your comment. Configuring it in code has nothing to do with the client side. You will need a different registration into Windsor for the client side (AsWcfService vs AsWcfClient). Regardless of that comment, I will add another answer with configuring it in code. – TylerOhlsen Nov 08 '12 at 19:29
  • The *.svc files are just metadata to tell the external host (like IIS) how you would like it instantiate your service. You are doing the same work by registering it in Windsor. To consume the same code on server and client, you would just need the ServiceContract interface in a shared library that both reference. – TylerOhlsen Nov 08 '12 at 19:32

2 Answers2

3

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() { }
    }
}
TylerOhlsen
  • 5,485
  • 1
  • 24
  • 39
  • So, correct me if I'm wrong, but in order to consume these services IAccountDataService has to be in a separate library that another application uses as the service contract? And the *.svc files are no longer necessary? – pfluggs11 Nov 08 '12 at 20:51
  • In my opinion, having a shared library that contains the interfaces is the easiest way to consume the service if you have control over both client and server. That is not the only way though. Your client application can have no reference to any shared library and it can import the contract through [svcutil](http://msdn.microsoft.com/en-us/library/aa347733.aspx) or as a [service reference](http://msdn.microsoft.com/en-us/library/bb628652.aspx) by pointing to the exposed WSDL from your endpoint. – TylerOhlsen Nov 08 '12 at 22:16
  • 1
    Really, truely, the *.svc files are not necessary when you self host. They are generally only for hosting within IIS. [More info](http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/034ac9f4-c868-4174-b244-76e489af7935/) – TylerOhlsen Nov 08 '12 at 22:23
2

Here's how I got this to work self hosted using an app.config file. I did it this way because this is what I am familiar with. If you want, I can modify it to be configured through code instead of through the app.config file...

using System;
using System.ServiceModel;
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>();

            _container.Register(
                Component
                    .For<IAccountDataService>()
                    .ImplementedBy<AccountDataService>()
                    .AsWcfService(),
                Component
                    .For<IInstitutionDataService>()
                    .ImplementedBy<InstitutionDataService>()
                    .AsWcfService());

            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() { }
    }
}

And the config file...

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyHttpServiceBehavior">
          <serviceMetadata httpGetEnabled="True" />
          <serviceDebug includeExceptionDetailInFaults="True" httpHelpPageEnabled="True"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <services>
      <service name="DISandbox.AccountDataService" behaviorConfiguration="MyHttpServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:49246/AccountDataService"/>
          </baseAddresses>
        </host>
        <endpoint binding="basicHttpBinding" contract="DISandbox.IAccountDataService" />
      </service>

      <service name="DISandbox.InstitutionDataService" behaviorConfiguration="MyHttpServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:49246/InstitutionDataService"/>
          </baseAddresses>
        </host>
        <endpoint binding="basicHttpBinding" contract="DISandbox.IInstitutionDataService" />
      </service>
    </services>
  </system.serviceModel>

</configuration>
TylerOhlsen
  • 5,485
  • 1
  • 24
  • 39
  • Finally got it using installers and hosting in IIS. Both answers given by @TylerOhlsen did work but I went with a combination of the two. – pfluggs11 Dec 18 '12 at 22:18