Quantcast
Channel: Christos S. – chsakell's Blog
Viewing all articles
Browse latest Browse all 43

Dependency injection in WCF

$
0
0

Dependency injection is a software design pattern that implements inversion of control for resolving dependencies and is highly recommended for building scalable, testable and maintainable applications. In this very blog we have seen many times this pattern, mostly in ASP.NET MVC and ASP.NET Web API related posts where for example dependencies (data repositories or middle services) were injected into MVC constructors. We haven’t seen this pattern though in applications using the Windows Communication Framework. I decided to write this post cause I believe there many interesting things to cover when it comes to “marry” Dependency Injection and WCF. I will divide this post in the following three main areas:

Solution architecture

We are going to build a Service Oriented Application based on Windows Communication Framework. The architecture is mostly based on the decision that we are going to de-couple client and business entities in our application. Many developers believe that the only way to create WCF clients is to generate them using Visual Studio built in functionality. Of course this method works like a charm but the truth is that it strongly binds both sides of the wire: Proxies will fault if any either client’s data contract or service data contract change. And here is where the power of System.Runtime.Serialization comes into the play supporting Data Contract versioning through the IExtensibleDataObject interface. This interface has one property ExtensionData of type ExtensionDataObject which makes data contract changes non-breakable in both sides. That said we will create both business and client entities in such a way that implement the IExtensibleDataObject interface. WCF Service will serve business entities but proxies (clients) will be able to serialize using their client ones. Business and client entities will share some common properties but nothing stops us to add more or remove some others in either of them. The serialization will work properly. More over you ‘ll see that client entities may have any other functionality you wish such as custom validators which a Web client may need for it’s UI. Data repositories will use business entities mapped to the database using Entity Framework. For this example we ‘ll use Entity Framework in an hybrid way that is design the database in SQL Server and manually add later the respective properties in our business entities. Let’s take a look at the architecture of our application.
wcf-dependency-injection-03

Let’s start building our SOA application. Create an empty solution named WCF.DependencyInjection and add the following solution folders with their respective projects:

  1. Business
    • Business.Entities: Class library (Contains Service side Entities)
    • Business.Services: Class library (Contains WCF Service contracts and implementations)
  2. Client
    • Client.Contracts: Class library (Contains the relevant Client side WCF Contracts)
    • Client.Entities: Class library (Contains client side Entities)
    • Client.Proxies: Class library (Contains WCF client proxies)
  3. Core
    • Core.Common: Class library (Contains Common classes – extensions for our solution)
  4. Hosts
    • WCF.DependencyInjection.Web: Empty ASP.NET Web application (Hosts WCF Services)
  5. Data Access
    • Data.Core: Class library (Contains Entity Framework configurations, Data repositories etc..)
  6. Tests
    • Test.Proxies: Class library (Contains NUnit tests for our solution)

Business.Entities

