http://lourenco.co.za/blog/2013/08/wcf-windows-service-using-topshelf-and-servicemodelex/

There are two excellent .NET libraries that help us to build enterprise solutions using the Windows Communication Foundation (WCF)TopShelf and ServiceModelEx. These two libraries are responsible for two very different, but integral parts of a windows service:

  • TopShelf enables us to easily debug, install and host a long running process in Windows
  • ServiceModelEx provides functionality to configure how a WCF service behaves and provides its endpoints

This article follows on from my previous article about Service Oriented Architecture. I recommend that if you are just starting out with SOA, you give that a read-through before going through this implementation.

In case you skipped it, we basically want to implement the communication between an application and a service, using WCF:

What is a Service Interaction?

So what actually makes up a service interaction? There are four necessary elements:

  • A client
  • A service implementation
  • A service host
  • A contract, shared between the client and the service, that describes the interaction

A Little Bit About Contracts

We can think of the contract as an agreement between the client and service as to how they will be communicating.

Let’s look at a little analogy. People, for example, also have contracts! Consider a normal greeting interaction between two people meeting for the first time:

That makes sense! That is because Braden has an introduction contract (i.e. knows how to respond to a an introduction). In service terms, the contract would be:

  1. João (the client) sends Braden (the service) an introduction request, that contains a greeting, “Hi”, and his name, “João”.
  2. Braden accepts João’s introduction request, and returns an introduction response, that greets João with the same greeting, “Hi”, and in addition, his own name, “Braden”.

Without such a contract in place, you might imagine the following interaction:

That makes absolutely no sense! If a server is given a request that it has absolutely no idea what to do with, it will through an error (and that error might as well be gibberish).

Implementation Using TopShelf and ServiceModelEx

Seeing as we are looking at a greeting interaction, why don’t we see if we can implement this as a WCF Windows Service? Let’s get started!

The Visual Studio Solution

In Visual Studio, start with an empty solution. We need to add 4 projects to the solution:

  • Class Library for the contract (WcfService.Contract)
  • A Class Library for the service implementation (WcfService.Service)
  • A Console Application for the client (WcfService.Client)
  • Console Application for the service host (WcfService.WindowsService)

I know what you’re thinking: “Why have so many different projects, there are just two parts to this right?” I like to separate my implementations out so that there is as little code redundancy as possible. The reasons for this separation will (hopefully) become apparent by the end of this article.

Contract

We discussed how contracts describe what is necessary for an interaction to occur earlier. There are three parts to this:

  • Request
  • Action
  • Response

I touched on requests and responses a little earlier. I like to think of these as Data Transfer Objects, or abbreviated to DTOs. These are simple objects that are easily serializable and do not generally reflect the underlying domain objects or database entities. I recommend that you make use of such constructs as they will force you to think carefully about (and therefore limit) the amount of data that as actually sent down the line. The action is simply the method name.

In the contract project, let’s add two classes for our request and response classes – IntroductionRequest:

