In this article, we will demonstrate a WCF implementation using Spring.NET. This will enable us to utilize the IoC container features supporting Dependency Injection, so we can inject the dependencies required to instantiate and initialize the service.

In previous articles, we built POCO (Plain Old CLR Object) services to represent the business tier and slightly more complex objects for the data tier. This is still the recommended architecture for enterprise services when integration and interoperability are not required. A distributed/service oriented design will add overhead and require additional resources to support. If you intend to publish your services for external partners or integrate legacy systems then WCF is a great solution. 

I still recommend separation and building POCO services as discussed under previous articles. A POCO service is far more reusable than a highly decorated and framework dependent object. We can leverage WCF to expose the services to a variety of consumer applications without the burden of building all the plumbing. The following are some common WCF terms.

  • Endpoints – a network resource accessible by clients to communicate based on agreed contracts.
  • Bindings – the how to communicate with the endpoint. The lowest level is the transport to exchange messages over the network. The built-in transports include HTTP, TCP, Names Pipes and MSMQ. The parent levels include security and transactions.
  • Contracts – the contracts define the features exposed by the endpoint, which includes the message/data format and operations.
Contracts

The first step is create the service contract, which defines the service operations. The simplest approach is mark your service interface class with the ServiceContract attribute and the operation methods with the OperationContract attribute. In the example below, we decorated the IContactsService class with the appropriate attributes for a service contract.

01
using Joemwalton.ContactManager.Common.Model;
02
namespace Joemwalton.ContactManager.Common.Services
03
{
04
    [ServiceContract]
05
    public interface IContactsService
06
    {
07
        [OperationContract]
08
        void DeleteContact(int id);
09
        [OperationContract]
10
        Contact GetContact(int id);
11
        [OperationContract]
12
        IList<Contact> GetContacts();
13
        [OperationContract]
14
        int SaveContact(Contact contact);
15
    }
16
}

The above class will expose the DeleteContact, GetContact, GetContacts and SaveContact operations. The next step is defining the Data Contracts. The DataContract attribute will mark the classes that represent the message/data format for the operations. The Data Contract will instruct the framework to represent the marked items as an XSD, which is included in the WSDL exposed for consumers discovery. The DataMember attribute identifies the included class members.

Alternatively, types marked with the Serializable attribute and members not marked with NonSerialized will be serialized. The default DataContactSerializer will also serialize types and generate the WSDL for all CLR primitives (e.g. int32, string), arrays, collections and enumerations. Please see the Microsoft documentation for additional types supported by the serializer. In our example, we will add Serializable to the Contact class and leverage the supported primitives.

01
namespace Joemwalton.ContactManager.Common.Model
02
{
03
    [Serializable]
04
    public abstract class ModelBase
05
    {
06
        public virtual bool IsValid
07
        {
08
            get { return true; }
09
        }
10
    }
11
}
12
 
13
namespace Joemwalton.ContactManager.Common.Model
14
{
15
    /// <summary>
16
    /// Contact domain object
17
    /// </summary>
18
    ///
19
    [Table(Name="Contacts")]
20
    public class Contact : ModelBase
21
    {
22
 
23
        public Contact() { }
24
 
25
        public Contact(string name,
26
                       string emailAddress,
27
                       string mobilePhone)
28
        {
29
            Name = name;
30
            EmailAddress = emailAddress;
31
            MobilePhone = mobilePhone;
32
        }
33
 
34
        public Contact(int id,
35
                       string name,
36
                       string emailAddress,
37
                       string mobilePhone)
38
        {
39
            Id = id;
40
            Name = name;
41
            EmailAddress = emailAddress;
42
            MobilePhone = mobilePhone;
43
        }
44
 
45
        public Contact(int id,
46
                       string name,
47
                       string emailAddress,
48
                       string mobilePhone,
49
                       byte[] picture)
50
        {
51
            Id = id;
52
            Name = name;
53
            EmailAddress = emailAddress;
54
            MobilePhone = mobilePhone;
55
            Picture = picture;
56
        }
57
 
58
        private int _Id = -1;
59
        public int Id 
60
        {
61
            get { return _Id; }
62
            set { _Id = value; }
63
        }
64
 
65
        public string Name { get; set; }
66
 
67
        public string EmailAddress { get; set; }
68
 
69
        public string MobilePhone { get; set; }
70
 
71
        public byte[] Picture { get; set; }
72
 
73
        public override bool IsValid
74
        {
75
            get { return true; }
76
        }
77
     }
78
}
79

WCF Service

The next step is implementing the service, which as we discussed previously will be a wrapper exposing our concrete service. The following is the ContactsService concrete implementation, which is the same POCO service we are using for other enterprise applications.

01
using Joemwalton.ContactManager.Common.Model;
02
using Joemwalton.ContactManager.Common.Repository;
03
 