Switch to Business.Entities and create the following two Entities which will be used as Data Contracts at the business side of the wire.

 [DataContract]
    public class Blog : IExtensibleDataObject
    {
        [DataMember]
        public int ID { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string URL { get; set; }
        [DataMember]
        public string Owner { get; set; }

        #region IExtensibleDataObject Members

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion
    }
[DataContract]
    public class Article : IExtensibleDataObject
    {
        [DataMember]
        public int ID { get; set; }
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public string Contents { get; set; }
        [DataMember]
        public string Author { get; set; }
        [DataMember]
        public string URL { get; set; }
        [DataMember]
        public int BlogID { get; set; }

        [DataMember]
        public int ContentLength
        {
            get
            {
                return Contents.Length;
            }
            set { }
        }

        #region IExtensibleDataObject Members

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion
    }

As you can see both of our entities implement the IExtensibleDataObject interface in order to support Round-Tripping (Forward-Compatible Data Contracts). The same will apply in Client entities too. You need to add reference to System.Runtime.Serialization assembly. In fact, go and add this reference to all of our projects.

Data.Core

We need to create the data access repositories before move to WCF Services so switch to Data.Core project, add reference to Business.Entities and install Entity Framework through Nuget Packages. At this point you will allow me not to paste all the required code for this layer since we have seen the same code implementing generic data repositories in many other posts of this blog. Instead, I will show you only the most important parts that needs to be explained for this solution. More over, I will provide you links for all not shown classes from my github account. Of course once again, all the source code for this post is available (you ‘ll find a link at the bottom of this post).
wcf-dependency-injection-02
Let’s see Entity Framework configurations for our entities.

public class BlogConfiguration : EntityTypeConfiguration<Blog>
    {
        public BlogConfiguration()
        {
            HasKey(b => b.ID);
            Property(b => b.Name).IsRequired().HasMaxLength(100);
            Property(b => b.URL).IsRequired().HasMaxLength(200);
            Property(b => b.Owner).IsRequired().HasMaxLength(50);

            Ignore(b => b.ExtensionData);
        }
    }
public class ArticleConfiguration : EntityTypeConfiguration<Article>
    {
        public ArticleConfiguration()
        {
            HasKey(a => a.ID);
            Property(a => a.Title).IsRequired().HasMaxLength(100);
            Property(a => a.Contents).IsRequired();
            Property(a => a.Author).IsRequired().HasMaxLength(50);
            Property(a => a.URL).IsRequired().HasMaxLength(200);

            Ignore(a => a.ExtensionData);
            Ignore(a => a.ContentLength);
        }
    }

It’s obvious that I just wanted to point that ExtensionData will not be mapped to a database table column. Now let’s see the DbContext class that we need to access table records through Entity Framework:

public class DIContext : DbContext
    {
        public DIContext()
            : base("DIContext")
        {
            Database.SetInitializer<DIContext>(null);
        }

        public DbSet<Blog> BlogSet { get; set; }
        public DbSet<Article> ArticleSet { get; set; }

        public virtual void Commit()
        {
            base.SaveChanges();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            modelBuilder.Configurations.Add(new ArticleConfiguration());
            modelBuilder.Configurations.Add(new BlogConfiguration());
        }
    }

We turned off the database initializer and we told Entity Framework not to use the default naming convention when accessing database. Tables will have the same name as our entity classes. Here are all the other required classes:

  1. Data.Core.Infrastructure
  2. Data.Core.Repositories

Database

Let’s see WCFDI database we need for our example. Run the following script to create the database and insert some records as well.

USE [master]
GO
/****** Object:  Database [WCFDI] ******/
CREATE DATABASE [WCFDI]
GO
USE [WCFDI]
GO

-- Create Blog Table
CREATE TABLE [dbo].[Blog](
	[ID] [int] NOT NULL,
	[Name] [nvarchar](100) NOT NULL,
	[URL] [nvarchar](200) NOT NULL,
	[Owner] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_Blog] PRIMARY KEY CLUSTERED
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


-- Crate Article Table
CREATE TABLE [dbo].[Article](
	[ID] [int] NOT NULL,
	[Title] [nvarchar](100) NOT NULL,
	[Contents] [nvarchar](max) NOT NULL,
	[Author] [nvarchar](50) NOT NULL,
	[URL] [nvarchar](200) NOT NULL,
	[BlogID] [int] NOT NULL,
 CONSTRAINT [PK_Article] PRIMARY KEY CLUSTERED
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO
-- Create FK
GO
ALTER TABLE [dbo].[Article]  WITH CHECK ADD  CONSTRAINT [FK_Article_Blog] FOREIGN KEY([BlogID])
REFERENCES [dbo].[Blog] ([ID])
GO
ALTER TABLE [dbo].[Article] CHECK CONSTRAINT [FK_Article_Blog]
GO

-- Add some records
INSERT INTO dbo.Blog
        ( ID, Name, URL, Owner )
VALUES  ( 1, -- ID - int
          N'chsakells blog', -- Name - nvarchar(100)
          N'http://chsakell.com', -- URL - nvarchar(200)
          N'Chris Sakellarios'  -- Owner - nvarchar(50)
          )

INSERT INTO dbo.Article
        ( ID ,
          Title ,
          Contents ,
          Author ,
          URL ,
          BlogID
        )
VALUES  ( 1 , -- ID - int
          N'WCF Dependency Injection' , -- Title - nvarchar(100)
          N'Dependency injection is a software design pattern that implements..' , -- Contents - nvarchar(max)
          N'Christos Sakellarios' , -- Author - nvarchar(50)
          N'https://chsakell.com/2015/07/03/dependency-injection-in-wcf/' , -- URL - nvarchar(200)
          1  -- BlogID - int
        )

Business.Services

Now that we have done with the Data access layer we can proceed implement the core WCF Services that are going to be actually hosted (from Web application or Self hosts in our NUnit Tests). Add references to Data.Core, Business.Entities projects and System.ServiceModel assembly as well. Create a folder named Contracts inside the Business.Services project and add the following Service Contracts

    [ServiceContract]
    public interface IBlogService
    {
        [OperationContract]
        void Add(Blog blog);

        [OperationContract]
        void Update(Blog blog);

        [OperationContract]
        void Delete(Blog blog);

        [OperationContract]
        Blog GetById(int id);

        Blog[] GetAll();
    }
    [ServiceContract]
    public interface IArticleService
    {
        [OperationContract]
        void Add(Article article);

        [OperationContract]
        void Update(Article article);

        [OperationContract]
        void Delete(Article article);

        [OperationContract]
        Article GetById(int id);

        [OperationContract]
        Article[] GetAll();
    }


At the root of this project create Services Implementations:

public class BlogService : IBlogService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IBlogRepository _blogRepository;

        public BlogService(IUnitOfWork unitOfWork, IBlogRepository blogRepository)
        {
            _unitOfWork = unitOfWork;
            _blogRepository = blogRepository;
        }
        public void Add(Blog blog)
        {
            _blogRepository.Add(blog);
            _unitOfWork.Commit();
        }

        public void Update(Blog blog)
        {
            _blogRepository.Update(blog);
            _unitOfWork.Commit();
        }

        public void Delete(Blog blog)
        {
            _blogRepository.Delete(blog);
            _unitOfWork.Commit();
        }

        public Blog GetById(int id)
        {
            return _blogRepository.GetById(id);
        }

        public Blog[] GetAll()
        {
            return _blogRepository.GetAll().ToArray();
        }
    }
public class ArticleService : IArticleService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IArticleRepository _articleRepository;

        public ArticleService(IUnitOfWork unitOfWork, IArticleRepository articleRepository)
        {
            _unitOfWork = unitOfWork;
            _articleRepository = articleRepository;
        }
        public void Add(Article article)
        {
            _articleRepository.Add(article);
            _unitOfWork.Commit();
        }

        public void Update(Article article)
        {
            _articleRepository.Update(article);
            _unitOfWork.Commit();
        }

        public void Delete(Article article)
        {
            _articleRepository.Delete(article);
            _unitOfWork.Commit();
        }

        public Article GetById(int id)
        {
            return _articleRepository.GetById(id);
        }

        public Article[] GetAll()
        {
            return _articleRepository.GetAll().ToArray();
        }
    }

