How to apply Unity with WCF Services?

Dec 12, 2011 at 12:53 PM

Hello,

It is the first time that I use Unity IoC.

I have a WCF service that sends data to another WCF service. I choose Unity as my IoC container to apply Dependency Injection Principle.

How to use Unity to solve that issue?

Another problem is that my WCF services are calling another layer : Domain Layer 

How to solve that issue?

Thank you in advance,

Best regards.

Maher Jendoubi

Dec 12, 2011 at 1:14 PM

Could you please elaborate on your scenario?

  • What host do you use for your WCF services? IIS? WAS? Windows Service? Self hosting?
  • What is your problem with calling the Domain Layer from a WCF service? Finding the right part of your Domain Layer to call?
  • Can you give an outline of your system architecture?
Dec 12, 2011 at 4:18 PM

Thank you for the reply, 

  • I use Windows Service as a host for my WCF services.
  • I am looking for a sample of code that use Unity IoC container, that all WCF services send some data to one WCF Service in the WCF Layer using dependency injection(DI), and using DI for the other layers. My problem is where and how to put dependency injection snippet codes into such a layer as I described in the following line:
  • My system architecture is made up of a DAL, a BLL, a WCF Service Layer (5 WCF services independent and hosted as Windows Services), a ClientProxy Layer and GUI using Winforms.

I hope my answer is clearer now.

Thank you in advance,

Kindest regards.

Maher Jendoubi.

Dec 12, 2011 at 7:30 PM
Edited Dec 13, 2011 at 5:46 AM

For Windows Services I prefer to use TopShelf it makes handling services much easier. Nuget allows you to add references to TopShelf and Unity with just a few clicks.

The Contract:

 

[ServiceContract]
public interface IWcfService
{
  [OperationContract]
  string Reverse(string value);
}

 

The Server side. The project is a console application. You wire up the container in the Main() method and then tell TopShelf to launch the service. Notice the ConstructUsing() lambda. It uses the UnityContainer to construct the service.

 

public class Program
{
  public static void Main(string[] args)
  {
    var container = new UnityContainer();
    container.RegisterType<IWcfService, WcfService>();
    container.RegisterType<IRepository, Repository>();
    container.RegisterType<WindowsService>(
      new InjectionConstructor(typeof(IWcfService), new[] { new Uri("http://localhost/myservice") }));

    HostFactory.Run(x =>
      {
        x.Service<WindowsService>(s =>
          {
            s.SetServiceName("mysvc");
            s.ConstructUsing(() => container.Resolve<WindowsService>());
            s.WhenStarted(svc => svc.Start());
            s.WhenStopped(svc => svc.Stop());
          });
        x.RunAsLocalSystem();
        x.SetDescription("WCF Unity Sample");
        x.SetDisplayName("Some Display Name");
        x.SetServiceName("Some Service Name");
      });
  }
}

 

The WindowsService is a more or less empty shell that starts a WCF ServiceHost

 

public class WindowsService
{
  private readonly IWcfService svc;
  private readonly Uri[] baseAddresses;
  private ServiceHost host;
  public WindowsService(IWcfService svc, Uri[] baseAddresses)
  {
    this.svc = svc;
    this.baseAddresses = baseAddresses;
  }
  public void Start()
  {
    if (this.host != null)
    {
      this.host.Close();
    }
    this.host = new ServiceHost(this.svc, this.baseAddresses);
    this.host.Open();
    Console.WriteLine("WCF Host running...");
  }
  public void Stop()
  {
    if (this.host != null)
    {
      this.host.Close();
      this.host = null;
    }
  }
}

The other server side classes go without comments, they are just there for demonstration purposes.

 

 

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class WcfService : IWcfService
{
  private readonly IRepository repository;
  public WcfService(IRepository repository)
  {
    this.repository = repository;
  }
  public string Reverse(string name)
  {
    var domain = this.repository.LoadByName(name);
    string result = domain.DoIt();
    return result;
  }
}
public interface IRepository
{
  DomainObject LoadByName(string name);
}
public class Repository : IRepository
{
  public DomainObject LoadByName(string name)
  {
    return new DomainObject { Name = name };
  }
}
public class DomainObject
{
  public string Name { get; set; }
  public string DoIt()
  {
    return new string(this.Name.Reverse().ToArray());
  }
}

 

To start the windows service just launch the console project.

The client is another console application. Again you wire up Unity in the Main() method (same goes for a WinForms client).

 

class Program
{
  static void Main(string[] args)
  {
    var container = new UnityContainer();
    container.RegisterType<IWcfService>(
      new InjectionFactory(
        c =>
          {
            BasicHttpBinding binding = new BasicHttpBinding();
            EndpointAddress address = new EndpointAddress("http://localhost/myservice");
            ChannelFactory<IWcfService> cf = new ChannelFactory<IWcfService>(binding, address);
            var channel = cf.CreateChannel();
            return channel;
          }));
    Console.WriteLine("Enter name to send to service:");
    string name = Console.ReadLine();
    var proxy = container.Resolve<IWcfService>();
    var result = proxy.Reverse(name);
    Console.WriteLine(result);
    Console.WriteLine("Press ENTER");
    Console.ReadLine();
  }
}