04
namespace Joemwalton.ContactManager.Common.Services
05
{
06
    public class ContactsService : IContactsService
07
    {
08
        private IContactRepository _contactRepository;
09
        private INotificationService _notificationService;
10
 
11
        public ContactsService() { }
12
 
13
        //dependencies are now injected using the constructor
14
        public ContactsService(IContactRepository contactRepository,
15
                               INotificationService notificationService)
16
        {
17
            _contactRepository = contactRepository;
18
            _notificationService = notificationService;
19
        }
20
 
21
        public int SaveContact(Contact contact)
22
        {
23
            //code is simple and easy to understand!
24
            int id = _contactRepository.Save(contact);
25
            _notificationService.Send(contact);
26
            return id;
27
        }
28
 
29
        public IList<Contact> GetContacts()
30
        {
31
            return _contactRepository.FindAllContacts();
32
        }
33
 
34
        public Contact GetContact(int id)
35
        {
36
            return _contactRepository.FindById(id);
37
        }
38
 
39
        public void DeleteContact(int id)
40
        {
41
            _contactRepository.Remove(id);
42
        }
43
    }
44
}
45

First, add a new WCF Service to your project. You have an option to create a WCF service written in code or configuration. We will select the configuration option, which requires the addition of the ServiceModel configuration section. This will configure the endpoint including the address, binding and contract. The following is the web.config configuration section.

01
  <system.serviceModel>
02
      <services>
03
        <service name="ContactsService"
04
                 behaviorConfiguration="defaultBehavior">
05
          <host>
06
            <baseAddresses>
07
              <add baseAddress="http://localhost/ContactManager"/>
08
            </baseAddresses>            
09
          </host>
10
          <endpoint address=""
11
                    binding="basicHttpBinding"
12
                    contract="Joemwalton.ContactManager.Common.Services.IContactsService"/>
13
          <endpoint address="mex"
14
                    binding="mexHttpBinding"
15
                    contract="IMetadataExchange"/>
16
        </service>
17
      </services>  
18
      <behaviors>
19
            <serviceBehaviors>
20
                <behavior name="defaultBehavior">
21
                    <serviceMetadata httpGetEnabled="true" />
22
                    <serviceDebug includeExceptionDetailInFaults="true" />
23
                </behavior>
24
            </serviceBehaviors>
25
        </behaviors>
26
     <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
27
  </system.serviceModel>
28

The above ContactsService service configuration defines 2 endpoints. The first is the basicHttpBinding referencing our Joemwalton.ContactManager.Common.Services.IContactsService contract, which defines the service operations. This is not the concrete implementation, so this we will address in the next section. The basicHttpBinding is compatible with ASMX. The second is the MEX endpoint, which provides the meta-data information describing the service to consumers. The MEX endpoint is not exposed by default, so you can add this configuration. As you can see, we can add multiple service endpoints to provide the flexibility to communicate with a variety of consumers.

Since we selected a configuration-based WCF client, the svc file will be essentially empty (i.e. no code-behind for the service implementation). The following is the svc file, which you can edit the markup to include Spring.NET WCF configuration.

1
<%@ ServiceHost Language="C#" 
2
                Debug="true"
3
                Service="ContactsService"
4
                Factory="Spring.ServiceModel.Activation.ServiceHostFactory" %>

The 2 key attributes are Service and Factory. The Service attribute is the IoC object name of the concrete ContactsService implementation. The Factory attribute is set to the Spring.NET ServiceHostFactory, which provides the IoC Dependency Injection features. In this scenario, the factory will request an instance of the ContactsService object from the Spring.NET IoC container. You must set the Service attribute and IoC object name/id to the same value, which is necessary for the container to locate the correct object.

The next step is providing the instructions to the IoC container for wiring the objects, so the ServiceHostFactory returns the ContactsService object already initialized with all dependencies injected. In this case, the IContactRepository and INotificationService objects. The following is the XML meta-data defining the object for the IoC container.

01
<?xml version="1.0" encoding="utf-8" ?>
02
  <objects xmlns="http://www.springframework.net">
03
 
04
    <object id="EmailNotificationService"
05
            type="Joemwalton.ContactManager.Common.Services.EmailService, Joemwalton.ContactManager.Common">
06
      <constructor-arg name="smtpHost"  value="${Smtp.Host}" />
07
      <constructor-arg name="sender"    value="${Email.Sender}" />
08
      <constructor-arg name="subject"   value="Hello!" />
09
      <constructor-arg name="message"   value="Hello Email Contact!" />
10
    </object>
11
 
12
    <object id="ContactsService"
13
            type="Joemwalton.ContactManager.Common.Services.ContactsService, Joemwalton.ContactManager.Common"
14
            singleton="false">
15
      <constructor-arg name="contactRepository">
16
        <object type="Joemwalton.ContactManager.Common.Repository.SqlContactRepository, Joemwalton.ContactManager.Common" />
17
      </constructor-arg>