Here we can see for the first time how Data repositories are going to be injected to WCF Services. For this to be completed we ‘ll have to register service’s implementations using an Inversion of Control container. We ‘ll do that later, both in Web host and self hosts using Autofac.

Core.Common

It’s time to create the client side of our SOA but before doing this, let’s create a base class for our client entities that will allow them to provide custom validation using the Fluent Validation. As we have already mentioned, client entities can have much more functionality than the Business Entities, functionality that may be required from client applications, web or desktop ones. For our example, we assume that clients needs to know if an entity is Valid or not based on custom validation rules for the specific client entity. In Core.Common install Fluent Validation through Nuget Packages and create the following class:

public class Validatable
    {
        protected IValidator _validator = null;
        protected IEnumerable<ValidationFailure> _validityErrors = null;

        protected virtual IValidator GetValidator()
        {
            return null;
        }

        public IEnumerable<ValidationFailure> ValidityErrors
        {
            get { return _validityErrors; }
            set { }
        }

        public void Validate()
        {
            if (_validator != null)
            {
                ValidationResult results = _validator.Validate(this);
                _validityErrors = results.Errors;
            }
        }

        public virtual bool IsValid
        {
            get
            {
                if (_validityErrors != null && _validityErrors.Count() > 0)
                    return false;
                else
                    return true;
            }
        }
    }

All that our Client Entities need to have is an override for the GetValidator() virtual method.

Client.Entities

Make sure you also install Fluent Validation package through Nuget Packages and create the following two files.

namespace Client.Entities
{
    public class Blog : Validatable, IExtensibleDataObject
    {
        #region Variables
        int _id;
        string _name;
        string _url;
        string _owner;
        #endregion

