Introduction
In this article, I will be creating a simple Student Enrollment web service using WCF components from the ground up. This article does not use the WCF Project template in Visual Studio rather I will create this WCF web service using basic components that builds up the WCF technology. I will break down the components into separate discrete assemblies to give you a wider view of the big picture of WCF. Then, I will host this web service in a simple Console Application. Finally, I will consume the web service through a WPF application client.
Background
WCF is a unification of multiple technologies that were used in creating Distributed Applications which includes COM+, .NET Enterprise Services, MSMQ, .NET Remoting, Web services, etc. for communication. Hence, WCF is a vast topic to tackle in just one article. Because of this, you should be aware that this article only introduces you to a small part of the capability of WCF. However this article will try to demonstrate the most basic building blocks and architecture of WCF.
More of that, it would be much easier to grasp a topic if we can just create and test it, right? So let's dive into the code project!
Using the code
(Note: This project will use a Console Application to host the web service for simplicity. There are multiple ways to host a WCF web service. Please refer to other articles that discusses this part. Also note that this project uses Web Service Binding, specifically wsHTTPBinding. There are also many types of bindings to choose from that are not discussed in this article.)
First step - Define the WebService Interface
Tip 1: In this article I am just creating one interface for our service but you can actually create as many interfaces as you want in your desired project.
Tip 2: Interfaces contain the signatures of the operations, messages and data that the client can use to communicate with the server.
- Open Visual Studio 2010 and create a new project. Select Class Library project and name it BasicWCFWebService. Note: I am creating a Class Library for the WebService interface to decouple it from the service host logic and make it generally usable for both the client and host application.
- A default class file Class1.cs is generated. Rename this file to IStudentEnrollmentService.cs. This file will contain the "Interface" that is exposed by this service. Also rename the first project to SchoolServiceInterfaces.
- In Solution Explorer, right click on the References folder and then click Add Reference. In the .NET tab, select System.ServiceModel and System.Runtime.Serialization assemblies, finally click the OK button.
- System.ServiceModel contains the
ServiceContractAttribute
attribute definition - System.Runtime.Serialization contains the
DataContractAttribute
attribute definition
- After adding the latter assembly references, include these in the code by adding
using
statements for both System.ServiceModel
and System.Runtime.Serialization
namespaces.
IStudentEnrollmentService.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
namespace SchoolServiceInterfaces
{
[ServiceContract]
public interface IStudentEnrollmentService
{
[OperationContract]
void EnrollStudent(Student s);
[OperationContract]
Student[] GetEnrolledStudents();
}
[DataContract]
public class Student
{
private string name;
[DataMember]
public string StudentName
{
set { this.name = value; }
get { return this.name; }
}
}
}
Second Step - Create a database "mock up" class
Create a new Class Library project and name it SchoolDatabase.cs.
Tip 3: This is just a mock up database project. You can create or use the real database objects on your real project. This is the reason why we created a separate project for the database so that we can emulate a real world database architecture. Remember that a database is a separate entity and not bound to our project.
StudentDatabase.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace SchoolDatabase
{
public class StudentDatabase
{
private static List<string> studentData = new List<string>();
public void AddStudent(string studentName)
{
studentData.Add(studentName);
}
public List<string> GetStudentList()
{
return studentData;
}
}
}
Third step - Implement the web service interface
- Create a new Class Library project and name it SchoolServices and rename Class1.cs to StudentEnrollmentService.cs. Then, add references to the SchoolDatabase and SchoolServiceInterfaces projects. To do this, right click on the References folder in the SchoolServices project then on the "Project" tab, select the SchoolDatabase and StudentServiceInterfaces projects.
- Implement the
IStudentEnrollmentService
interface and use the StudentDatabase
class to store student information.
StudentEnrollmentService.cs
using System;
using System.Collections.Generic;
using System.Text;
using SchoolServiceInterfaces;
using SchoolDatabase;
namespace SchoolServices
{
public class StudentEnrollmentService : IStudentEnrollmentService
{
public void EnrollStudent(Student s)
{
StudentDatabase sd = new StudentDatabase();
sd.AddStudent(s.StudentName);
}
public Student[] GetEnrolledStudents()
{
StudentDatabase sd = new StudentDatabase();
List<string>studentList = sd.GetStudentList();
Student[] studentArray = new Student[studentList.Count];
for (int i = 0; i < studentList.Count; i++)
{
Student s = new Student();
s.StudentName = studentList[i];
studentArray[i] = s;
}
return studentArray;
}
}
}
Fourth step - Host the web service
- To run the web service we must host it. In this article we will just use a simple Console Application to host our web service.
- To do this, create a new Console Application project and name it SchoolServiceHost. Then add references to .NET
System.ServiceModel
and to our projects SchoolServices and SchoolServiceInterfaces. System.ServiceModel is a .Net component that contains the base mechanism of a .Net web service, in this case, to create a Service host by using ServiceHost. I suggest you read more about System.ServiceModel namespace.
Program.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using SchoolServices;
namespace SchoolServiceHost
{
class Program
{
static ServiceHost host = null;
static void StartService()
{
host = new ServiceHost(typeof(StudentEnrollmentService));
/***********
* if you don't want to use App.Config for the web service host,
* just uncomment below:
***********
host.AddServiceEndpoint(new ServiceEndpoint(
ContractDescription.GetContract(typeof(IStudentEnrollmentService)),
new WSHttpBinding(),
new EndpointAddress("http://localhost:8732/awesomeschoolservice")));
**********/
host.Open();
}
static void CloseService()
{
if (host.State != CommunicationState.Closed)
{
host.Close();
}
}
static void Main(string[] args)
{
StartService();
Console.WriteLine("Student Enrollment Service is running....");
Console.ReadKey();
CloseService();
}
}
}
Add App.config file to the SchoolServiceHost project. This will contain the ABC information of our WCF web service which are:
- "A" for "Address" - where to access the service. This defines the endpoint information where the service is located or accessible.
- "B" for "Binding" - how to access the service. This defines the transport protocol that should be used to access this service.
- "C" for "Contract" - what to access in the service. This defines the services offered by this web service.
App.config
="1.0"="utf-8"
<configuration>
<system.serviceModel>
<services>
<service name="SchoolServices.StudentEnrollmentService">
<endpoint
address="http://localhost:8732/awesomeschoolservice"
binding="wsHttpBinding"
contract="SchoolServiceInterfaces.IStudentEnrollmentService" />
</service>
</services>
</system.serviceModel>
</configuration>
Fifth step - Consuming the web service through a WPF client
Add a new WPF Application project and name it SchoolServiceClient. Then add references to System.ServiceModel and
SchoolServiceInterfaces
.
Tip 4: Client only needs to know the signature of our service, it doesn't need to know the implementation or how the service implemented those services that is why we will only add the reference of SchoolServiceInterfaces which contains the contract of the web service we are interested to. System.ServiceModel is a .Net component that contains the base mechanism of a .Net web service, in this case, to create a Service client by using ChannelFactory. I suggest you read more about System.ServiceModel namespace.
MainWindow.xaml
<Window x:Class="SchoolServiceClient.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="296" Width="293">
<Grid>
<Button Content="Enroll" Height="23" HorizontalAlignment="Left" Margin="184,12,0,0"
Name="enrollBtn" VerticalAlignment="Top" Width="75" Click="enrollBtn_Click" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="studentNameTxt"
VerticalAlignment="Top" Width="152" />
<Button Content="Get Enrolled Students" Height="23" HorizontalAlignment="Left" Margin="136,222,0,0"
Name="getEnrollBtn" VerticalAlignment="Top" Width="123" Click="getEnrollBtn_Click" />
<TextBox Height="165" HorizontalAlignment="Left" Margin="12,51,0,0" Name="enrolledTxt"
VerticalAlignment="Top" Width="247" />
</Grid>
</Window>
MainWindow.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ServiceModel;
using SchoolServiceInterface;
namespace SchoolServiceClient
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void enrollBtn_Click(object sender, RoutedEventArgs e)
{
if (studentNameTxt.Text == "") return;
using (ChannelFactory<IStudentEnrollmentService> schoolServiceProxy =
new ChannelFactory<IStudentEnrollmentService>("MyStudentEnrollmentServiceEndpoint"))
{
schoolServiceProxy.Open();
IStudentEnrollmentService schoolEnrollmentService = schoolServiceProxy.CreateChannel();
Student s = new Student();
s.StudentName = studentNameTxt.Text;
schoolEnrollmentService.EnrollStudent(s);
schoolServiceProxy.Close();
}
studentNameTxt.Text = "";
}
private void getEnrollBtn_Click(object sender, RoutedEventArgs e)
{
enrolledTxt.Text = "";
using (ChannelFactory<IStudentEnrollmentService> schoolServiceProxy =
new ChannelFactory<IStudentEnrollmentService>("MyStudentEnrollmentServiceEndpoint"))
{
schoolServiceProxy.Open();
IStudentEnrollmentService schoolEnrollmentService = schoolServiceProxy.CreateChannel();
Student[] enrolledStudents = schoolEnrollmentService.GetEnrolledStudents();
foreach (Student s in enrolledStudents)
{
enrolledTxt.AppendText(s.StudentName + "\n");
}
schoolServiceProxy.Close();
}
}
}
}
App.config
="1.0"="utf-8"
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IStudentEnrollmentService" />
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8732/awesomeschoolservice"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IStudentEnrollmentService"
contract="SchoolServiceInterfaces.IStudentEnrollmentService" name="MyStudentEnrollmentServiceEndpoint">
</endpoint>
</client>
</system.serviceModel>
</configuration>
Sixth Step - Run and test the web service
- Build the solution, then go to the output directories of SchoolServiceHost and SchoolServiceClient. First run SchoolServiceHost.exe, then SchoolServiceClient.exe, respectively.
Points of interest
Decoupling components greatly improves maintainability, scalability, and stability of an application. This topic demonstrates the most basic parts of a WCF application rather than using a template. Building these small parts gives you flexibility and an in-depth understanding of its underlying technologies. You should now be able to create more advanced WCF applications using the very basic understanding of its architecture and its building blocks.
Your feedback is always welcome.