artganify
artganify

Reputation: 703

Hosting multiple services within one service host?

I've got two completely separate services (along with their contracts) which have completely different dependencies as well as completely different responsibilities. However, things they got in common are:

Dummy contracts:

public class IFoo {
    void Foo();
}

public class IBar {
    void Bar();
}

Now, what I'd like to do is to host them both in the same service host. I am aware that it's possible to expose both services as endpoints and implement them in the same service type like this:

public class FooBar : IFoo, IBar { }

var host = new ServiceHost(typeof(FooBar));

However I'm looking for a way to do something like this:

public class FooImpl : IFoo { }

public class BarImpl : IBar { }

var host = new ServiceHost();
host.AddEndpoint(typeof(FooImpl);
host.AddEndpoint(typeof(BarImpl);
host.Open();

So I can keep my service implementations nice and tidy, each with their own dependencies instead of a god object for everything.

Anyone has an idea on how to accomplish this?

Upvotes: 2

Views: 4444

Answers (2)

Wicher Visser
Wicher Visser

Reputation: 1543

You can host multiple ServiceHosts, each with their own service and endpoint, all sharing the same base address and port. Here is my implementation, encapsulated into a ServiceHosting class:

public class ServiceHosting<T1, T2>
{
    //Declaration
    protected ServiceHost SelfHost;
    protected string BaseUrlString;
    protected int Port;
    protected string HostUrlString = "";
    protected bool ExtendedBinding;

    //Constructor
    public ServiceHosting(string url, int port, bool extendedBinding = false)
    {
        BaseUrlString = url;
        Port = port;
        ExtendedBinding = extendedBinding;
    }

    //Properties
    protected int Max => int.MaxValue;

    public virtual bool StartService(int port)
    {
        try
        {
            var hostName = System.Net.Dns.GetHostName();

            HostUrlString = $@"net.tcp://{hostName}:{port}{BaseUrlString}"; //GM 10.09.2012: 

            try
            {
                SelfHost = new ServiceHost(typeof(T1), new Uri(HostUrlString));

                var smb = SelfHost.Description.Behaviors.Find<ServiceMetadataBehavior>() ??
                          new ServiceMetadataBehavior() { };
                smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;

                SelfHost.Description.Behaviors.Add(smb);

                var throttleBehavior = new ServiceThrottlingBehavior();
                SelfHost.Description.Behaviors.Add(throttleBehavior);

                var mexUrlString = String.Format(@"net.tcp://{0}:{1}{2}/mex", hostName, port, BaseUrlString);

                // Add MEX endpoint
                SelfHost.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexTcpBinding(), new Uri(mexUrlString));

                // Add binding
                var binding = ConfigureBinding();

                // Add application endpoint
                SelfHost.AddServiceEndpoint(typeof(T2), binding, "");

                if (ExtendedBinding)
                {
                    foreach (ServiceEndpoint ep in SelfHost.Description.Endpoints)
                    {
                        foreach (OperationDescription op in ep.Contract.Operations)
                        {
                            var dataContractBehavior = op.Behaviors[typeof(DataContractSerializerOperationBehavior)] as DataContractSerializerOperationBehavior;

                            if (dataContractBehavior != null)
                            {
                                dataContractBehavior.MaxItemsInObjectGraph = Max;
                            }
                        }
                    }
                }

                // Open the service host to accept incoming calls
                SelfHost.Open();
            }
            catch (CommunicationException)
            {
                // log
                SelfHost.Abort();
                return false;
            }
            catch (Exception)
            {
                // log
                SelfHost.Abort();
                return false;
            }

        }
        catch (Exception)
        {
            // log
            return false;
        }
        return true;
    }

    private NetTcpBinding BaseConfigureBinding()
    {
        return new NetTcpBinding
        { Security = { Mode = SecurityMode.None }, CloseTimeout = new TimeSpan(0, 0, 0, 5) };
    }

    protected virtual NetTcpBinding ConfigureBinding()
    {
        var binding = BaseConfigureBinding();

        if (ExtendedBinding)
        {
            binding.MaxBufferPoolSize = Max;
            binding.MaxReceivedMessageSize = Max;
            binding.MaxBufferSize = Max;
            binding.MaxConnections = 200; //rdoerig 12-03-2013 default value is 10:
            binding.ListenBacklog = 200; //rdoerig 12-03-2013 default value is 10 : buffer of pending connections 

            binding.ReaderQuotas.MaxDepth = Max;
            binding.ReaderQuotas.MaxStringContentLength = Max;
            binding.ReaderQuotas.MaxArrayLength = Max;
            binding.ReaderQuotas.MaxBytesPerRead = Max;
            binding.ReaderQuotas.MaxNameTableCharCount = Max;

            binding.CloseTimeout = new TimeSpan(0, 0, 10, 0);
            binding.OpenTimeout = new TimeSpan(0, 0, 10, 0);
            binding.ReceiveTimeout = new TimeSpan(0, 0, 10, 0);
            binding.SendTimeout = new TimeSpan(0, 0, 10, 0);

        }

        return binding;
    }

    public bool StopService()
    {
        try
        {
            SelfHost?.Close();
        }
        catch (Exception)
        {
            // log
            return false;
        }
        return true;
    }
}

This can be instantiated like so:

     private readonly ServiceHosting<LoginService, ILoginService> _serviceHostLogin = new ServiceHosting<LoginService, ILoginService>(LoginUrl, true);

And started/stopped like so:

            _serviceHostLogin.StartService();
            _serviceHostLogin.StopService();

To make sure you won't get an error when hosting multiple services, you should configure the URIs for services to be different, e.g.

new ServiceHosting<LoginService, ILoginService>("/Services/LoginService", true);
new ServiceHosting<ConfigService, IConfigService>("/Services/ConfigService", true);

Upvotes: 4

Ricardo Pontual
Ricardo Pontual

Reputation: 3757

You can implement both interfaces in same service class and have one endpoint, but with separate contracts:

[ServiceBehavior]
public partial class IntegratedService
{
    // You can implement "base" methods here
}

Then, implement each interface:

public partial class IntegratedService : IFoo
{
   // Implement IFoo interface
}

public partial class IntegratedService : IBar
{
   // Implement IBar interface
}

Hope it helps.

Upvotes: -1

Related Questions