18
      <constructor-arg name="notificationService"
19
                       ref="EmailNotificationService" />
20
    </object>
21
  </objects>
22

The ServiceHostFactory will request the ContactsService object from IoC container by name/id. The container will instantiate the object based on the XML meta-data above, which references the type as ContactsService. The constructor is provided an instance of the SqlContactRepository and EmailNotificationService reference. The second constructor argument is a reference to another object, which is also defined in the XML meta-data as EmailNotificationService. If you are reading this post before the article describing the design patterns then I recommend reviewing the previous series for additional information.

Hosting

We basically built our WCF service and leveraged the Spring.NET IoC container to instantiate the service using WCF Service Host extensibility. Since we are exposing HTTP endpoint bindings, we will select IIS as the service host environment. The ContactManager.Soa.Server project, which will host our service under IIS. The web project will include the svc, XML meta-data and web.config. We will configure the web project to create a virtual directory and host under local IIS, so we can call the service locally for testing.

This configuration will allow us to access the service using the Project URL and service name. The following browser image represents the result when calling the service.

You can view the WSDL by clicking the link, which returns the following. As discussed previously, the WSDL provides the instructions for consumers to properly interact with your service.

Summary

In summary, we created a WCF service as a wrapper to an existing service. The POCO service is a concrete implementation containing the business logic to fulfill the request and respond appropriately. In our example, the service will call the Repository to persist data and a notification service to send email messages. We can still call the POCO service directly within the enterprise, but WCF offers a distributed architecture with options to communicate with external clients. Since we followed our core design patterns and principles, we introduced Spring.NET to provide an IoC container to instantiate and initialize our service. We are able to preserve all the design benefits of our non-distributed enterprise applications.

The following are a few additional items to consider.

  • The example is a standard synchronous request-response pattern with the client blocked, so consider an asynchronous request-response pattern for longer running service operations. One-way and duplex are also options.
  • Although HTTP is the most popular transport protocol, consider TCP and Named Pipes for a .NET only environment. The TCP and Named Pipes provide a performance boost over HTTP. 
  • IIS6 is a great hosting environment for HTTP based service endpoints, although Windows Process Activation Services (WAS)/II7 and a Managed Windows Service provide support for non-HTTP protocols.
  • Expose multiple endpoints to support legacy and non-.NET consumers (i.e. Java).
  • WCF provides many bindings including Web Services based on WS-I Basic Profile 1.1 (i.e. SOAP 1.1, WSDL 1.1) and the more advanced WS-* specifications.
  • Text encoding is standard, but binary and MTOM are options to reduce the payload and improve performance. You can define the encoding in the bindings. Check binding encoding restrictions.
  • Don’t forget about security for sensitive and confidential data!!! Transport and Message-based security are supported. The transport deals with protecting data over the wire, where SSL is the most common. Client Authentication is available with Windows credentials, certificates, SAML tokens and user/password secret.
  • Set the service XML meta-data object singleton attribute to false, so the IoC container creates a new instance.
  • Refer to the source code download for additional configuration and settings required for this solution not covered in the article.
Source Code

The source code for the solution contains several projects, which provide various scenarios and examples. The following is a brief summary of the projects.

  • ContactManager.Common – includes domain objects, repository and POCO services. The SqlContactRepository, LinqContactRepository and MemoryContactRepository are three Contact Repository concrete implementations. 
  • ContactManager.Soa.Server – includes the WCF service host with Spring.NET IoC support.
  • ContactManager.Soa.Client – includes the WCF service client.
  • ContactManager.Web – an MVC version of the Contact Manager UI with Spring.NET IoC support, which is currently setup to call the ContactManager.Common ContactsService POCO service. The ContactsService is currently configured to call the SqlContactRepository.
  • ContactManager.WinForm – a WinForm version of the Contact Manager UI, which is currently setup to call the ContactManager.Soa.Client. The ContactManager.Soa.Server is configured to call the LinqContactRepository using Spring.NET. 

You can download the source code and setup a local SQL Server database using the create scripts in the sql folder. You can also easily modify the Contact Repository implementation via the IoC container XML meta-data. The MemoryContactRepository is available for testing when you do not have a persistent data store available (e.g. SQL Server).

What’s Next?

I will follow-up with part 2 to discuss the service client setup and configuration, which does not require any external libraries or processes. The service host will handle the business and data tier, so the service client is a standard implementation.

I am also planning a Windows Workflow Foundation (WF) article as an alternative implementation for the business tier. In our example, the ContactsService method calls the Repository and NotificationService objects to fulfill the business requirement. We will replace this implementation with a workflow encapsulating this process and inject business rules into the workflow, which will also be exposed as a WCF service. If you have worked with Team Foundation Server (TFS) builds then you will be familiar with workflows.

I hope this article provides a WCF implementation, which follows the design patterns and principles for enterprise systems.

Source Code