I don't like generated proxies so I use the ChannelFactory to create a proxy at runtime. There is a sample on how to integrate the ChannelFactory smoothly with Unity in the TecX project (TecX.ServiceModel.AutoMagic). It can also use WCF Discovery to find and use Discovery enabled services without any configuration on the client side. There are some tests that show how it is used.

 

Hope this helps.

Dec 12, 2011 at 11:17 PM
Edited Dec 12, 2011 at 11:17 PM

@weberse has given a good example of creating the proxy objects.

I'll add that it's also possible to perform injection of the WCF services themselves.  There are many good articles to read:

 

Basically the approach is to create an IInstanceProvider for Unity to instantiate the UnityContainer, create a Unity Service Behavior, create a Unity Service Host, and create a Unity Service Host Factory.

In terms of the domain layer you can just register your types with the container.  Are there any specific issues or questions you have?   In order to work with your DAL and BLL classes you would just use the typical Unity approach.  I recommend reading:  Using Unity in Applications.  

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com

Dec 13, 2011 at 9:28 AM

Thank you @weberse, I have tried your sample but I find issue with Topshelf

First, I created a new Console project.

I use nuget 1.6 to add Unity and Topshelf references, I add a new WCF Service called WcfService as you did, then I update the content of WcfService and IWcfService with the code that you gave me. I added also a new class for WindowsService.

I build the projet then I got this error:

Error 1 The type or namespace name 'Topshelf' could not be found (are you missing a using directive or an assembly reference?)

and 2 warnings:

Warning 3 The referenced assembly "log4net" could not be resolved because it has a dependency on "System.Web, Version=4.0.0.0, Culture=neutral,

PublicKeyToken=b03f5f7f11d50a3a" which is not in the currently targeted framework ".NETFramework,Version=v4.0,Profile=Client".

Please remove references to assemblies not in the targeted framework or consider retargeting your project. 1-WCFandUnity

Warning 2 The referenced assembly "Topshelf" could not be resolved because it has a dependency on "System.Web, Version=4.0.0.0, Culture=neutral,

PublicKeyToken=b03f5f7f11d50a3a" which is not in the currently targeted framework ".NETFramework,Version=v4.0,Profile=Client".

Please remove references to assemblies not in the targeted framework or consider retargeting your project. 1-WCFandUnity

Did you get such an error and warnings please?

Best regards,

Maher Jendoubi.


Dec 13, 2011 at 9:37 AM

The console project targets the ".NET Framework 4 Client Profile". You need to switch the "Target Framework" to ".NET Framework 4" in your projects properties. That should do the trick.

Dec 13, 2011 at 9:52 AM

I went to my project properties and I change my console project target from ".NET Framework 4 Client Profile" to ".NET Framework 4", the result is that the project can't be loaded again,

I try to use an class library to test the project but I don't know how to host it the Topshelf because I get just dll.

Dec 13, 2011 at 10:02 AM

Sorry, I don't understand the setup of your project. I don't have the sample solution at hand but as far as I can remember this is what it looks like

Project "Contract" -> Class Library, contains IWcfService.cs, Profile can be ".NET Framework 4 Client Profile" I think

Project "Server" -> Console Project, ".NET Framework 4" profile, TopShelf and Unity initialized in Program.cs, contains IRepository, Repository, WindowsService and WcfService

Project "Client" -> Console Project, ".NET Framework 4" profile, only contains Program.cs which wires up Unity and WCF

 

You need to start both Console Projects (Server first, then Client). For testing purposes this can be done from Visual Studio (right click -> Debug -> Start new instance).

Dec 13, 2011 at 10:14 AM

how did you do this please I can't do it with vs2010 sp1 Project "Client" -> Console Project, ".NET Framework 4" profile, only contains Program.cs which wires up Unity and WCF?

Dec 13, 2011 at 10:25 AM

I find it just I need to reopen the project. Thank you @weberse

Dec 13, 2011 at 10:55 AM

 

@weberse I need the code source for the sample solution please. I have a problem in the server side, the server launch a windows console and it stops after 2 or 3 seconds

and when I write in the client console my string to reverse, 

I get an EndpointNotFoundException at the following line of code: 

var result = proxy.Reverse(name);
and the message of the exception is:

There was no endpoint listening at http://localhost/myservice that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.

Dec 13, 2011 at 11:15 AM

I don't have the source code at hand but I can mail it to you tonight if you send me a PM with your email address.

Do you start Visual Studio as Administrator? You need admin privileges to open up a WCF endpoint, otherwise you would get an exception.

Do you start the Service before you start the client? The Service needs some time before it is available.

Dec 13, 2011 at 11:58 AM

OK, I started Visual Studio as Administrator and I started the Service before the client. I sent you my email.

Thank you,

Regards,

Maher Jendoubi.

Dec 13, 2011 at 1:16 PM

@randylevy thank you for the reply and the links, I started with the sample solution of @weberse but my system architecture is different. In the Server projet I have 4 WCF Services.

3 of my WCF Services send some data to notify the forth WCF Service. So let's forget the Client project. I have one Server Project. Is this technically possible with WCF?

Dec 13, 2011 at 1:19 PM

It is as if my 3 WCF Services are client of my server which the forth WCF Service.