        #region Properties
        public int ID
        {
            get
            {
                return _id;
            }
            set { _id = value; }
        }
        public string Name
        {
            get
            {
                return _name;
            }
            set { _name = value; }
        }
        public string URL
        {
            get
            {
                return _url;
            }
            set { _url = value; }
        }
        public string Owner
        {
            get
            {
                return _owner;
            }
            set { _owner = value; }
        }

        #endregion

        #region IExtensibleDataObject

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion

        #region Validation
        protected override IValidator GetValidator()
        {
            return new BlogValidator();
        }
        #endregion
    }

    // Validator Class
    class BlogValidator : AbstractValidator<Blog>
    {
        public BlogValidator()
        {
            RuleFor(b => b.Name).NotEmpty();
            RuleFor(b => b.Owner).NotEmpty();
            RuleFor(b => b.ID).GreaterThan(0);
        }
    }
}
namespace Client.Entities
{
    public class Article : Validatable, IExtensibleDataObject
    {
        #region Variables
        int _id;
        string _title;
        string _contents;
        string _author;
        string _url;
        int _blogID;
        #endregion

        #region Properties
        public int ID
        {
            get
            {
                return _id;
            }
            set { _id = value; }
        }
        public string Title
        {
            get
            {
                return _title;
            }
            set { _title = value; }
        }
        public string Contents
        {
            get
            {
                return _contents;
            }
            set { _contents = value; }
        }
        public string Author
        {
            get
            {
                return _author;
            }
            set { _author = value; }
        }
        public string URL
        {
            get
            {
                return _url;
            }
            set { _url = value; }
        }
        public int BlogID
        {
            get
            {
                return _blogID;
            }
            set { _blogID = value; }
        }
        #endregion

        #region IExtensibleDataObject

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion

        #region Validation
        protected override IValidator GetValidator()
        {
            return new ArticleValidator();
        }
        #endregion
    }

    // Validator Class
    class ArticleValidator : AbstractValidator<Article>
    {
        public ArticleValidator()
        {
            RuleFor(a => a.Author).NotEmpty();
            RuleFor(a => a.BlogID).GreaterThan(0);
            RuleFor(a => a.Contents).NotEmpty();
        }
    }
}

I believe it’s getting clearer now what we are trying to achieve. Those two classes will be used for serializing WCF call results. Clients won’t be tightly bound to WCF Business side entities. The next thing to do, is to create the relevant Client side Service Contracts that use Client Entities.

Client.Contracts

Switch to Client.Contracts and add references to Client.Entities and Core.Common projects. You also need to add reference to System.ServiceModel assembly. Create the following two contracts:

namespace Client.Contracts
{
    [ServiceContract]
    public interface IBlogService
    {
        [OperationContract]
        void Add(Blog blog);

        [OperationContract]
        void Update(Blog blog);

        [OperationContract]
        void Delete(Blog blog);

        [OperationContract]
        Blog GetById(int id);

        Blog[] GetAll();
    }
}
namespace Client.Contracts
{
    [ServiceContract]
    public interface IArticleService
    {
        [OperationContract]
        void Add(Article article);

        [OperationContract]
        void Update(Article article);

        [OperationContract]
        void Delete(Article article);

        [OperationContract]
        Article GetById(int id);

        [OperationContract]
        Article[] GetAll();
    }
}

Seems familiar? Of course it does, it’s the same contracts we added in the business side of our SOA except that uses Client Entities.

Client.Proxies

We have created the client service contracts but we haven’t provided yet implementations for those interfaces. Guess what, the classes that will implement those contracts are the WCF Client proxies. Switch to Client.Proxies and add references to Client.Entities, Core.Common, Client.Contracts projects and System.ServiceModel assembly. Now provide the following implementations:

public class BlogClient : ClientBase<IBlogService>, IBlogService
    {
        #region IBlogService implementation
        public void Add(Blog blog)
        {
            Channel.Add(blog);
        }

        public void Update(Blog blog)
        {
            Channel.Update(blog);
        }

        public void Delete(Blog blog)
        {
            Channel.Delete(blog);
        }

        public Blog GetById(int id)
        {
            return Channel.GetById(id);
        }

        public Blog[] GetAll()
        {
            return Channel.GetAll();
        }
        #endregion

        public void CleanUp()
        {
            try
            {
                if (base.State != CommunicationState.Faulted)
                    base.Close();
                else
                    base.Abort();
            }
            catch (Exception ex)
            {
                base.Abort();
            }
        }
    }