[DataContract]
public class IntroductionRequest
{
[DataMember]
public string Greeting { get; set; }

[DataMember]
public string Name { get; set; }

public override string ToString()
{
return string.Format("{0}, I'm {1}", Greeting, Name);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[DataContract]
public class IntroductionRequest
{
    [DataMember]
    public string Greeting { get; set; }
 
    [DataMember]
    public string Name { get; set; }
 
    public override string ToString()
    {
        return string.Format("{0}, I'm {1}", Greeting, Name);
    }
}

And IntroductionResponse:

[DataContract]
public class IntroductionResponse
{
[DataMember]
public string Greeting { get; set; }

[DataMember]
public string ClientName { get; set; }

[DataMember]
public string ServiceName { get; set; }

public override string ToString()
{
return string.Format("{0} {1}, my name is {2}", Greeting, ClientName, ServiceName);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[DataContract]
public class IntroductionResponse
{
    [DataMember]
    public string Greeting { get; set; }
 
    [DataMember]
    public string ClientName { get; set; }
 
    [DataMember]
    public string ServiceName { get; set; }
 
    public override string ToString()
    {
        return string.Format("{0} {1}, my name is {2}", Greeting, ClientName, ServiceName);
    }
}

Notice a few things about these classes:

  • The classes are decorated with the DataContract attribute
  • The properties are decorated with the DataMember attribute
  • You will need a reference to the System.Runtime.Serialization assembly that comes with the .NET environment

Let’s add the contract now – add a new interface called IIntroductionService:

[ServiceContract]
public interface IIntroductionService
{
[OperationContract]
IntroductionResponse Introduce(IntroductionRequest request);
}
1
2
3
4
5
6
[ServiceContract]
public interface IIntroductionService
{
    [OperationContract]
    IntroductionResponse Introduce(IntroductionRequest request);
}

Notice a few things about this interface:

  • The interface is decorated with the ServiceContract attribute
  • The method is decorated with the OperationContract attribute
  • You will need a reference to the System.ServiceModel assembly that comes with the .NET environment

Your contract project should now look something like this:

This is a common project that is shared between the client and the service. Remember how we discussed the fact that we don’t want our implementations being released to the whole world in my previous article about Service Oriented Architecture? They don’t have very much to go on, do they?

Service

This is the part of the project where we will implement that actual functionality that our service will provide. The service needs to know what contract it will be fulfilling, so let’s add a reference to the contract project.

Next, add a new class for the service implementation called IntroductionService. We want this class to implement the IIntroductionService in the contract:

[ServiceBehavior]
public class IntroductionService : IIntroductionService
{
[OperationBehavior]
public IntroductionResponse Introduce(IntroductionRequest request)
{
throw new NotImplementedException();
}
}
1
2
3
4
5
6
7
8
9
[ServiceBehavior]
public class IntroductionService : IIntroductionService
{
    [OperationBehavior]
    public IntroductionResponse Introduce(IntroductionRequest request)
    {
        throw new NotImplementedException();
    }
}

Notice a few things about this interface:

  • The class is decorated with the ServiceBehavior attribute
  • The method is decorated with the  OperationBehavior attribute
  • You will need a reference to the System.ServiceModel assembly that comes with the .NET environment

At the moment, our service might as well be replying “Bumblebee” to any request that it receives, because it’s not doing anything with it! So, let’s flesh out the implementation of the introduction:

[OperationBehavior]
public IntroductionResponse Introduce(IntroductionRequest request)
{
return new IntroductionResponse
{
Greeting = request.Greeting,
ClientName = request.Name,
ServiceName = "Braden"
};
}
1
2
3
4
5
6
7
8
9
10
[OperationBehavior]
public IntroductionResponse Introduce(IntroductionRequest request)
{
    return new IntroductionResponse
        {
            Greeting = request.Greeting,
            ClientName = request.Name,
            ServiceName = "Braden"
        };
}

And now we have implemented the business logic for our service!

Your service project should now look something like this:

Service Host

Simply having an implementation is not enough! We need to have a way to actually host and run our implementation. There are several ways to accomplish this. For this article, I have decided to create a windows service using TopShelf and ServiceModelEx.

Using the NuGet Package Manager, add the TopShelf package to the service host project. Unfortunately, ServiceModelEx is not available on NuGet, so you will need to download the source (don’t worry, it’s free) from the IDesign downloads page. If you don’t feel like compiling it yourself, I have done so for you and uploaded a release build here. Add this DLL as a reference in the project, along with:

  1. The .NET assemblies:

    • System.ServiceModel
    • System.ServiceModel.Channels
  2. The solution projects:
    • WcfService.Contract
    • WcfService.Service

In the service host project, add a new class called Host:

internal class Host
{
private ServiceHost<IntroductionService> _service;

internal Host()
{
Console.WriteLine("Setting up services...");
_service = new ServiceHost<IntroductionService>(new Uri[] { });
}

public void Start()
{
Console.WriteLine("Starting services...");
_service.Open();
Console.WriteLine("Started!");
}

public void Stop()
{
Console.WriteLine("Stopping services...");
try
{
if (_service != null)
{
if (_service.State == CommunicationState.Opened)
{
_service.Close();
}
}
Console.WriteLine("Stopped!");
}
catch (Exception ex)
{
Console.WriteLine("Could not stop: " + ex.Message);
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
internal class Host
{
    private ServiceHost<IntroductionService> _service;
 
    internal Host()
    {
        Console.WriteLine("Setting up services...");
        _service = new ServiceHost<IntroductionService>(new Uri[] { });
    }
 
    public void Start()
    {
        Console.WriteLine("Starting services...");
        _service.Open();
        Console.WriteLine("Started!");
    }
 
    public void Stop()
    {
        Console.WriteLine("Stopping services...");
        try
        {
            if (_service != null)
            {
                if (_service.State == CommunicationState.Opened)
                {
                    _service.Close();
                }
            }
            Console.WriteLine("Stopped!");
        }
        catch (Exception ex)
        {
            Console.WriteLine("Could not stop: " + ex.Message);
        }
    }
}

This little bit of code utilizes the functionality provided by ServiceModelEx to create endpoints for the IntroductionService implementation and exposes it using WCF.

We need to modify the service host’s Main method:

static void Main(string[] args)
{
Console.WriteLine("Introduction Service");
try
{
const string name = "IntroductionService";
const string description = "Introduction Service";
var host = HostFactory.New(configuration =>
{
configuration.Service<Host>(callback =>
{
callback.ConstructUsing(s => new Host());
callback.WhenStarted(service => service.Start());
callback.WhenStopped(service => service.Stop());
});
configuration.SetDisplayName(name);
configuration.SetServiceName(name);
configuration.SetDescription(description);
configuration.RunAsLocalService();
});
host.Run();
}
catch (Exception ex)
{
Console.WriteLine("Introduction Service fatal exception. " + ex.Message);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static void Main(string[] args)
{
    Console.WriteLine("Introduction Service");
    try
    {
        const string name = "IntroductionService";
        const string description = "Introduction Service";
        var host = HostFactory.New(configuration =>
        {
            configuration.Service<Host>(callback =>
            {
                callback.ConstructUsing(s => new Host());
                callback.WhenStarted(service => service.Start());
                callback.WhenStopped(service => service.Stop());
            });
            configuration.SetDisplayName(name);
            configuration.SetServiceName(name);
            configuration.SetDescription(description);
            configuration.RunAsLocalService();
        });
        host.Run();
    }
    catch (Exception ex)
    {
        Console.WriteLine("Introduction Service fatal exception. " + ex.Message);
    }
}

This code uses the Topshelf functionality to create a Windows service. It’s really great – when you run this console application with no parameters, it’s a simple console application! However, there are a few switches that enable you to easily deploy the Windows service:

  • WcfService.WindowsService.exe install installs the service on the machine
  • WcfService.WindowsService.exe start starts the service (once installed)
  • WcfService.WindowsService.exe stop stops the service (if it has started)
  • WcfService.WindowsService.exe uninstall removes the service from the machine

For the more complex commands, the Topshelf Command-Line Reference will help.

Together, the two libraries provide a WCF Windows Service!

Configuration

In order for the service host to accept incoming connections, we need to add a little configuration. As this is a Windows service, I am going to expose the service as a net.tcp endpoint. This basically means that the protocol that the service will be communicating over is TCP, and the net prefix means that it was designed solely for .NET implementations (i.e. I don’t care about interoperability). You could be asking, “Why not expose it over the HTTP protocol and use SOAP?” The HTTP protocol is an application-layer protocol that runs on top of TCP, and we would therefore have to run our own application-layer on top of another one – HTTP! This extra step results in the service being inherently slower!

The configuration requires two parts in our configuration – a binding and an endpoint. Let’s get to it! Add a new Application Configuration file to the service host project and call it App.config:

Paste the following XML into the App.config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="WcfService.Service.IntroductionService">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:50123/IntroductionService"/>
</baseAddresses>
</host>
<endpoint address=""
binding="netTcpBinding"
contract="WcfService.Contract.IIntroductionService" />
</service>
</services>
</system.serviceModel>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="WcfService.Service.IntroductionService">
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:50123/IntroductionService"/>
          </baseAddresses>
        </host>
        <endpoint address=""
                  binding="netTcpBinding"
                  contract="WcfService.Contract.IIntroductionService" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

So what are we doing here? Let’s break it down:

  1. Declaring a service with a name that exactly matches the service implementation’s namespace and class
  2. Assigning a base address to the service (i.e. each endpoint address declared will be appended to this base URI)
  3. Adding an endpoint to the service that uses the net.tcp protocol and promises to match the contract declared

Note: The address for the endpoint is empty, as this will be the default endpoint for this service.

Your service host project should look something like this:

At this point in time, we can run the service host project, and the console application should start up:

Testing the Service

There is a special tool that comes bundled with Visual Studio – a WCF Test Client. We can use this client to connect to our service to test it out! Unfortunately, this client (obviously), has no idea about the contract that we have just created! Fortunately for us, it is easy to get it to build its own contract! However, by default, our service does not give any of its secrets away! Not even its contract.

We need to make a couple of changes to our configuration file to allow us to test the service:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="WcfService.Service.IntroductionService" behaviorConfiguration="mexServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:50123/IntroductionService"/>
</baseAddresses>
</host>
<endpoint address=""
binding="netTcpBinding"
contract="WcfService.Contract.IIntroductionService" />
<endpoint address="mex"
binding="mexTcpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="mexServiceBehavior">
<serviceMetadata />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="WcfService.Service.IntroductionService" behaviorConfiguration="mexServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:50123/IntroductionService"/>
          </baseAddresses>
        </host>
        <endpoint address=""
                  binding="netTcpBinding"
                  contract="WcfService.Contract.IIntroductionService" />
        <endpoint address="mex"
                  binding="mexTcpBinding"
                  contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="mexServiceBehavior">
          <serviceMetadata />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

What have we changed? Let’s take a look:

  1. On line 5, we have configured the service to use a behavior, specified by name
  2. Lines 14-16 add a new endpoint to the service that allows metadata exchange (information about what contract the service will fulfill)
  3. Lines 19-25 configure the behavior we want to apply to the service

At this point, you can actually start the service host project and make test calls using the WCF Test Client that comes bundled with Visual Studio! The WcfTestClient.exe application can be found in the following folders:

  • C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE (for Windows 64-bit), or
  • C:\Program Files\Microsoft Visual Studio 11.0\Common7\IDE (for Windows 32-bit)

With the service host project running (make sure that you started it since modifying the App.config), start up the test client and click File → Add Service. Add the following connection to the client:

The client will then use the metadata exchange to add the service:

And finally, display the service that we have just implemented! Let’s give it a test. Select the Introduce() method, fill in the Greeting and Name values, check Start a new proxy and finally, click Invoke:

Exciting, it works! So now that we’ve tested our service out, it’s time to write our own client application!

Client

Firstly, we need to know how to communicate with our WCF Windows Service, so the client needs a reference to the contract project.

Next up, we need some sort of way to create a connection to the service. I like this little proxy object (it’s lightweight, tried and tested):

public class WcfProxy<TContract> : IDisposable
where TContract : class
{
public TContract Service { get; private set; }

public WcfProxy()
{
try
{
var factory = new ChannelFactory<TContract>(typeof(TContract).Name + "_Endpoint");
factory.Open();
Service = factory.CreateChannel();
}
catch (Exception ex)
{
Console.WriteLine("Could not create proxy: {0}", ex.Message);
Service = null;
}
}

public void Dispose()
{
if (Service != null)
{
var internalProxy = Service as ICommunicationObject;

try
{
if (internalProxy != null)
{
if (internalProxy.State != CommunicationState.Closed && internalProxy.State != CommunicationState.Faulted)
internalProxy.Close();
}
}
catch (Exception ex)
{
Console.WriteLine("Could not close proxy: {0}", ex.Message);
try
{
if (internalProxy != null)
internalProxy.Abort();
}
catch (Exception exInternal)
{
Console.WriteLine("Could not abort proxy: {0}", exInternal.Message);
}
}

if (internalProxy is IDisposable)
{
try
{
if (internalProxy.State != CommunicationState.Faulted)
(internalProxy as IDisposable).Dispose();
}
catch (Exception ex)
{
Console.WriteLine("Could not dispose proxy: ", ex.Message);
}
}
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class WcfProxy<TContract> : IDisposable
    where TContract : class
{
    public TContract Service { get; private set; }
 
    public WcfProxy()
    {
        try
        {
            var factory = new ChannelFactory<TContract>(typeof(TContract).Name + "_Endpoint");
            factory.Open();
            Service = factory.CreateChannel();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Could not create proxy: {0}", ex.Message);
            Service = null;
        }
    }
 
    public void Dispose()
    {
        if (Service != null)
        {
            var internalProxy = Service as ICommunicationObject;
 
            try
            {
                if (internalProxy != null)
                {
                    if (internalProxy.State != CommunicationState.Closed && internalProxy.State != CommunicationState.Faulted)
                        internalProxy.Close();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Could not close proxy: {0}", ex.Message);
                try
                {
                    if (internalProxy != null)
                        internalProxy.Abort();
                }
                catch (Exception exInternal)
                {
                    Console.WriteLine("Could not abort proxy: {0}", exInternal.Message);
                }
            }
 
            if (internalProxy is IDisposable)
            {
                try
                {
                    if (internalProxy.State != CommunicationState.Faulted)
                        (internalProxy as IDisposable).Dispose();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Could not dispose proxy: ", ex.Message);
                }
            }
        }
    }
}

This requires that the client project has a reference to the System.ServiceModel assembly that comes with the .NET framework. Furthermore, this assumes that there is an endpoint defined in the client’s App.config file that is named IIntroductionService_Endpoint, as highlighted below:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="net.tcp://localhost:50123/IntroductionService"
binding="netTcpBinding"
contract="WcfService.Contract.IIntroductionService"
name="IIntroductionService_Endpoint">
</endpoint>
</client>
</system.serviceModel>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <client>
      <endpoint address="net.tcp://localhost:50123/IntroductionService"
          binding="netTcpBinding"
          contract="WcfService.Contract.IIntroductionService"
          name="IIntroductionService_Endpoint">
      </endpoint>
    </client>
  </system.serviceModel>
</configuration>

Notice that the endpoint is almost exactly the same as the service configuration! If you want a shortcut for the client side configuration, the WcfTestClient can actually generate the config file for you:

There are a few differences between the generated config and the one I am actually using:

  • The name of the endpoint has been changed so that the WcfProxy<T>  above works
  • The bindings element and the bindingConfiguration attribute) have been removed as I am using the default netTcpBinding
  • I have qualified the
    contract attribute with the full namespace of the contract as it lives in another assembly

Calling the Service

And now, for the magic! In the Program.cs file in
the client project, we are going to actually call the service. Let’s
start off by calling the service using a hard-coded request. In the
Main method, add the following code:

static void Main(string[] args)
{
Console.WriteLine("Press enter to send the introduction request");
Console.ReadLine();
using (var proxy = new WcfProxy<IIntroductionService>())
{
Console.ForegroundColor = ConsoleColor.Blue;
var request = new IntroductionRequest
{
Greeting = "Hello",
Name = "João"
};
Console.WriteLine("Sending: {0}", request);

Console.ForegroundColor = ConsoleColor.Green;
var response = proxy.Service.Introduce(request);
Console.WriteLine("Received: {0}", response.ToString());
}
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static void Main(string[] args)
{
    Console.WriteLine("Press enter to send the introduction request");
    Console.ReadLine();
    using (var proxy = new WcfProxy<IIntroductionService>())
    {
        Console.ForegroundColor = ConsoleColor.Blue;
        var request = new IntroductionRequest
            {
                Greeting = "Hello",
                Name = "João"
            };
        Console.WriteLine("Sending: {0}", request);
 
        Console.ForegroundColor = ConsoleColor.Green;
        var response = proxy.Service.Introduce(request);
        Console.WriteLine("Received: {0}", response.ToString());
    }
    Console.ForegroundColor = ConsoleColor.White;
    Console.WriteLine("Press enter to exit");
    Console.ReadLine();
}

All we’re doing here is

  1. Creating an instance of our proxy to call the service
  2. Creating a request object and displaying it (in green)
  3. Sending it to the service and waiting for a response
  4. Once the response has come back, we are displaying it (in blue)

At this point, your project should look like this:

We need to set up the service host and client projects to start up by default. Right click the solution in Solution Explorer, and click Set StartUp Projects

Select the Multiple startup projects radio button, and set the service host and client projects to Start:

Now, whenever we run the project, both projects will start in debug mode! Let’s start them up now! You should see the following in the client window:

That’s it! You’ve got a working solution for an application talking to a service over WCF!

Improving the Client

Just for fun, why don’t we allow the user to type in their own greeting and name. See if you can work through the modified code below and figure out what is going on:

class Program
{
static void Main(string[] args)
{
WriteInstructions();

const string regexString = "(?<Greeting>.*?), I'm (?<Name>.*)";
Regex regex = new Regex(regexString);
string line;
while (!string.IsNullOrWhiteSpace(line = Console.ReadLine()))
{
var match = regex.Match(line);
if (match.Success)
{
using (var proxy = new WcfProxy<IIntroductionService>())
{
Console.ForegroundColor = ConsoleColor.Blue;
var request = new IntroductionRequest
{
Greeting = match.Groups["Greeting"].Value,
Name = match.Groups["Name"].Value
};
Console.WriteLine("Sending: {0}", request);

Console.ForegroundColor = ConsoleColor.Green;
var response = proxy.Service.Introduce(request);
Console.WriteLine("Received: {0}", response.ToString());

Console.ForegroundColor = ConsoleColor.White;
}
}
else
{
WriteInstructions();
}
}
}

static void WriteInstructions()
{
Console.WriteLine("Type a greeting as '<greeting>, I'm <name>', then press enter");
Console.WriteLine(" e.g. Hello, I'm João");
Console.WriteLine("And the server will respond in kind :)");
Console.WriteLine("Press enter only to exit");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Program
{
    static void Main(string[] args)
    {
        WriteInstructions();
 
        const string regexString = "(?<Greeting>.*?), I'm (?<Name>.*)";
        Regex regex = new Regex(regexString);
        string line;
        while (!string.IsNullOrWhiteSpace(line = Console.ReadLine()))
        {
            var match = regex.Match(line);
            if (match.Success)
            {
                using (var proxy = new WcfProxy<IIntroductionService>())
                {
                    Console.ForegroundColor = ConsoleColor.Blue;
                    var request = new IntroductionRequest
                    {
                        Greeting = match.Groups["Greeting"].Value,
                        Name = match.Groups["Name"].Value
                    };
                    Console.WriteLine("Sending: {0}", request);
 
                    Console.ForegroundColor = ConsoleColor.Green;
                    var response = proxy.Service.Introduce(request);
                    Console.WriteLine("Received: {0}", response.ToString());
 
                    Console.ForegroundColor = ConsoleColor.White;
                }
            }
            else
            {
                WriteInstructions();
            }
        }
    }
 
    static void WriteInstructions()
    {
        Console.WriteLine("Type a greeting as '<greeting>, I'm <name>', then press enter");
        Console.WriteLine("  e.g. Hello, I'm João");
        Console.WriteLine("And the server will respond in kind :)");
        Console.WriteLine("Press enter only to exit");
    }
}

You may now call the service with whichever greeting and name you like:

Gotchas and Notes

There are a couple of things that stumped me at first!

ServiceContract,  ServiceBehavior, OperationContract and OperationBehavior Attributes

Exceptions are thrown when trying to start up the service if these attributes are not added to the contract definitions and service implementations.

DataContract and DataMember Attributes

Adding a non-default constructor caused my classes to fail to serialize and/or deserialize without these attributes. If you want to add other properties that you don’t want serialized (e.g. calculated properties), just decorate the property with the  IgnoreDataMember attribute.

Deserializing Objects and Constructors

There is no need for a non-default constructor as the .NET serialization classes responsible for creating these objects in memory don’t use them! Give it a try – create a default constructor and do some custom initialization. This code never actually runs!

Download the Solution

If you would like to download the complete source code, the Visual Studio 2012 solution, projects and code (with ServiceModelEx.dll but without the NuGet packages) are available here

Final Thoughts

The steps to actually write software using an SOA approach are quite simple, aren’t they? All of our top-secret business logic sits safely in our data centers, out of the hands of the evil hackers!

This service does not actually hold any state or need to run any scheduled jobs, so it would work fine as a Web-Activated Service (WAS) hosted in Internet Information Services (IIS). Remember how we separated our service implementation from our service host? In order to implement this as a WAS, very few steps are necessary! Now I am going to do something that I really hate seeing on other blogs – a promise (not really) of another post! Well, here it is: I am planning on writing a post describing such a process in the near future.

WCF Windows Service Using TopShelf and ServiceModelEx z的更多相关文章

  1. WCF - Windows Service Hosting

    WCF - Windows Service Hosting The operation of Windows service hosting is a simple one. Given below ...

  2. .NET开发Windows Service程序 - Topshelf

    在实际项目开发过程中,会经常写一些类似定时检查,应用监控的应用.这类应用在windows平台通常都会写成window service程序. 在百度上搜索一下'c#开发windows service', ...

  3. 通过TopShelf简单创建windows service

    目前很多项目都是B/S架构的,我们经常会用到webapi.MVC等框架,实际项目中可能不仅仅是一些数据的增删改查,需要对数据进行计算,但是将计算逻辑放到api层又会拖累整个项目的运行速度,从而会写一些 ...

  4. 宿主在Windows Service中的WCF(创建,安装,调用) (host到exe,非IIS)

    1. 创建WCF服务 在vs2010中创建WCF服务应用程序,会自动生成一个接口和一个实现类:(IService1和Service1) IService1接口如下:   using System.Ru ...

  5. WCF寄宿到Windows Service

    WCF寄宿到Windows Service[1] 2014-06-14 WCF寄宿到Windows Service参考 WCF寄宿到Windows Service 返回 在前面创建一个简单的WCF程序 ...

  6. quartz.net结合Topshelf实现windows service服务托管的作业调度框架

    topshelf可以很简单方便的实现windows service服务,详见我的一篇博客的介绍 http://www.cnblogs.com/xiaopotian/articles/5428361.h ...

  7. WCF寄宿到Windows Service[1]

    WCF寄宿到Windows Service 返回 在前面创建一个简单的WCF程序,我们把WCF的服务寄宿到了Host这个控制台项目中了.下面将介绍如何把WCF的服务寄宿到Windows服务中(源代码) ...

  8. 客户端通过wcf来启动或者停止服务器上的windows service

    1.设置服务器上的windows service的security,下面的命令只能用cmd.exe来运行(以管理员模式) sc sdset "LISA_43_Dev_Batch" ...

  9. [WCF] - 使用 bat 批处理文件将 WCF 服务部署为 Windows Service

    1. 添加 Windows Service 项目 2. 添加 WCF 项目引用 3. 更新 App.config 配置文件(可以从 WCF的 Web.config 拷贝过来),设置服务地址. 4. 配 ...

随机推荐

  1. JSBinding + SharpKit / 安装SharpKit以及添加SharpKit工程

    本文说明如何往 sln 中添加 SharpKit 工程,以及配置. SharpKit 工程用于将 C# 源代码编译成 JS  代码. QQ群 189738580 1. 安装SharpKit 到 sha ...

  2. Android如何防止apk程序被反编译

    作为Android应用开发者,不得不面对一个尴尬的局面,就是自己辛辛苦苦开发的应用可以被别人很轻易的就反编译出来. Google似乎也发现了这个问题,从SDK2.3开始我们可以看到在android-s ...

  3. vs2010 无法创建 *.edmx(Entity Frame Work) 文件的问题

    当你安装了VS2010或者已经安装了EntityFramework41RC.exe之后发现依然在Add New Item时无法找到ADO.NET Entity Model,有可能是你创建的不是netf ...

  4. linux包之nc之nc命令

    nc-1.84-22.el6.x86_64不用系统上提供的nc版本会有所不同,其提供的参数使用方法也略有差异 nc -v -w 1 192.168.2.10 -z 1-65535|grep succe ...

  5. unity, collider/trigger on children

    参考:http://answers.unity3d.com/questions/410711/trigger-in-child-object-calls-ontriggerenter-in-pa.ht ...

  6. weblogic11g重置控制台管理员用户名/密码

    weblogic安装后,很久不用,忘记访问控制台的用户名或者密码,可通过以下步骤来重置用户名密码. 说明:%DOMAIN_HOME%:指WebLogic Server 域(Domain)目录例如我的做 ...

  7. CentOS6.5系统挂载NTFS分区的移动硬盘 centos安装repoforge源(yum)

    CentOS6.5系统挂载NTFS分区的移动硬盘 作为IT的工作者,避免不了使用Linux系统,我现在使用的系统是CentOS6.5 X86_64位版本,但是插入NTFS移动硬盘没有办法识别.通过下面 ...

  8. ASP.NET MVC 页面调整并传递参数

    转自:http://blog.csdn.net/zhensoft163/article/details/7174661 使用过ASP.NET MVC的人都知道在MVC中页面后台中常用的页面跳转方法有几 ...

  9. 【Reporting Services 报表开发】— 矩阵的使用

    矩阵,相较于数据表示一维的数据,只能指定固定的数据列,来呈现动态的明细数据行,所以,它可以说是种二维的数据展现形式,让我们能够很容易地从数据行和数据集的交替中查看对应的汇总信息.像SQL Server ...

  10. ORA-12519, TNS:no appropriate service handler found

    解决问题: 有时候连不上数据库是因为连接数到了极限了. select count(*) from v$process --当前的连接数 130 select value from v$paramete ...