Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

Dependency Injection in WCF Service Library Project with Self hosting

5.00/5 (1 vote)
30 Jul 2019CPOL5 min read 21.1K   302  
There are several contents available on the internet that talk about injecting dependencies through .svc file's markup [in WCF service application] and it's easy when you have a WCF service application. But, it is equally easy to do that even in your WCF Library project (without .svc).

Introduction

There are many ways to make your application loosely-coupled. Using Dependency injection is one such way.
Loosely coupled application helps you in making your application maintainable, extensible and it also helps in testing your code, etc. This article will walk you through step by step into implementing IOC container into your WCF service library project. Finally, we are going to add the self hosting component. Here, we are going to use Autofac as the IOC Container.

Background

For making application loosely coupled, it should adhere to the decorum of SOLID principles in object oriented programming.

  • Single responsibility principle: A class should only have a single responsibility, that is, only changes to one part of the software's specification should be able to affect the specification of the class.
  • Open–closed principle: "Software entities ... should be open for extension, but closed for modification."
  • Liskov substitution principle: "Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program." See also design by contract.
  • Interface segregation principle: "Many client-specific interfaces are better than one general-purpose interface."
  • Dependency inversion principle: One should "depend upon abstractions, [not] concretions."

Dependency injection helps in attaining 'Dependency Inversion Principle'
Source: https://en.wikipedia.org/wiki/SOLID

For IOC Container, we are going to use Autofac and Autofac.WCF
Source: https://autofaccn.readthedocs.io/en/latest/integration/wcf.html

Things You Need

  1. Visual Studio (I am using VS Community Edition 2017)
  2. Autofac
  3. Autofac.WCF
  4. And an open mind :)

Using the Code

I am going to use very simple code as an example. My focus is on making things simple to understand.

Step 1: Create a Solution and Add WCF Library Project

Image 1

We'll name the solution as "DependencyInjectionWcfWithSelfHosting" and the WCF service library project as "TestService".

Step 2: Add Scaffolding Code in WCF Service Project

  • Remove app.config, and the system created IService1, Service1.cs file (You can rename these files but I like to start with a clean slate):

    Image 2

  • Right click on your TestService project and add new item as a WCF Service class and name it as "DisplayService".

    Image 3

It should look like this:

IDisplayService.cs

C#
[ServiceContract]
public interface IDisplayService
{
    [OperationContract]
    string DisplayInputValue(string input);
}

DisplayService.cs

C#
public class DisplayService : IDisplayService
{
    public string DisplayInputValue(string input)
    {
        return input;
    }
}

As you can see, there is nothing fancy in code, just an input passed is return back as is.
But hold on a minute, we can do something that would decorate that input with some string message, say for an example:

If I pass "Sunny" as an input, I can add some prefix string to it as "You've entered" to the passed input and return it as "You've entered Sunny".

Well, I can simply append input string with "You've entered" and return it from DisplayInputValue().

C#
public string DisplayInputValue(string input)
{
    return "You've entered" + input;
}

But, to Get Around the Topic of Dependency Injection in Wcf Service, We Will Try to Do the Appending Part From Some Utility Classes. and Then, Inject Its Instance Through Constructor Injection.

Step 3: Add Just Enough Code for Utility Class

  • Add folder name "Utilities" in TestService project and add a class in it with name as "StringMixer" with the following code:
    C#
    public class StringMixer : IStringMixer
    {
        /// <summary>
        /// This method deals with adding ""You've entered" before the passed input
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public string MixStringValue(string value)
        {
            return $"You've entered {value}";
        }
    }
    
  • Extract interface from the "StringMixer" class. That would be "IStringMixer". This will help us during constructor injection into our "DisplayService".
    C#
    public interface IStringMixer
    {
        string MixStringValue(string value);
    }
    

We are going to make use of these utilities in a moment.

Step 4: Add Hosting Support

  • Add the console application to the solution and name it as "Host".
  • Go to the program.cs in console application and add the following code in Main(). Make sure you've added reference for "TestService" and "System.ServiceModel".
C#
using System;
using System.ServiceModel;
using TestService;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost host = new ServiceHost(typeof(DisplayService));
            host.Open();
            Console.WriteLine("Host has started");
            Console.ReadLine();
        }
    }
}

Now there might be an app.config file in the console application we've recently added. Add the following ServiceModel section in it.

XML
<system.serviceModel>
        <services>
            <service name="TestService.DisplayService">
                <endpoint address="" 
                          binding="basicHttpBinding" 
                          contract="TestService.IDisplayService"
                          name="TestServiceEndPoint"  />

                <endpoint address="mex" 
                          binding="mexHttpBinding" 
                          contract="IMetadataExchange"
                          name="TestServiceMexEndPoint"/>

              <host>
                <baseAddresses>
                  <add baseAddress="http://localhost/TestService" />
                </baseAddresses>
              </host>
            </service>
        </services>
      <behaviors>
        <serviceBehaviors>
          <behavior>
            <serviceDebug includeExceptionDetailInFaults="true" />
            <serviceMetadata httpGetEnabled="true" />
          </behavior>
        </serviceBehaviors>
      </behaviors>
    </system.serviceModel>

OR

If you want, you can manually create your own service model configuration by right clicking on app.config and click on "Edit WCF Configuration" and do as shown in the wizard (make sure it will run).

  • Run your "Host" Application to make sure everything works
  • You might get the access permission error, simply run your Visual Studio in administrator mode and then run your Host application