public class ArticleClient : ClientBase<IArticleService>, IArticleService
    {
        #region IArticleService Members
        public void Add(Article article)
        {
            Channel.Add(article);
        }

        public void Update(Article article)
        {
            Channel.Update(article);
        }

        public void Delete(Article article)
        {
            Channel.Delete(article);
        }

        public Article GetById(int id)
        {
            return Channel.GetById(id);
        }

        public Article[] GetAll()
        {
            return Channel.GetAll();
        }
        #endregion

        public void CleanUp()
        {
            try
            {
                if (base.State != CommunicationState.Faulted)
                    base.Close();
                else
                    base.Abort();
            }
            catch (Exception ex)
            {
                base.Abort();
            }
        }
    }

Business Entities – Client Entities Deserialization

So far so good, but yet we haven’t completed all the missing parts relating to Deserializing Client entities to Business Entities and vise versa. Take a look at the Article entity in both sides, Client and Business. First of all notice that Business Article has DataContract and DataMember attributes to inform what parts of the class will be used for serialization. There isn’t such thing in the client side entity which means that all public properties will be used for serialization. I made that choice in order not to pollute the class since I may add even more functionality later. More over, If you are familiar with WCF Services, you will be aware that client and service data contracts must have the same namespace otherwise the deserialization will fail. At this point our client and business entities belong to different namespaces so it won’t work. We could add a namespace attribute value in the business entities like this:

[DataContract(Namespace="http://www.chsakell.com/WCF_DI")]

But this would require to insert the same attributes in the client side which is something I ‘d like to avoid. To solve this, there is a trick you can do, and register the common namespace for those entities. Switch to the AssemblyInfo.cs file in both of the projects and register there the namespace you want to be used for serialization-deserialization during WCF calls, by adding the following entries.

[assembly: ContractNamespace("http://www.chsakell.com/WCF_DI",
                              ClrNamespace = "Business.Entities")]
[assembly: ContractNamespace("http://www.chsakell.com/WCF_DI",
                              ClrNamespace = "Client.Entities")]

WCF Web Host

Switch to WCF.DependencyInjection.Web Web application to host our WCF Services. You need to add references to Business.Services, Data.Core projects and System.ServiceModel assembly. You also need to install Entity Framework and Autofac.Integration.Wcf packages from Nuget Packages as well.
Add two WCF Service files named BlogService.svc and ArticleService. Make sure you remove the *.svc.cs file created for each service and just leave the .svc file. Alter those files as follow:

<%@ ServiceHost
    Language="C#"
    Debug="true"
    Service="Business.Services.Contracts.IBlogService, Business.Services"
    Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>
<%@ ServiceHost
    Language="C#"
    Debug="true"
    Service="Business.Services.Contracts.IArticleService, Business.Services"
    Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>

It’s time to register our services and implementations using Autofac in order for Dependency Injection to work. Create the following Bootstrapper class:

public static class Bootstrapper
    {
        /// <summary>
        /// Configures and builds Autofac IOC container.
        /// </summary>
        /// <returns></returns>
        public static IContainer BuildContainer()
        {
            var builder = new ContainerBuilder();

            // register services

            builder.RegisterType<BlogService>().As<IBlogService>();
            builder.RegisterType<ArticleService>().As<IArticleService>();

            // register repositories and UnitOfWork
            builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
            builder.RegisterType<DbFactory>().As<IDbFactory>();
            builder.RegisterType<ArticleRepository>().As<IArticleRepository>();
            builder.RegisterType<BlogRepository>().As<IBlogRepository>();

            // build container
            return builder.Build();
        }
    }

You need to register this container in Application start event so create a Global.asax file (if not exists) and paste the following code that uses Autofac.Integration.Wcf.

protected void Application_Start(object sender, EventArgs e)
        {
            IContainer container = Bootstrapper.BuildContainer();
            AutofacHostFactory.Container = container;
        }

Don’t forget to add a connection string in Web.config file that points to WCFDI database we created before, to be used by Entity Framework.

  <connectionStrings>
    <add name="DIContext" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=WCFDI;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

