Introduction
Because of my job, I need to learn WCF. This series of articles will record my learning process. If you are a beginner just like me, you can write the code in this tip by yourself. After we finish learning the fundamental steps, I will upload my source code. Besides, I am in the process of learning English, so if you find some mistakes in my tip, please tell me!
Create a WCF Service
Step 1: Create the Service Contract and its Implementation
Quote:
First let's start creating the service contract and its implementation. Create an IEcho
interface, add the System.ServiceModel
reference to project, add ServiceContract
and OperationContract
attribute to the class. These contracts will expose methods to the outside world for using the service.
using System.IO;
using System.ServiceModel;
name space EchoService
{
[ServiceContract]
public interface IEcho
{
[OperationContract]
string Echo(string message);
[OperationContract]
Stream DownloadStream();
[OperationContract]
void UploadStream(Stream stream);
}
}
using System;
using System.IO;
using System.ServiceModel.Activation;
namespace EchoService
{
public class EchoService : IEcho
{
public string Echo(string message)
{
return message;
}
public Stream DownloadStream()
{
byte[] buffer = new byte[1024];
Random rand = new Random();
rand.NextBytes(buffer);
MemoryStream stream = new MemoryStream(buffer);
return stream;
}
public void UploadStream(Stream stream)
{
int readResult;
int bytesRead = 0;
byte[] buffer = new byte[1000];
do
{
readResult = stream.Read(buffer, 0, buffer.Length);
bytesRead += readResult;
}
while (readResult != 0);
stream.Close();
}
}
}
Service Contract
- It describes the client-callable operations exposed by the service.
- It maps the interface and methods of your service to a platform-independent description.
- It describes message exchange patterns. Some service operations might be one-way; others might require a request-reply pattern.
Step 2: Create a Console Application to Host the Service?
Quote:
Now we are ready with the service. Let's go for implementing the hosting process. Create a console application and name it as "SelfHosting.cs".
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace SelfHostedTest
{
class Program
{
static void Main(string[] args)
{
string address = "http://localhost:8090/SelfHostService/EchoSerivce";
Uri httpUrl = new Uri(address);
ServiceHost host = new ServiceHost(typeof(SelfHostService.EchoSerivce), httpUrl);
BasicHttpBinding binding = new BasicHttpBinding();
host.AddServiceEndpoint(typeof(SelfHostService.IEchoSerivce), new BasicHttpBinding(), address);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
host.Open();
Console.WriteLine("Service is host at " + address);
Console.WriteLine("Host is running... Press <Enter> key to stop");
Console.ReadLine();
}
}
}
When a service is hosted in Internet Information Services(IIS) or Windows Process Activation Service(WAS), the base address of the service is provided by the hosting enviroment. In the self-hosted case, you must specify the base address yourself. You can use the code above to config the service, you can also use the App.config to config the service. Notice the code will override the App.config file configuration.
<service
name="SelfHostService.EchoService"
behaviorConfiguration="EchoService">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8090/SelfHostService/EchoSerivce"/>
</baseAddresses>
</host>
...
</service>
ServiceHost
ServiceHost
class is an important class to configure and expose a service for use by client-applications when you are not using IIS or WAS to host the service.
ServiceHost
object is made to load a service, configure endpoint, apply security setting, and start listeners to handle incoming requests.
Note: Host process must be running before the client calls the service, which typically means you have to prelaunch it.
Step 3: Create a Client to Call the Service
Quote:
Service is hosted, now we need to implement the proxy class for the client. There are different ways of creating the proxy.
- Using SvcUtil.exe, we can create the proxy class and configuration file with end points.
- Adding Service reference to the client application.
- Implementing
ClientBase<T>
class:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Security;
using System.Text;
using ComMethods;
namespace EchoServiceTester
{
static void main()
{
RunTests()
}
private static void RunTests()
{
string address = string.Format("http://{0}/EchoService.svc/DefaultEcho", host);
Binding binding = new BasicHttpBinding();
InvokeServiceOperations(address, binding);
}
private static void InvokeServiceOperations(string address, Binding binding)
{
Console.WriteLine("Testing service operations
using a binding with the following elements:");
DescribeBinding(binding);
TestEcho(address, binding);
TestUploadStream(address, binding);
TestDownloadStream(address, binding);
Console.WriteLine("Test passed.");
}
static void DescribeBinding(Binding binding)
{
foreach (BindingElement element in (new CustomBinding(binding)).Elements)
{
Console.WriteLine(element.GetType().Name);
}
}
static void TestEcho(string address, Binding binding)
{
ChannelFactory<IEcho> channelFactory =
new ChannelFactory<IEcho>(binding, address);
IEcho client = channelFactory.CreateChannel();
((IChannel)client).Open();
Console.WriteLine("Invoking Echo... Response = {0}",
client.Echo("Hello World!"));
((IChannel)client).Close();
}
static void TestUploadStream(string address, Binding binding)
{
byte[] buffer = new byte[1024];
Random rand = new Random();
rand.NextBytes(buffer);
MemoryStream stream = new MemoryStream(buffer);
ChannelFactory<IEcho> channelFactory =
new ChannelFactory<IEcho>(binding, address);
IEcho client = channelFactory.CreateChannel();
((IChannel)client).Open();
Console.WriteLine("Invoking UploadStream...");
client.UploadStream(stream);
((IChannel)client).Close();
}
static void TestDownloadStream(string address, Binding binding)
{
ChannelFactory<IEcho> channelFactory =
new ChannelFactory<IEcho>(binding, address);
IEcho client = channelFactory.CreateChannel();
((IChannel)client).Open();
Console.WriteLine("Invoking DownloadStream...");
Stream stream = client.DownloadStream();
int readResult;
int bytesRead = 0;
byte[] buffer = new byte[1000];
do
{
readResult = stream.Read(buffer, 0, buffer.Length);
bytesRead += readResult;
}
while (readResult != 0);
stream.Close();
Console.WriteLine("Read {0} bytes.", bytesRead);
((IChannel)client).Close();
}
}
Typically, to create a channel to a service endpoint, you can generate a client type with Svcutil.exe or Add Service Reference to create an instance of generated type. You can also create a channel by using the ChannelFactory<TChannel>
class. Then, configure how the client can communicate with the service. Of these three methods, Implementing ClientBase<T>
is the best practice. If you are using rest two methods, we need to create a proxy class every time we make changes in Service implementation. But this is not the case for ClientBase<T>
. It will create the proxy only at runtime and so it will take care of everything.