If you managed to get so far, congratulations. you are halfway done !!

Step 5: Verify What We Did So Far Is Working or Not

  • Try to open the url "http://localhost/TestService" in your WCFTestClient and make sure it works.

Image 4

Step 6: Complete Unfinished Business

As I mentioned at the end of point 3, we are going to inject the utility class "StringMixer" in the "DisplayService" as a constructor injection.

  • Get to the "TestService" project, open your display service class and add a constructor.
  • Inject the dependency of StringMixer using its interface as an abstraction, i.e., "IStringMixer".

DisplayService.cs now might look like this:

C#
public class DisplayService : IDisplayService
    {
        private readonly IStringMixer _mixer;

        public DisplayService(IStringMixer mixer)
        {
            _mixer = mixer;
        }

        public string DisplayInputValue(string input)
        {
            return _mixer.MixStringValue(input);
        }
    }

We are going to inject this dependency from the host application. Let's get started for an exciting adventure.

Step 7: Setup Autofac and Autofac.Wcf in "Host" Project

  • Add "Autofac" and "Autofac.Wcf" as a nuget package in "Host" project:

    Image 5

  • Add class in "Host" project with name "Bootstrapper". In this class, we will register our dependencies in Autofac container.
    C#
    public static ContainerBuilder RegisterContainerBuilder()
            {
                ContainerBuilder builder = new ContainerBuilder();
                builder.Register(c => new StringMixer()).As<IStringMixer>();
                builder.Register(c => new DisplayService
                                (c.Resolve<IStringMixer>())).As<IDisplayService>();
                return builder;
            }
Quote:

It's like whenever Autofac sees the reference to those dependencies during constructor injection based upon their abstraction (interfaces), It will inject their registered class into it.

For example

"builder.Register(c => new StringMixer()).As<IStringMixer>();"

means, wherever there is a reference about IStringMixer, Autofac will inject StringMixer as its implementation.

Step 8: Tweak "Host" Project - A Little

  • Modify code in program.cs file by adding the following code in it:
    C#
    static void Main(string[] args)
            {
                using (IContainer container = Bootstrapper.RegisterContainerBuilder().Build())
                {
                    ServiceHost host = new ServiceHost(typeof(DisplayService));
    
                    IComponentRegistration registration;
                    if (!container.ComponentRegistry.TryGetRegistration
                       (new TypedService(typeof(IDisplayService)), out registration))
                    {
                        Console.WriteLine("The service contract 
                                           has not been registered in the container.");
                        Console.ReadLine();
                        Environment.Exit(-1);
                    }
    
                    host.AddDependencyInjectionBehavior<IDisplayService>(container);
                    host.Open();
                    Console.WriteLine("Host has started");
                    Console.ReadLine();
    
                    host.Close();
                    Environment.Exit(0);
                }

Step 9: Run the Host Application. This Time, You've Got the Powers of Dependency Injection.

Thank you for your patience. We are done walking those steps !!

Image 6

(Image Courtesy: daniel-cheung from unsplash)

Points of Interest

  1. You can remove the app.config file from "Host" project and write the code related to EndPoint configuration in your host application instead. Just for information, I've added support for "http" and "netNamedPipe".
    C#
    static void Main(string[] args)
                {
                    using (IContainer container = 
                           Bootstrapper.RegisterContainerBuilder().Build())
                    {
                        var httpLocation = "http://localhost/DisplayService";
                        Uri address = new Uri(httpLocation);
    
                        var netNamedPipeLocation = "net.pipe://localhost/DisplayService/";
    
                        ServiceHost host = new ServiceHost(typeof(DisplayService));
    
                        host.AddServiceEndpoint(typeof(IDisplayService), 
                               new BasicHttpBinding(), httpLocation);
    
                        host.AddServiceEndpoint(typeof(IDisplayService),
                            new NetNamedPipeBinding(NetNamedPipeSecurityMode.None),
                            netNamedPipeLocation);
     
    
                        IComponentRegistration registration;
                        if (!container.ComponentRegistry.TryGetRegistration
                           (new TypedService(typeof(IDisplayService)), out registration))
                        {
                            Console.WriteLine("The service contract has not been registered 
                                               in the container.");
                            Console.ReadLine();
                            Environment.Exit(-1);
                        }
    
                        host.AddDependencyInjectionBehavior<IDisplayService>(container);
                        host.Description.Behaviors.Add(new ServiceMetadataBehavior 
                                 { HttpGetEnabled = true, HttpGetUrl = address });
    
                        // Add MEX endpoint
                        host.AddServiceEndpoint(
                            ServiceMetadataBehavior.MexContractName,
                            MetadataExchangeBindings.CreateMexNamedPipeBinding(),
                            netNamedPipeLocation + "mex"
                        );
    
                        host.Open();
    
                        Console.WriteLine("The host has been opened.");
                        Console.ReadLine();
    
                        host.Close();
                        Environment.Exit(0);
                    }
                }
  2. You can also make use of the information provided in this article in WAS hosting as well.
  3. You can use any IOC container apart from Autofac or can make one of your own for dependency injection.
  4. It's not always necessary to have an interface for your class, But it gives better abstraction and composition over inheritance. Though, there are certain ways to Inject dependency for such class (I mean class without interface) using IOC Container.

References

History

  • 29/07/2019
    • Format added for code snippet
    • Updated images

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)