At this point you should be able to use dependency injection concerning injecting data repositories to your WCF services.

Test.Proxies

We have done a lot of work structuring our SOA application and now it’s time to test all of our components. We need to test that data repositories are indeed injected and of course view how to configure proxy dependency injection. Switch to Test.Proxies project and for start add references to all of the other projects except the web application. Now install the following Nuget packages:

  1. Autofac WCF Integration
  2. Moq (We ‘ll mock the data repositories)
  3. NUnit
  4. NUnit Test Adapter

Also add reference to System.ServiceModel assembly.
Before creating the relevant Bootstrapper to be used by WCF self hosts, let’s create a class that needs WCF proxies to make WCF calls. Those proxies will have to be injected by Autofac container rather than be manually created.

public class ClientInjectionClass : Disposable
    {
        public Client.Contracts.IBlogService _blogProxy;
        public Client.Contracts.IArticleService _articleProxy;
        public ClientInjectionClass(Client.Contracts.IArticleService articleServiceProxy,
            Client.Contracts.IBlogService blogServiceProxy)
        {
            this._blogProxy = blogServiceProxy;
            this._articleProxy = articleServiceProxy;
        }

        #region IDisposable
        protected override void DisposeCore()
        {
            base.DisposeCore();
            try
            {
                (_blogProxy as Client.Proxies.BlogClient).CleanUp();

                (_articleProxy as Client.Proxies.ArticleClient).CleanUp();
            }
            catch
            {
                _blogProxy = null;
                _articleProxy = null;
            }
        }
        #endregion

        #region Methods

        public Client.Entities.Article[] GetArticles()
        {
            return _articleProxy.GetAll();
        }

        public Client.Entities.Blog GetBlogById(int id)
        {
            return _blogProxy.GetById(id);
        }

        #endregion
    }

Let’s view the Bootstrapper which will mock data repositories, register WCF Services, Client contracts with their respective implementations.

public static class Bootstrapper
    {/// <summary>
        /// Configures and builds Autofac IOC container.
        /// </summary>
        /// <returns></returns>
        public static IContainer BuildContainer()
        {
            var builder = new ContainerBuilder();

            // register services
            builder.RegisterType<BlogService>().As<Business.Services.Contracts.IBlogService>();
            builder.RegisterType<ArticleService>().As<Business.Services.Contracts.IArticleService>();

            // register proxies
            builder.Register(c => new ChannelFactory<Client.Contracts.IArticleService>("BasicHttpBinding_IArticleService"))
                .InstancePerLifetimeScope();
            builder.Register(c => new ChannelFactory<Client.Contracts.IBlogService>("BasicHttpBinding_IBlogService"))
                .InstancePerLifetimeScope();

            builder.RegisterType<ArticleClient>().As<Client.Contracts.IArticleService>().UseWcfSafeRelease();
            builder.RegisterType<BlogClient>().As<Client.Contracts.IBlogService>().UseWcfSafeRelease();

            // Unit of Work
            var _unitOfWork = new Mock<IUnitOfWork>();
            builder.RegisterInstance(_unitOfWork.Object).As<IUnitOfWork>();
            // DbFactory
            var _dbFactory = new Mock<IDbFactory>();
            builder.RegisterInstance(_dbFactory.Object).As<IDbFactory>();

            //Repositories
            var _articlesRepository = new Mock<IArticleRepository>();
            _articlesRepository.Setup(x => x.GetAll()).Returns(new List<Business.Entities.Article>()
                {
                    new Business.Entities.Article() {
                        ID = 1,
                        Author = "Chris Sakellarios",
                        BlogID = 1,
                        Contents = "Dependency injection is a software design pattern that implements..",
                        Title = "WCF Dependency Injection",
                        URL =" https://chsakell.com/2015/07/03/dependency-injection-in-wcf/"}
                });
            builder.RegisterInstance(_articlesRepository.Object).As<IArticleRepository>();

            var _blogsRepository = new Mock<IBlogRepository>();
            _blogsRepository.Setup(x => x.GetById(It.IsAny<int>()))
                .Returns(new Func<int, Business.Entities.Blog>(
                    id => new Business.Entities.Blog() {
                        ID = id,
                        Name = "chsakell's blog",
                        Owner = "Chris Sakellarios",
                        URL = "https://chsakell.com/"
                    }));
            builder.RegisterInstance(_blogsRepository.Object).As<IBlogRepository>();

            builder.RegisterType<ClientInjectionClass>();

            // build container
            return builder.Build();
        }
    }

