Introduction
This is a complete walk-through of creating a self-hosted WCF Service and Client. You will need .NET 3.0 and Visual Studio 2005 in order to get this to work.
Example WCF Service
Getting Started
Open Visual Studio 2005 and create a new WCF Service Library project. This project type is listed under NET Framework 3.0. This will create a project and a C# file (Class1.cs). The C# file contains a service stub that we will modify.
Modifying the Service
First we will change the data contract class near the bottom of the page. It starts off looking like this:
[DataContract]
public class DataContract1
{
string firstName;
string lastName;
[DataMember]
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
[DataMember]
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
All we are going to do is change the class name. Change the class name to "Patient
" now.
Data Contract definition
A data contract is a formal agreement between a service and a client that abstractly describes the data to be exchanged. That is, in order to communicate, the client and the service do not have to share the same types, only the same data contracts. A data contract precisely defines, for each parameter or return type, what data is serialized (turned into XML) in order to be exchanged. (Microsoft)
Next we need to change the service contract interface.
Service Contract definition
Service contracts express valid message exchange patterns between two parties. The party that initiates communication is called the initiator. The other party is called the service. Service contracts specify one or more operation contracts. An operation contract represents an individual message exchange or a correlated request/reply message exchange. (Don Box)
The service contract's name is fine, but let's change the methods marked with the [OperationContract]
attribute to Get
and Set
methods for our Patient
class (data contract).
[ServiceContract()]
public interface IService1
{
[OperationContract]
Patient GetPatient(Int32 index);
[OperationContract]
void SetPatient(Int32 index, Patient patient);
}
The index parameter to the above methods will be used to access a single Patient
in an array of Patient
s. This will become obvious after we modify the IService1
implementation class next. Replace the service1
class with the following code:
[ServiceBehavior(IncludeExceptionDetailInFaults=true)]
public class PatientService : IService1
{
Patient[] pat = null;
public PatientService()
{
pat = new Patient[3];
pat[0] = new Patient();
pat[0].FirstName = "Bob";
pat[0].LastName = "Chandler";
pat[1] = new Patient();
pat[1].FirstName = "Joe";
pat[1].LastName = "Klink";
pat[2] = new Patient();
pat[2].FirstName = "Sally";
pat[2].LastName = "Wilson";
}
public Patient GetPatient(Int32 index)
{
if (index <= pat.GetUpperBound(0) && index > -1)
return pat[index];
else
return new Patient();
}
public void SetPatient(Int32 index, Patient patient)
{
if (index <= pat.GetUpperBound(0) && index > -1)
pat[index] = patient;
}
}
The ServiceBehavior
attribute, IncludeExceptionDetailInFaults=true
, will give us more detailed error information while debugging if we mess something up. Without this we get a generic FaultException
that tells us nothing about the real error.
The constructor of our PatientService
class creates and fills our Patient
array.
The GetPatient
method returns the Patient
object at index.
The SetPatient
method sets the Patient
object at index to the Patient
object passed in via the second parameter.
Now we should have a working service but we need a host for our service to reside in.
The Service Host
Right-click on the service project and go to Properties. Change the Output Type from Class Library to Windows Application.
Close the properties Window and right-click the project again and this time choose Add, New Item, choose Windows Form and click the Add button.
In the form Designer add a single button and double-click the newly added button. This will add a click event handler and move you to the code window.
Add a using System.ServiceModel;
up at the top. This will allow us to host our service.
Then, right above the Form1
constructor, add 2 new class variables.
bool serviceStarted = false;
ServiceHost myServiceHost = null;
Now back to the
button1_Click
event handler code. Make the code listed below your method body.
if (serviceStarted)
{
myServiceHost.Close();
serviceStarted = false;
button1.Text = "Start Service";
}
else
{
Uri baseAddress = new Uri("net.tcp://localhost:2202/PatientService");
NetTcpBinding binding = new NetTcpBinding();
myServiceHost = new ServiceHost(typeof(PatientService), baseAddress);
myServiceHost.AddServiceEndpoint(typeof(IService1), binding, baseAddress);
myServiceHost.Open();
serviceStarted = true;
button1.Text = "Stop Service";
}
What this bit of code does is startup a host for our service if its not started already, or closes the host if it is started.
To host or start a service you have to know your ABC's. Not the alphabet, but the service's Address, Binding, and Contract.
In this case, we pretty much make up an address. We'll use TCP binding (method of communication) so the first part of the address (URI) is set to net.tcp
. The next part of the address is the machine name or IP and port number. Localhost just means your machine and the port number (2202) was just arbitrary. You will need to make sure its a valid and open port before just choosing any number. The last part of the address is just our service name.
The binding is TCP so we need to make a NetTcpBinding
object.
Our contract is IService1
, the name of our service contract interface.
We create a new ServiceHost
object which takes a "singletonInstance
" type and our Address
as parameters. The "singletonInstance
" is our contract's implementation class, PatientService
.
Next we add an Endpoint
to the host we just created. This takes our ABC's as parameters, but in reverse order for some reason.
The host is Open
ed or Close
d to allow clients access or not respectively.
We need to add a main entry point to our host application so at the bottom, right below the class but inside the namespace brackets, add the following code.
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
Go ahead back to the form designer and change the text on the button to "Start Service".
You can build and run this application if you want to now. You have now finished a self-hosted service, but we need a client to consume the service.
Example Client
Right-click on the solution and click Add, New Project, Windows, and choose Windows Application.
Add 3 label controls, 3 textboxes, and 2 buttons to the new Form.
- Change
label1 's text property to "Index:" - Change
label2 's text property to "First Name:" - Change
label3 's text property to "Last Name:" - Change
button1 's text property to "Get Patient" - Change
button2 's text property to "Set Patient" - Line up
textBox1 and button1 to label1 Line up textBox2 and button2 to label2 Finally, line up textBox3 to label3
| The end product should look like this:
|
Now double-click a blank part of the form to add a Load
event handler for the form. Then double-click both of the buttons to add click event handlers for each.
We need to add 2 references to the client project. Select the Project menu item and click Add Reference. Under the .NET tab, find and select System.ServiceModel
and click OK. Add another reference, this time choose the Projects tab and select your service that you made earlier.
Back in Form1.cs, add using System.ServiceModel;
and using YourService;
(replace YourService
with the name of your service) to the list of other using
s.
Now we're ready to access our service. Add a class variable right above the constructor called IService1 patientSvc
and set it to null
. Like in our host application we have to use our ABC's to setup an Endpoint
to our service. In the Form1_Load
event handler, add the following code:
EndpointAddress address = new EndpointAddress
(new Uri("net.tcp://localhost:2202/PatientService"));
NetTcpBinding binding = new NetTcpBinding();
ChannelFactory<iservice1 /><IService1> factory =
new ChannelFactory<iservice1 /><IService1>(binding, address);
patientSvc = factory.CreateChannel();</iservice1 /></iservice1 />
This code creates a new IService1
object in the form of our PatientService
class, again using the service contract's ABC's. Microsoft defines a ChannelFactory
as... A factory that creates channels of different types that are used by clients to send messages to variously configured service endpoints. So now we have a PatientService
(IService1)
object, which is all we need to know at this point.
Put the following code into the button click handlers:
private void button1_Click(object sender, EventArgs e)
{
Patient patient = patientSvc.GetPatient(Convert.ToInt32(textBox1.Text));
if (patient != null)
{
textBox2.Text = patient.FirstName;
textBox3.Text = patient.LastName;
}
}
private void button2_Click(object sender, EventArgs e)
{
Patient patient = new Patient();
patient.FirstName = textBox2.Text;
patient.LastName = textBox3.Text;
patientSvc.SetPatient(Convert.ToInt32(textBox1.Text), patient);
}
When button1
is clicked, it takes the index entered in textBox1
, converts it to an Int32
, and passes it to our GetPatient
method in our PatientService
. What returns is a full-fledged Patient
object if the index is valid (null
otherwise). This is the same Patient
object that we defined in our DataContract
class in our service. We then fill the other 2 textboxes with the first and last names.
When button2
is clicked, we create a new Patient
object, fill its 2 properties with values from our textboxes and then set the Patient
at index in our PatientService
to this new Patient
.
Conclusion
That's it! We now have a working self-hosted WCF service and client. Right-click on the solution and choose Set Startup Projects, then choose Multiple Startup Projects. Set the Action on both projects to Start, then click OK. Now debug the solution and the service host and the client will both be running. Make sure to click the Start Service button before attempting to click any buttons on the client.
History
- May 14, 2007: Version 1.0 uploaded
- May 17th, 2007: Fixed article mistake in client
ChannelFactory
settings