I have highlighted the parts that we register client side service contracts to used for WCF proxy injection because when this is done, clients will search for the respective Endpoint configurations in the configuration file. To make this work, add an App.config file in the Test.Proxies project and paste the following code.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IArticleService" />
        <binding name="BasicHttpBinding_IBlogService" />
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:18850/ArticleService.svc"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IArticleService"
          contract="Client.Contracts.IArticleService" name="BasicHttpBinding_IArticleService" />
      <endpoint address="http://localhost:18850/BlogService.svc"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IBlogService"
          contract="Client.Contracts.IBlogService" name="BasicHttpBinding_IBlogService" />
    </client>
  </system.serviceModel>
</configuration>

Create a ProxiesTests.cs to write our NUnit Tests. First of all we need to Setup the self host and the IoC Container before each of our test runs. At the end of the test we can close the host. for those two we ‘ll use NUnit’s Setup and TearDown attributes.

    [TestFixture]
    public class ProxiesTests
    {
        IContainer container = null;
        ServiceHost svcArticleHost = null;
        ServiceHost svcBlogHost = null;
        Uri svcArticleServiceURI = new Uri("http://localhost:18850/ArticleService.svc");
        Uri svcBlogServiceURI = new Uri("http://localhost:18850/BlogService.svc");


        [SetUp]
        public void Setup()
        {
            try
            {
                container = Bootstrapper.BuildContainer();

                svcArticleHost = new ServiceHost(typeof(ArticleService), svcArticleServiceURI);
                svcBlogHost = new ServiceHost(typeof(BlogService), svcBlogServiceURI);

                svcArticleHost.AddDependencyInjectionBehavior<Business.Services.Contracts.IArticleService>(container);
                svcBlogHost.AddDependencyInjectionBehavior<Business.Services.Contracts.IBlogService>(container);

                svcArticleHost.Open();
                svcBlogHost.Open();
            }
            catch (Exception ex)
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
        }

        [TearDown]
        public void TearDown()
        {
            try
            {
                if (svcArticleHost != null && svcArticleHost.State != CommunicationState.Closed)
                    svcArticleHost.Close();

                if (svcBlogHost != null && svcBlogHost.State != CommunicationState.Closed)
                    svcBlogHost.Close();
            }
            catch (Exception ex)
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
            finally
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
        }
}

Before running tests, make sure you run Visual Studio as administrator for self host to work. Let’s write a test to check that self host works as expected:

        [Test]
        public void test_self_host_connection()
        {
            Assert.That(svcArticleHost.State, Is.EqualTo(CommunicationState.Opened));
            Assert.That(svcBlogHost.State, Is.EqualTo(CommunicationState.Opened));
        }

Test that ArticleClient and BlogClient proxies are injected properly when resolving Client.Contracts.IArticleService and Client.Contracts.IBlogService client service contracts:

        [Test]
        public void test_article_proxy_is_injected()
        {
            using (var lifetime = container.BeginLifetimeScope())
            {
                Client.Contracts.IArticleService proxy
                = container.Resolve<Client.Contracts.IArticleService>();

                Assert.IsTrue(proxy is ArticleClient);
            }
        }

        [Test]
        public void test_blog_proxy_is_injected()
        {
            using (var lifetime = container.BeginLifetimeScope())
            {
                Client.Contracts.IBlogService proxy
                = container.Resolve<Client.Contracts.IBlogService>();

                Assert.IsTrue(proxy is BlogClient);
            }
        }

Test proxy state expected behavior:

        [Test]
        public void test_article_proxy_state()
        {
            Client.Contracts.IArticleService proxy;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                CommunicationState state = (proxy as ArticleClient).State;
                Assert.That(state, Is.EqualTo(CommunicationState.Created));

                // Open connection
                (proxy as ArticleClient).Open();
                Assert.That((proxy as ArticleClient).State, Is.EqualTo(CommunicationState.Opened));
                // Close connection
                (proxy as ArticleClient).Close();
                Assert.That((proxy as ArticleClient).State, Is.EqualTo(CommunicationState.Closed));
            }
        }

Test data repositories injection in WCF Services:

        [Test]
        public void test_article_proxy_getall()
        {
            Client.Contracts.IArticleService proxy;
            Client.Entities.Article[] articles = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                articles = proxy.GetAll();
            }

            Assert.That(articles.Count(), Is.EqualTo(1));

            // Close connection
            if ((proxy as ArticleClient).State == CommunicationState.Opened)
                (proxy as ClientBase<Client.Contracts.IArticleService>).Close();
        }

Test that proxies are injected and cleaned properly in ClientInjectionClass.

        [Test]
        public void test_constructor_injected_proxy()
        {
            ClientInjectionClass _testClass = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                using (_testClass = new ClientInjectionClass(container.Resolve<Client.Contracts.IArticleService>(),
                   container.Resolve<Client.Contracts.IBlogService>()))
                {
                    {
                        Client.Entities.Article[] _articles = _testClass.GetArticles();
                        Client.Entities.Blog _blog = _testClass.GetBlogById(1);

                        Assert.That(_articles.Count(), Is.EqualTo(1));
                        Assert.That(_blog, Is.Not.Null);
                        Assert.That(_blog.IsValid, Is.EqualTo(true));
                    }
                }
            }

            Assert.That((_testClass._articleProxy as ArticleClient).State, Is.EqualTo(CommunicationState.Closed));
            Assert.That((_testClass._blogProxy as BlogClient).State, Is.EqualTo(CommunicationState.Closed));
        }

In case you have noticed, I added an extra property named ContentLength at the Business Article entity, a property that doesn’t exist in the client Article entity. Still serialization – deserialization works like a charm due to the IExtensibleDataObject interface the both implement. Let’s write a test to view that Article client holds this property in it’s ExtensionData property. To read this property we need a method that uses reflection and you can view it from here.

        [Test]
        public void test_article_extension_data_not_empty()
        {
            Client.Contracts.IArticleService proxy;
            Client.Entities.Article[] articles = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                articles = proxy.GetAll();
            }

            Assert.That(articles.Count(), Is.EqualTo(1));

            var contentLength = Extensions.GetExtensionDataMemberValue(articles.First(), "ContentLength");

            Assert.That(articles.First().Contents.Length, Is.EqualTo(Int32.Parse(contentLength.ToString())));
        }

Discussion

We have seen many interesting concepts in this article, from WCF Dependency Injection integration and client proxy injection to de-coupled and extensible Data Contracts. There is one thing I would like to discuss here and it has to do about dependency injection in constructors. Days ago, a fellow asked me what if a class required ten or even twenty repositories? Do I have to pollute my constructor that much? The answer is not necessary. There is another more abstract and generic pattern that you can use where you create a generic Factory capable of resolving custom IDataRepository interfaces. In other words, you can create an interface like this:

public interface IDataRepositoryFactory
    {
        T GetCustomDataRepository<T>() where T : IDataRepository;
    }

And then implement it as follow:

public class DataRepositoryFactory : IDataRepositoryFactory
    {
        T IDataRepositoryFactory.GetCustomDataRepository<T>()
        {
            return Container.Resole<T>();
        }
    }

Then all you have to do is register this high level interface using an IoC and use this in your constructors instead of custom ones. When you need a custom repository, for example an IArticleRepository you get it like this:

private IArticleRepository _articleRepository;
private IBlogRepository _blogRepository;
private IDataRepositoryFactory _repositoryFactory;

// constructor
public ArticleService(IDataRepositoryFactory repositoryFactory)
        {
            _repositoryFactory= repositoryFactory;
        }

// method that requires IArticleRepository
public Article[] GetAll()
{
  _articleRepository = _repositoryFactory.GetCustomDataRepository<IArticleRepository>();
  return _articleRepository.GetAll();
}
// method that requires IBlogRepository
public Blog[] GetAll()
{
  _blogRepository= _repositoryFactory.GetCustomDataRepository<IBlogRepository>();
  return _blogRepository.GetAll();
}

That’s it, we ‘re done! I hope you enjoyed reading this post as much as I did writing it. You can download the source code for this post here.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small


Viewing all articles
Browse latest Browse all 43

Trending Articles