Академический Документы
Профессиональный Документы
Культура Документы
A WCF Service is comprised of the following major components. The diagram below
shows how the components are related to each other:
Service Contract
Operation Contract
Data Contract
Data Member
Service Contract
Service contract is a contract that specifies the direction and type of the messages in
a conversation. It is an interface or a class that defines the service contract in a
Windows Communication Foundation (WCF) application. A service contract is the
gateway to a service for external applications to make use of the service functions,
and at least one service contract should be available in a service. A service contract
is defined as follows:
The ServiceContract attribute of the interface defines the service contract in the
service interface. The service contract defines the operations available in the service,
operations like web service methods in a web service. IstudentService is a
student service interface which exposes all the operation contracts or methods in this
service to external systems.
Operation Contract
An operation contract defines the methods of the service that are accessible by
external systems. The OperationContract attribute needs to be applied for all
these methods, these are also like web methods in a web service. Operation
contracts are defined as follows:
Data Contract
A data contract defines a type with a set of data members or fields that will be used
as the composite type in a service contract. It is a loosely-coupled model that is
defined outside the implementation of the service and accessible by services in other
platforms. To define a data contract, apply the DataContract attribute to the class
to serialize the class by a serializer, and apply the DataMember attribute to the fields
in the class that must be serialized. A StudentInformation data contract can be
defined as follows:
Data Member
A data member specifies the type which is part of a data contract used as a
composite type member of the contract. To define a data member, apply the
DataMember attribute to the fields that must be serialized. The DataMember
attribute can be applied to private properties, but they will be serialized and
deserialized, and will be accessible to the user or process. The code below shows
how to define a data member in a data contract:
[DataMember]
public int StudentId
{
get { return _studentId; }
set { _studentId = value; }
}
These are the steps needed to be followed to create a WCF application with Visual
Studio 2008:
You can see the ServiceContract attribute with IService1, and the methods
exposed are defined with the OperationContract attribute. Service1 is the
concrete class for the implementation of IService1. Endpoints and other behavioral
properties are defined in the system.serviceModel section of the Web.Config file.
You need to make the necessary changes in the above section of Web.Config if you
are renaming the services; otherwise, external systems can not identify the services.
Now, we need to make the necessary changes in the default WCF application
according to our requirements. The first step is renaming the existing service files or
deleting the existing files and create new ones. In this example, I would go ahead
with the first approach, renaming the existing files.
[DataMember]
public int StudentId
{
get { return _studentId; }
set { _studentId = value; }
}
[DataMember]
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
[DataMember]
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
}
}
// Method returning the Full name of the student for the studentId
public string GetStudentFullName(int studentId)
{
IEnumerable<string> Student
= from student in Students
where student.StudentId == studentId
select student.FirstName + " " + student.LastName;
The WCF service can be hosted in Internet Information Service (IIS), and Windows
Activation Service (WAS) with Vista and IIS 7.0. In this example, IIS is used for
hosting the WCF service. Build the service project, and run it by pressing F5 to run
from Visual Studio 2008. By default, it would take IIS as the hosting service. You can
select the StudentService.svc file from the list of files displayed in the browser
window, and the page will be displayed as follows:
You can not directly test the WCF service with out a client, as we do in a web service.
There is an interface provided by Microsoft to test the service in an easy way, which
is the WcfTestClient utility. The WcfTestClient window will be opened if the service is
hosted by the WCF Service Host (WcfSvcHost.exe), from Visual Studio, or you can
explicitly run the WcfTestClient utility from the command prompt. You can run it as
follows:
The WCF service can be communicated through a WCF client from a client
application. The first step of consuming a WCF service is to create a WCF client and
the related configuration files. The Service Model Metadata Utility Tool (Svcutil.exe)
is a command-line tool and one of its options is to create a WCF Client. The other
option is to create a WCF Client by adding the service reference in the Windows
project. The second option is used in this example to create the WCF Client.
Also, add a Windows application with a form which has Buttons, Labels, a
TextBox, and a DataGridView to display the student information.
To add the service reference to the Windows application, follow these steps:
Create the instance of the WCF client to communicate to the service. The following
line create the service client proxy:
After creating the proxy or instance of the WCF client, we can access the methods of
the service like accessing the methods of a web service in the previous versions of
.NET. The GetStudentFullName and GetStudentInfo methods of the service can
be called as follows:
In the form, if you enter the student ID and click on the Display Student Info button,
you will call the service methods and populate the Fullname label and The
DataGridView with the details.
Conclusion
Introduction
I�m impressed! And that�s not an easy thing to do. I�ve been playing with the CTP
(February 2006) release for a while now, and believe it or not, I think we may
actually have something here. I have not had the time to explore the other
�Foundation� members (of WinFX), but from what I�ve seen of the Windows
Communication Foundation (WCF), it really is a very nice package. Yes, I know that
each time there is a new technology released, there is always some neat features.
But in the past, I always thought that we were paying more (in complexity) than
what we were getting (in functionality), can you spell COM? This time, it feels like we
are getting the whole thing for free. It�s possible that it may be just my na�ve
perspective, but hey it�s all about me anyway.
I like to preface my articles with a disclaimer. The content in this article is simply my
impressions and interpretation, and it should be read in that context. Even though
the prose may at times seem like I know what I�m talking about, any similarity to
actual facts may simply be coincidence. Enjoy the journey.
The minute we stepped out of the DOS box, we realized that we were not alone any
more, and we needed to learn to get along and communicate with the other
inhabitants of the virtual world. DDE, OLE, COM, DCOM, and Remoting have been
some of the attempts at providing mechanisms for two applications to be able to talk
to each other. Remember how OLE and COM were described when first introduced?
As the �foundation for all future products�. With hindsight, we can see that they
were really just baby steps. Each one solved only a small part of the whole problem.
So if they were baby steps, then WCF is certainly a giant leap. WCF provides a
complete solution to the communication problem. And it does it with elegance and
simplicity. Can you tell that I�m just a little enthusiastic?
Now, I have not explored every nook and cranny, or option, or possibility, but from
what I�ve seen, it�s an excellent solution. At least for me, and as I said before�
What is a service?
Let�s see how much trouble I can get myself into here. I think that, in its most
elemental form, a service is simply some functionality (code) running in some
external process that is made available to other processes in a standard way.
That�s pretty much the crux of it, except that in a standard way also
encompasses platform and language neutrality.
So, by the above definition, a service really has two parts. First is the code that must
be running somewhere in order to provide some functionality to clients. And second,
there must be some generic mechanism that can be used by any process, regardless
of the platform, language, or locality, that makes the service accessible. That generic
mechanism has turned out to be XML and SOAP. Of course, there are some
additional facilities required in order for a client to be able to know (or discover)
what functionality the service makes available. But I think of those as supporting
technologies.
There is also some glue that is required in order to tie the two parts of a service
together. That glue is the code that will support the communication medium
(transport) that is being used by the service and the client to talk to each other.
Being lazy�I mean smart, we�ve come up with some generic glue also. This way,
each service implementation does not have to re-invent the wheel. For Web
Services, the generic glue is a Web Server. So, a Web Server provides a hosting
environment for services that use HTTP as their transport mechanism. I would also
like to suggest that Web Services are a special implementation of a service as
defined above.
Here are the things that we will be examining in the rest of this article. How do you
define a service? How do you implement a service? How do you host a service? How
do you access and use a service? Once we have the basics nailed down, we�ll look
at some of the more complex communication options that WCF facilitates.
So what is WCF?
WCF is services based technology. It has, as its roots, Web Services, and thus XML
and SOAP are its core technologies. WCF took the concept of Web Services and
super-charged it. Much of the look and feel of WCF behaves like traditional Web
Services. In fact, I like to think of WCF services as Web Services on steroids. You
define WCF services much like Web Services. You can interrogate a known service
for its methods with the same protocols that are available for Web Services. And you
have very similar infrastructure requirements as are for Web Services. The main
difference is that WCF has expanded the transports that are available to include
TCP/IP, IPC (named pipes), and message queue-ing, in addition to HTTP. I think the
focus of Web Services is to solve the interoperability problem, and the focus of WCF
is to solve the much broader communication problem. And it has done this while still
maintaining a uniform API as well providing more efficient mechanisms.
The most important feature from a developer perspective is that you don�t have to
be concerned with what or how you are communicating. The code is the same, no
matter what the final transport mechanism or locality of service might be.
Our WCF journey starts with how services define the functionality that they expose.
Much of the infrastructure required to implement services under WCF is specified
using declarative programming. That means, using attributes to specify functionality.
The following shows how to declare an interface that will be exposed as a service:
The ServiceContract attribute specifies that the interface defines the functionality
of a service. OperationContract is used to decorate each method that is to be
exposed as part of the service. That is all that is required to create a WCF service.
Just slightly more is required to actually deploy the service, which we�ll cover later
on.
By the way, you don�t have to use interfaces when implementing a service, just like
you don�t have to use an interface to define a class. You do have to specify what
you want exposed through a service, explicitly. You can define anything else you
want or need as part of the interface, but only methods, and only methods that get
decorated with [OperationContract], will be exposed by the service.
DataContract specifies the data type that you are exposing and, DataMember
specifies the members that are part of the data type. As is the case with
ServiceContract, you have to explicitly declare which members are to be exposed
to external clients, using DataMember. What that means is that you can include
anything else that you may want (or need) as part of the class definition, but only
the members decorated with DataMember will be visible to clients.
As we saw above, one of the options available to specify functionality under WCF is
to use attributes. Attributes are translated by the compiler to generate much of the
infrastructure required by WCF in order for us to create and use services.
The second way you can specify many of the options is through configuration files.
This allows you to make changes without having to re-compile. Many of the WCF
classes will automatically use default values from the config file. Here�s an example
of an endpoint specified using config data (endpoints will be described shortly). First,
the config file, then the code statement referencing the config file data:
Endpoints
Endpoints are the �identity� of a service. They define all the information that we
need in order to establish and communicate successfully with a service. Endpoints
are made up of three pieces of information: Address, Binding, and Contract. The
address is obviously the location of the service, such as
�net.pipe://localhost/LocalTimeService�. The binding specifies security options,
encoding options, and transport options, which means a lot of options! Luckily, there
is a collection of pre-defined bindings provided with WCF that we can use to make
our life simpler. And finally, the contract is the actual interface that the service
implements.
So, a service is nothing more than a regular class that gets decorated with some
special attributes. The attributes are then translated by the compiler to generate the
special infrastructure code required to expose the class as a service to the world. In
the following code, we first define an interface that has one method that returns the
local time of where the service has been deployed. The LocalTimeService class
then implements the interface, and thus exposes the functionality to the world, or at
least to whomever is interested.
[ServiceBehavior(InstanceContextMode =
InstanceContextMode.PerCall)]
public class LocalTimeService : ILocalTime
{
public string GetLocalTime()
{
return DateTime.Now.ToShortTimeString();
}
}
That�s all that�s needed to create a WCF service. If you compile the above code
into a DLL (a library), you will have created a service. Of course, there is a little
more needed in order to have something that�s useable. We need two other pieces
in order to complete the service. We need something that will be able to load the
service DLL when a client requests the functionality of the service. And we need
something that will be able to listen on a communication port, and look through
everything that is being received to see if it matches what we are responsible for,
our service contract.
There are a number of ways to deploy our service. First, if we implement our service
to support HTTP, then we could deploy our service just like a regular Web Service,
using IIS. If we want to support some of the other transports, then we could deploy
the service using Windows Activation Service (WAS), which is an enhancement
available in IIS 7.0. If either of these is not suitable or we want more control over
the service, then the other solution is to build our own hosting environment by using
ServiceHost. ServiceHost is a class available in WCF to host services, almost
like a mini IIS, just for our service. We can implement ServiceHost in any housing
available under Windows, in a console app, a Windows executable, or a Windows
service (formerly NT service).
ServiceHost will listen on whatever channel we specify for our service, using
whatever protocol we specify, and call our service whenever a client request for our
specific service comes in. That�s a lot of bang for just a couple of lines of code. All
that we need to do is tell ServiceHost the endpoint that it is responsible for and
the class that it should instantiate when a matching message is received. Here�s the
code that�s required to host the LocalTimeService in a console app:
You can now compile the service. However, if you try to run it, you�ll get an error
message indicating that you haven�t provided ServiceHost with an endpoint. As
we saw above, you can specify endpoints either programmatically, or by using the
configuration file. The nice thing about using configuration is that you can change it
at any time and you don�t have to recompile. As we�ll see later, you can specify
multiple endpoints for a service, depending on the clients that you want to support.
And if at some point later, you decide to not support a specific transport, you just
have to edit the configuration file.
Here�s the config file that we�ll need for the LocalTimeService:
Let�s examine the entries in the config file. You should note that there could be as
many entries as needed. For example, there could be several endpoints that the
service supports (different transports). There is also only one service being specified
in this example, but there could be several services provided in the same housing.
You can see that the endpoint has three properties: address, binding, and contract.
The binding indicated is referencing the standard netNamedPipeBinding provided
in WCF. There are various default binding classes provided for each transport. You
can see the options for each in the docs.
I will say here that you too will encounter the �zero application (non-infrastructure)
endpoints� exception at some point. There won�t be too many clues as to what
exactly is not matching up, so you�ll have to scrutinize the text. Make sure that you
have the correct namespaces specified.
Now you can execute the application, and the service will be available to any client
that knows how to communicate with it.
Proxies
Just by saying we want to go to New York and we are going to go by car, does not
get us there. We need a car to actually get us there. Having a completely defined
endpoint is not enough. We need something (code) that will actually take the
endpoint as a parameter and allow us to do what we want to do, call a method. And
that something is a proxy.
As was the case in the past with COM, proxies take care of all the low level plumbing
(serializing and packaging our parameters) so that we just need to make the call. We
don�t care how they are forced through the �spigot� or how they are pulled out on
the other side.
And as we�ve also had in the past, there is a utility that will create the proxies for
us. However, this is one area of WCF that needs some improvement. And I�m
guessing this functionality will become incorporated into Visual Studio in future
releases. At least, I would hope so. To create a proxy, you need to use the command
line utility, svcutil, which has a number of switches that are not all or well
documented. But hey, I�m not complaining, it�s a small inconvenience for a whole
lot of major improvements. And it�s still only Beta.
So, you run svcutil against your service DLL and bam! You got your proxy class.
There are other options, like if the service has a MEX endpoint, you can direct it to
the service, and it will extract the service information dynamically from the service.
This is essentially the same functionality provided through Studio when creating a
Web Service, and we use the �Add Web Reference� dialog. What I really want is for
Visual Studio to automatically generate the proxies since it has all the information in
the source files to begin with! But as I said, I�m not complaining. ;)
Currently then, creating the proxy is a two step process. First, you run svcutil
against your service DLL, which will create the schema (XSD) and WSDL files. Then,
you invoke svcutil again, but this time, you run it against the output it just created
(*.xsd, *.wsdl).
The svcutil will generate �output.cs� as the default file name, unless you specify
otherwise, I normally just rename it. There are also options to just generate
DataContracts or just ServiceContracts, and also an option to generate a
client config file. Here�s the proxy file for the LocalTimeService, with some
portions edited for readability. There�s not much there, since all the magic occurs in
ClientBase.
public LocalTimeProxy()
{
}
public string GetLocalTime()
{
return base.InnerProxy.GetLocalTime();
}
}
So now, the next logical step is to build a client that knows how to consume the
service that is being provided. The client code only needs two things: the proxy that
allows it to communicate with the service, and the endpoint to the service. Here�s
one version of a client that consumes the LocalTimeService:
Console.WriteLine("Service is running....press" +
" any key to terminate.");
Console.ReadKey();
client.keepClocking = false;
//wait 2 seconds
Thread.Sleep(2000);
}
}
Not much to this client. All that it's doing is making a call to the service method
GetLocalTime(), once a second. As you can see, the client code has no indication
as to what or where the other end of the method call is. Nor what mechanism is
actually being used to make the connection. It is just a simple class method call! As
we look at other examples, you'll keep seeing the simplicity of coding that is
provided under WCF. And here is the config file that specifies the endpoint to the
service which is required by the client.
Compile and run the client. Start several instances. Just make sure that the service
is started before you start the clients, otherwise nobody will be listening.
That�s it for the basics in getting services listening and consuming services. In Part
2, we'll build some examples that will demonstrate WCF's support for the various
communication patterns. The download includes all of the source code for the sample
applications described in the articles.
Abstract
Introduction
To make developer's life easy, Microsoft continuously brings new technologies in the
market. Sometimes those are similar to their predecessors and sometimes they bring
large enhancements in the current technologies. As a result we can see the
processing architecture of web page is totally changed in .NET 2.0. Now .NET 2.0
does not bring significant enhancements in Winform application development. So
how can we go with a Windows application that can be distributed across diverse
operating systems and that will gain the performance benefit while used on the same
OS? The answer is WCF, i.e. Windows Communication Foundation (WCF) code name
INDIGO.
In this article we will learn the basic components of WCF and also dive into some of
the details of these components. We will try to address the issues faced in previous
versions and find out why to use WCF.
What is WCF
Prerequisite
WCF is based upon the following key components that are used to build a WCF
service. We will go in the details of each component in the sections below:
1. Contract definitions
o Data contracts and data member
o Service contracts
o Fault contracts
o Message contracts
2. End points
3. Bindings
o BasicHttpBinding
o WSHttpBinding
o WSDualHttpBinding
o WSFederationHttpBinding
o MsmqIntegrationBinding
o NetMsmqBinding
o NetNamedPipeBinding
o NetPeerTcpBinding
o NetTcpBinding
4. Hosting environments
o Windows application
o Console application
o Windows service
o IIS
o WAS (Windows activation service) comes with IIS 7.0
o Windows Presentation Foundation
Till now we have seen what WCF is and what WCF components are. But unless and
until we come to know the issues addressed by WCF, we will never know the power
of WCF.
Imagine you are developing an enterprise application for a bank. Now when you start
designing the system, the requirements are very complex and critical. Say for
example, the application is used by bank staff, outside customers, some of payment
gateway systems. You are not sure that what operating system and what type of
clients will be used by outside customers. You want performance with security,
reliability when used on intranet and security, reliability when used from outside.
Previously you could have gone for web services exposing web methods for outside
clients and a remoting or COM+ application for Windows OS. Both these applications
are using the same business logic and the same database. Suppose you want to add
some new business logic like introducing a new loan system, imagine how much it
will take to change all the systems. This is perhaps the simplest example. What if
you are going to modify business logic or add 3-4 additional schemes in a span of 6
months? The current design will not scale. You will start thinking, what if I would
have a single application exposing multiple endpoints for diverse and similar clients
and will give me all benefits of industry-wide open standards. That's it. WCF is the
answer to your question. WCF, with managed code, using power and features of
.NET 2.0, giving all the benefits of performance, security, transaction, reliability by
supporting all open industry standards makes life easy for the developer and the
organization by unifying the programming model with the best features from all
available technologies.
1. Web services
2. .NET - .NET communication
3. Distributed transactions
4. Support for WS-* specifications
5. Queued messaging
1. Contracts
2. Bindings
3. End point definitions
4. Hosting environment
Contracts
Contracts are one of the most important components of WCF. Contracts make
developing interoperable services possible. It is a common understanding between
the client and server of available methods, data structures, and message structures.
They remain loosely coupled and platform independent. WCF contains the following
contracts. As SOAP is the native protocol of WCF for communication, interfaces,
classes are translated to SOAP types by CLR.
Service contract
Translated to WSDL. Service contract defines the operations that a service
can perform. This is what a WCF service must contain at least one. It can
have more than one service contract also. It is defined using
[ServiceContract]attribute. The implementer of the service contract is
managed code. Hence, service contract also maps the interface and methods
to platform independent description. Using the attribute feature of .NET, we
can extend the behavior of the service like is it one way or two way, the
exception handling behavior etc.
Each service contract contains method that are exposed outside and
annotated with [OperationContract]attribute.
Service contract can also specify the requirements that should be satisfied by
the endpoint.
E.g. session required should satisfied by endpoints.
Example:
[ServiceContract
(ProtectionLevel=System.Net.Security.ProtectionLevel.None)]
public interface IWCFService
{
[OperationContract]
string GetSystemIPAddress();
[OperationContract]
List GetEmpolyeeArray();
[OperationContract]
Employee CreateNewEmployee();
}
Usually we can use service contract over a class too. But it is not a good
practice as interfaces allows separation from the implementation logic. While
using ServiceContract and OperationContract over a method or a
class, access modifiers does not make any sense. Everything that is marked
with ServiceContract or OperationContract will be exposed to the
client outside the application domain.
Services are of three types:
1. Typed: Used for simple communication. It can accept or return simple
or complex data objects.
E.g. CreateNewEmployee() method in the above class
2. UnTyped: It allows developers to work at message level.
E.g. Message ParseRequest(Message myRequestMessage)
3. Typed message: Falls between typed and untyped services.
Uses custom message classes defined with Message contracts.
MyMessage ProcessedRequest(MyRequestMessage msg)
Data contract
Translated to XML schema (XSD). Basically describes data structure. When we
want to pass or return more values, then we go for data contract. It is a
serializable object that can be passed or returned to or from a service. It
supports versioning provided you do not change the name of the existing
members, name of the namespace to maintain compatibility.
Data contract can be defined annotating [DataContract] attribute on a
class, structure or enum. The data members that are to be exposed outside
are annotated with [DataMember] attribute for class and structure and
[EnumMember] attribute for enum.
[DataContract] tells WCF how to serialize the object. Depending upon the
client WCF serializes the members. If the communication is between WCF and
WCF then it uses binary serialization over SOAP to optimize the performance.
While in case of WCF to Non-Windows client, it uses SOAP serialization.
Here also access modifiers do not play any role.
There are some cases when you do not have proxy generated for the given
service. This may be the case when the WCF service using only TCP endpoints
to allow interaction. In that case WSDL will not be available unless and until it
is explicitly defined in service behavior with HTTP end point. Even if the client
does not have WSDL with us, we can create the same WCF dummy class or
proxy class at client side. WCF service will remain as it is, the implementer
will derive from ClientBase and implement the WCF service (IWCFService).
In such a case, the operation contract method will call
base.Channel.GetSystemIPAddress() and it will be routed to the server
by foundation. In this case we can have data contract members defined in the
same way. The only thing and most important thing is that namespace must
have same names as that of server. Also method names and data member
names should remain the same.
Fault contract
Translated to SOAP faults. Allows developer to document the errors that a
service can produce. We can define service contract only on
[OperationContract] by annotating with
[FaultContract(typeof(System.Exception))] attribute. Here the
exception type can be exception to want to document. Custom exception
should be marked as serializable.
Message contract
[MessageContract]
public class MyMessage
{
[MessageHeader]
public string myHeader;
[MessageBodyMember]
public Employee myMessageBody;
}
Bindings
Bindings are communication channels between a WCF service and client. They decide
how the WCF service will communicate with the client. Depending upon the different
protocols like HTTP, TCP, MSMQ, there are different types of binding. Binding
supports encoding of messages using text encoding, binary encoding and Message
Transmission Optimization mechanism (MTOM, interoperable message format used
for effective transmission of large attachments greater than 64K) As WCF supports
all industry open standards, it supports security using SSL and WS-Security (schema
defined security) standards.If we want
session enabled service then binding determines whether the service is session
enabled or not. Not all binding supports sessions. As WCF is extendible, we can
define our own custom bindings. But this is very rare case because WCF has nine in-
built bindings that are sufficient for most of the applications. The exceptions can be:
RSS feed of a site or SMTP protocol.
http : HTTP
net.tcp : TCP
net.pipes : Names pipes
net.msmq : MSMQ
End points
End points are nothing but a URI which are exposed to the outside world and from
where the client can connect to the WCF service.
Each end point consists of:
We can define as many end points as the application requires. For example, if the
application is to be used by a JAVA client and WPF client then we can expose an
endpoint with TCP binding where the WPF client will communicate and an end point
with basic HTTP binding where JAVA client will communicate. We can control and
configure the behavior of an end point programmatically and using application
configuration file.
Hosting environments
As the diagram shows, we need a host to run a WCF service. Fortunately there are
many options that can be used for hosting a WCF service. This actually depends
upon the application requirement. Following is the list of hosts:
Security in WCF
When you go for a distributed application, the most important aspect is security. As
mentioned earlier, WCF supports security using binding. Binding supports security
using SSL, WS-Security standards. You can also use Windows authentication. One of
the primary goals behind WCF development was to make it easy for the development
of secured applications. WCF service can be secured using four ways:
1. Authentication
2. Message integrity
3. Message confidentiality
4. Authorization
There are some guidelines about using security mechanisms. Depending upon the
application need and requirement, we can easily select the suitable one and
implement with few efforts. Here are the guidelines:
There are many other features which are present in WCF and can be used to achieve
reliable communication. Those are as follows:
[OperationContract]
[OperationBehavior(TransactionScopeRequired=true,
TransactionAutoComplete=true)]
public void TransferMoney(Account creditor, Account debitor)
{
//
}
Queuing: Using NetMsmqBinding or MsmqIntegrationBinding, WCF
supports queuing of messages.
Extensibility: WCF allows creating custom channels, bindings as per the
application requirements. For example you can create a channel for
communicating with SMTP protocol or RSS protocol.
Application outline
1. Console client: It calls the WCF service without creating a proxy. Contract and
data members are created manually at client side. It uses simple TCP binding.
2. MsmqClient: As the name says, it uses MSMQ binding. The MSMQ must be
installed and the private queue with the given name should be created before
running this application.
3. SystemInfo: Simple data access class. It does nothing special. WCF service
calls methods of this class.
4. WCFClient: Simple Windows application to demonstrate working of WSDL
created using svcutil. Uses Basichttp binding.
5. WCFHost: A simple console application acting as a host for WCF service.
6. WCFservice: Actual WCF service with service contract and data contract.
Also contains .SVC file so that it can be hosted in IIS directly. It also contains
other details required for MSMQ binding. There is one implementer class
which implements service contracts. Most of the code is self explanatory. Your
suggestions are always welcome.
Conclusion
License
This article has no explicit license attached to it but may contain usage terms in the
article text or the download files themselves. If in doubt please contact the author
via the discussion board below.
Contents
Introduction
PreRequisites
Things To Do To Get It Running For You
Show It
LINQ to Entities
WCF Service
Windows Service Hosting And Installation
WPF Client
Introduction
It has been a while since I have undertaken a large article, so I thought it was about
time I corrected that, and wrote a fairly meaty article. This article represents this
meat.
So I suppose I should start by stating what it is this article will cover. Well it's going
to cover quite a lot actually. The basic idea is that there is a SQL Server database
somewhere that has the Northwind database installed, and that we will be using an
ORM to obtain data from this database using a WCF service. The WCF itself will be
hosted inside a Windows service which will be installed via a customer installer.
In order to interact with this Windows hosted WCF service we will be using a WPF
client application. The WPF client application will allow the user to obtain particular
entities (Customers only as I would have been writing this article for ever to allow for
everything) to be queried using a custom query builder that will then be used to
send to the Windows hosted WCF service, which in turn will then query the ORM for
the queried entities, and return the results to the WPF client app.
In essence that is it, it may not be that exciting, but there is enough here for me to
show you how to create a service/client and an hosting windows service. Also along
the way I will be taking slight detours to talk about certain practices and cool code
that may make your life easier.
I should point out that the UI was meant to be a throw away, ok I tried to make it an
ok looking one, but that's just because I like WPF. I mean there is still some nice
ideas in there, like the way the queries are constructed, that could be ported to a
richer query generator, but that is left for an excercise to the reader.
PreRequisites
You will need VS2008 (for the Entity Framework) and VS2008 SP1, and .NET 3.5 SP1
installed.
In order to get the attached code (WCF service hosted in a windows service/WPF
client) you will need to make sure the following has been done
1. That the windows service has been installed and provided with login details as
part of the installation
2. That the LINQ to Entities connectionStrings section of the associated .Config
file are updated to point to your local SQL server installation
3. That the windows service is running when you attempt ot use the WPFClient
4. That the App.Config within the WPFClient project is configured to use the
correct security login, this is in the identity/userPrincipalName element.
Just to get you a little bit more familiar with it all here is the project structure
Show It
So what does it look like when running, well I decided to show you some screen
shots and also have a link to a small video for this one.
Initial view loaded
Changing the type of view for the selected Customer Orders
LINQ to Entities
For the ORM I had originally thought about using LINQ to SQL, but you know I think
Microsoft are slowly sweeping (personal opinion) that one under the carpet, in favour
of LINQ to Entities. So I thought why not use LINQ to Entities. So that's what this
article uses for the ORM.
I had in the past installed the LINQ to Entities whilst it was still in BETA and it was
horrible, the designer used to churn out 3 sections of XML:
As I saw the designer used to create these, but not all that well in the old days and
you always had to get your hands dirty in the XML, and there was loads of it, a
couple of 1000nd lines for a small database. Eeek.
If you want to know how bad this was just use any of the 3 links above.
Mantaining these 3 sections of XML using the old beta of LINQ to Entities was a
frigging nightmare. Thankfully the new designer does away with the user having to
worry about the XML directly. Which I think is a good thing. What happens now is
that the cod behind for the designer is now a C#|VB class that has a App.Config file
associated with it, with a very strange ConnectionStrings section. Let us have a
look at one of these:
</connectionStrings>
</configuration>
To run the attached code you will need to change the associated config within the
folder where you decide to install the service using the installer, this is covered later
within the article in section Windows Service Hosting And Installation.
We can see that there are still links to the CSDL/MSL/SSDL, but there are now
treated as metadata resources. Much nicer.
So if we now focus our attention to the LINQ to Entities designer, we can see that we
have something like the LINQ to SQL designer, but it is a little different. This is to
allow an Entity to be constructed from multiple views|tables.
For this example I will however be using a 1 to 1 mapping between Entity and
Northwind database table.
LINQ to Entities actually goes one step further than LINQ to SQL did in that it now
supports a full blown SQL query language, which as far as I can tell is actually the
same as SQL syntax. Here is a small example.
Collapse Copy Code
using (AdventureWorksEntities advWorksContext =
new AdventureWorksEntities())
{
string esqlQuery = @"SELECT contactID, AVG(order.TotalDue)
FROM AdventureWorksEntities.SalesOrderHeader
AS order GROUP BY order.Contact.ContactID as
contactID";
try
{
foreach (DbDataRecord rec in
new ObjectQuery<DbDataRecord>(esqlQuery, advWorksContext))
{
Console.WriteLine("ContactID = {0} Average TotalDue = {1} ",
rec[0], rec[1]);
}
}
catch (EntityException ex)
{
Console.WriteLine(ex.ToString());
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.ToString());
}
}
Now the fact that we can store a String with a query is kind of nice, as it means we
can pass a query accross a service boundary, which AFAIK was something that LINQ
to SQL just couldn't do out of the box. I mean you couldn't serialize a
Expression<Func<Customers,Bool>> nor could you return a var , as var has
method level scope. You are also unable to create LINQ queries at a Client and
serialize them (though this would be very cool) and send them to a service to run.
So LINQ to SQL was definately limited. So this new String query ability appears to
be a good thing at first glance.
But let's just think about that for a minute, we used to able to do this sort of thing
with inline/across the wire SQL using ADO.NET and look what a mess that was, not
to mention SQL Injection Attacks. So one would have to ask whether this was a good
thing after all. I mean what is to stop a malicous user from creating there own query
string, the fact that it now uses the Entity Framework rather than the actual
database doesn't really matter to the malicous user, they would probably get the
same results.
I think a far better option to is to just limit the search results by using a dynamically
generated Where clause, or if you have time to create a SQL Query builder that
never actually contains the full string, until the last second when it needs to create
the actual query string, and pass it to the DB|ORM. The later approach is what we
actually do at work, this article will actually discuss the dynamically generated Where
clause solution.
How annoying. Oh well, luckily help is at hand. Recall what I said about LINQ to SQL
not being able to run dynamic queries out of the box, well that was/is true. There is
however an extra Microsoft constructed class that has IQueryable<T> extension
methods that allow both LINQ to Entities and LINQ to SQL to create dynamic where
clauses.
Some of you may recall that I recently wrote an article about Expression Trees and
went to show you how they worked. This DynamicQuery API is the entire reason that
I wrote that article.
So I hope you can now see why we might want to create an Expression Tree at
runtime. It allows us to create a dynamic query based on an Expression Tree.
WCF Service
Overall Structure
The WCF service is actually pretty simple, the entire ServiceContract is shown
below.
namespace Service.Portal
{
[ServiceContract(
Namespace = "http://www.sachabarber.net/2008/cp",
SessionMode = SessionMode.Allowed)]
public interface IPortal
{
//Allows FaultContract(s) to callback
[OperationContract(IsOneWay = false)]
//Allows TransactionScope at client
[TransactionFlow(TransactionFlowOption.Allowed)]
//Allows SOAP faults to client
[FaultContract(typeof(ApplicationException))]
[FaultContract(typeof(ArgumentException))]
[FaultContract(typeof(Exception))]
Response ExecuteRequest(Request request);
}
}
It can be seen from the code above that there is a single ExecuteRequest
OperationContract that accepts a Request and returns a Response. Sounds
easy enough, but aren't there going to be many many Request objects. Well yes
actually, but is just a bit of standard OO polymorphism in play. Where we can store
any sub class of Request in its base class. Ill cover this in a minute for now let's
carry on examining the service, it can be seen that the single ExecuteRequest
OperationContract it marked as OneWay=false, this means that there is a
callback to the client from the service. You may notice that I am not specifying a
CallBack interface anywhere, so what is the OneWay=false all about. Quite simply it
allows faults to be sent back to the client. More on this later. The other thing to note
is that the service allows transactions to be used should the client wish to use them.
namespace Service.Portal
{
/// <summary>
/// A Base request
/// </summary>
[DataContract]
[KnownType(typeof(CustomerRequest))]
[KnownType(typeof(CustomerSearchRequest))]
public abstract class Request : IExtensibleDataObject
{
#region Data
private ExtensionDataObject extensionDataObject=null;
#endregion
....
....
That's essentially it. By using this approach you do not have to worry about a
changing service, as the ServiceContract is always the same, the only thing that
changes is the number of Requests.
}
}
KnownTypes
One of the really cool things about WCF is that you can expose a type such as
Request which has many sub classes, and you can mark the base class (Request in
this case) up with a KnowTypeAttribute and the DataContractSerializer will
know how to treat these.
Within the attached demo application there are only 2 sub classes of Request, but
they should serve well enough to demonstrate the idea of working with KnowTypes.
IExtensibleDataObject
In practical terms what this means is that by making your DataContract class
implement IExtensibleDataObject you are creating a versionable object.
namespace Service.Portal
{
[DataContract]
[KnownType(typeof(CustomerRequest))]
[KnownType(typeof(CustomerSearchRequest))]
public abstract class Request : IExtensibleDataObject
{
private ExtensionDataObject extensionDataObject=null;
Basically this is a good idea and one that I recommend that you all do.
Fault Contracts
If we now visit the WCF service and have a look at the class that implements the
ServiceContract
The WCF service is actually pretty simple, the entire ServiceContract is shown
below.
namespace Service.Portal
{
[ServiceContract(
Namespace = "http://www.sachabarber.net/2008/cp",
SessionMode = SessionMode.Allowed)]
public interface IPortal
{
We can see that we are catching Exceptions and throwing some strange looking
FaultException<T>. Where the FaultException<T> are used in a client
application to catch contractually-specified SOAP faults. It can be seen that the
service implementation is set for per call, which means we will get a new service per
call, and we are not concerned with dealing with a threading synchronization context.
The other thing to mention is that we have a small helper that lets use
IncludeExceptionDetailInFaults if we are in debug mode, and not in release
mode. The single operation is also marked to not allow impersonation. There is no
need to allow impersonation in the attached service code, as it is the only service
within the call chain so there is no need to flow the current credentials to another
service. I have also insisted the service participate in trasactional calls, where the
client code will need to use a TransactionScope class to manage the transaction.
The TransactionAutoComplete property indicates that if no unhandled exceptions
occur, the transaction scope is completed automatically.
namespace Service.Portal
{
[ServiceBehavior(
InstanceContextMode= InstanceContextMode.PerCall,
UseSynchronizationContext = false,
IncludeExceptionDetailInFaults =
DebugHelper.IncludeExceptionsInFaults)]
public class Portal : IPortal
{
if (request == null)
throw new ArgumentNullException("request");
try
{
Object results = request.Execute();
r = request.CreateResponse(results);
}
...
...
...
catch (Exception ex)
{
Console.WriteLine(String.Format("Server Exception {0}",
ex.Message));
return r;
}
#endregion
As I said right at the start of this article the WCF service is actually hosted inside a
Windows service. Inside the attached demo you will find a seperate project that
allows you to debug the WCF service by the use of the following code if you are in
debug mode (where you would right click the PortalHost project and choose
"debug"), or run the service normally if you are in release mode.
namespace PortalHost
{
class Program
{
static void Main(string[] args)
{
#if (!DEBUG)
try
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new Service() };
ServiceBase.Run(ServicesToRun);
}
catch (Exception e)
{
Console.WriteLine("Error occurred " + e.Message);
}
#else
try
{
Console.WriteLine("Starting Service");
Service.StartService();
Console.WriteLine("Service Started");
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
}
catch (Exception e)
{
Console.WriteLine("Error occurred " + e.Message);
}
#endif
}
}
}
namespace PortalHost
{
public partial class Service : ServiceBase
{
private static ServiceHost portalHost;
public Service()
{
}
protected override void OnStart(string[] args)
{
Service.StartService();
}
....
....
....
....
}
}
Also part of the PortalHost project is a custom installer that looks like the following:
namespace PortalHost
{
[RunInstaller(true)]
public partial class ProjectInstaller : Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
return accountSet;
}
}
}
When dealing with WCF the last thing one wants to hear about is a faulted channel,
this is incredibly bad news. Basically a faulted channel is beyond useless. So what
can we do about this situation within a hosting Windows service? Well quite simply,
what we do is catch the ServiceHost.Faulted exception, which WCF allows us to
do (thank god) and stop/restart the WCF service. This is demonstrated below:
namespace PortalHost
{
public partial class Service : ServiceBase
{
private static ServiceHost portalHost;
public Service()
{
}
}
}
Installation
As previously stated the attached demo WCF service is actually hosted inside a
Windows service, and as most of us should know a Windows service runs under a
particular user account. But how does one install a Windows service?
The attached demo code has an installer that deals with installing the windows
service that hosts the WCF service. As part of this installation process you will be
prompted for login credentials for the service.
Using InstallUtil.exe
A Windows service can be installed using the InstallUtil.exe command line util. So in
order to install the attached Portal service you will need something like
installutil.exe
C:\Users\sacha\Desktop\Linq2WCF_NEW\Linq2WCF\Linq2WCF\PortalHost\bin\Releas
e\PortalHost.exe
Which when run will have a Dialog like the following where you can put in the
account details for the Windows service to use.
Using An MSI
The ServiceInstallerDialog code that is used within the custom installer for the
service, that is used to show the service login screen below, is as follows:
if (svcDialog.Result == ServiceInstallerDialogResult.OK)
{
// Do a very simple validation on the user
// input. Check to see whether the user name
// or password is blank.
svcInst.Account = ServiceAccount.User;
svcInst.Username = svcDialog.Username;
svcInst.Password = svcDialog.Password;
}
}
else if (svcDialog.Result ==
ServiceInstallerDialogResult.UseSystem)
{
svcInst.Account = ServiceAccount.LocalSystem;
svcInst.Username = null;
svcInst.Password = null;
accountSet = true;
}
if (!accountSet)
{
// Display a message box. Tell the user to
// enter a valid user and password, or cancel
// out to leave the service account alone.
DialogResult result;
result = MessageBox.Show(
"Invalid user name or password for service
installation." +
" Press Cancel to leave the service account
unchanged.",
"Change Service Account",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Hand);
if (result == DialogResult.Cancel)
{
// Break out of loop.
break;
}
}
} while (!accountSet);
return accountSet;
}
Which the installer is run it will allow you to enter the service login credentials to run
under, using the following dialog box.
Once the installer has been run you will need to change the Config file to point to
your own SQL server installation. The default config file is the name of the output
project used by the installer project, where the config file will be something like
"PortalHost.exe.config", and you should make sure to change the
<connectionStrings> section.
I'll leave it as an excercise to the reader to have a look at the "PortalMSIInstaller"
project, its all fairly standard stuff.
Once the service is installed you will need to start it. This is easily done using the
services manager.
WPF Client
I shall tackle each of these areas in turn in the rest of the sections below
Before we start to get into the code, I would like to talk about the rough structure of
the application.
Windows
There are 2 windows, 1 diagnostics window and 1 main window. The main window
hosts a 3D carosel and a both a CurrentCustomerControl and a
CurrentCustomerOrdersControl control.
UserControls
ViewModels
The attached code is using the MVVM pattern, for the main window
(CustomerOrdersViewModel) and the SearchClauseControl
(SearchClauseViewModel)
As for the rest of the code I think it's all pretty standard WPF stuff, like Attached
Properties / Styles / Commands etc etc
Making Sure You Have The Correct User, For The WCF Service Calls
In order to correctly use the WCF service being hosted in the Windows service
(assuming it is installed and running), you will need to supply login credentials, this
is done via the identity\userPrincipalName section of the App.Config file for the
"WPFClient" project in the attached code.
I think any help that you can give end users, to aid in problems that they may
encounter is a good thing.
It should also be remembered that some users may not actually be technically
minded, so would not relish the thought of trawling around for an App.Config file and
having to look at XML, some I vouch would not even know what XML is. So
protecting users from that is pretty nessecary in my opinion.
To this end I have created a small diagnostics screen that allows a user to view
diagnostic information about the WCF configuration associated with the given Client
App.Config file. The following code applies equally to a WPF/WinForms or a console
app that consumes a WCF service.
The code basically using some of the Configuration based classes found in the
System.ServiceModel.Configuration namespace, along with a little reflection
to search the App.Config file for all relevant WCF elements and to use this to display
in a diagnostic window.
try
{
BindingsSection bindingsSection =
ConfigurationManager.GetSection(
"system.serviceModel/bindings") as BindingsSection;
if (bindingsSection == null)
{
Console.WriteLine(
"The configuration file doesn't contain " +
"a system.serviceModel/bindings configuration section");
}
else
{
endpointNames.Add("BINDINGS");
foreach (BindingCollectionElement bce in
bindingsSection.BindingCollections)
{
AnalyseBinding(endpointNames, bce);
}
}
ClientSection clientSection =
ConfigurationManager.GetSection(
"system.serviceModel/client") as ClientSection;
if (clientSection == null)
{
Console.WriteLine("The configuration file doesn't " +
"contain a system.serviceModel/client configuration
section");
}
else
{
ChannelEndpointElementCollection endpointCollection =
clientSection.ElementInformation.
Properties[String.Empty].Value as
ChannelEndpointElementCollection;
endpointNames.Add(Environment.NewLine);
endpointNames.Add("ENDPOINTS");
foreach (ChannelEndpointElement
endpointElement in endpointCollection)
{
GetPropetiesFromType(endpointNames,
endpointElement.GetType(), endpointElement);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return endpointNames;
}
}
And here is are 2 small helper methods that are used to obtain property values
if (currentValue.StartsWith("System.ServiceModel"))
{
Object o = pi.GetValue(source, null);
foundSysName = true;
GetPropetiesFromType(endpointNames, o.GetType(), o);
}
if (!foundSysName)
endpointNames.Add(String.Format("{0} - {1}",
pi.Name, currentValue));
else
endpointNames.Add(String.Format("{0} - {1}",
pi.Name, "<SEE ABOVE>"));
}
endpointNames.Add(Environment.NewLine);
}
}
3D Carosel
For the 3D carosel, I am using the truly excellent ElementFlow that is part of the
FluidKit codeplex contribution by Pavan Podila. This is an excellent WPF control that I
have toyed with creating myself in the past. When I found Pavans code, I basically
gave up as its so cool. It acts just like a regular panel, which means you can use it
inside ListBoxes, ItemsControls instead of the normal panels these controls use.
Pavan has done a bang up job, and this ElementFlow comes with the following
features
SelectedItem
Reflection On|Off
Many many different views that are part of the standard control, that you can
see in action using the F12 key. I am only using the following views, but there
are others.
o CoverFlow
o Carousel
o RollerCoaster
o Rolodex
From the 3D Carosel that is used to hold a list of Northwind.Customers with Orders,
the selected Customer is used to bind against a DP within the
CurrentCustomerOrdersControl control. And from inside the
CurrentCustomerOrdersControl control, a CustomerOrders DP is set with the
current Northwind.Customers.Orders. This is shown below
/// <summary>
/// Gets or sets the CustomerOrders property.
/// </summary>
public List<Orders> CustomerOrders
{
get { return (List<Orders>)GetValue(CustomerOrdersProperty); }
set { SetValue(CustomerOrdersProperty, value); }
}
/// <summary>
/// Handles changes to the CustomerOrders property.
/// </summary>
private static void OnCustomerOrdersChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
List<Orders> orders = e.NewValue as List<Orders>;
if (orders != null)
((CurrentCustomerOrdersControl)d).lstOrders.ItemsSource = orders;
}
/// <summary>
/// BoundCustomer Dependency Property
/// </summary>
public static readonly DependencyProperty BoundCustomerProperty =
DependencyProperty.Register("BoundCustomer", typeof(Customers),
typeof(CurrentCustomerOrdersControl),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(OnBoundCustomerChanged)));
/// <summary>
/// Gets or sets the BoundCustomer property.
/// </summary>
public Customers BoundCustomer
{
get { return (Customers)GetValue(BoundCustomerProperty); }
set { SetValue(BoundCustomerProperty, value); }
}
/// <summary>
/// Handles changes to the BoundCustomer property.
/// </summary>
private static void OnBoundCustomerChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
Customers customer = e.NewValue as Customers;
if (customer !=null)
((CurrentCustomerOrdersControl) d).CustomerOrders = customer.Orders;
}
Once the Northwind.Customers.Orders are obtained they are used as a source of the
embedded ItemsControl, within the CurrentCustomerOrdersControl.
Search Builder
Right at the beginning of this article I stated that the attached code used the LINQ to
Entities framework, and that we would be writing dynamic queries in the UI that
were then sent to the WCF service and would be used to query the LINQ to Entities
framework.
SearchClauseControl
This is a simple control that takes a Type and then uses Reflection to build up
various properties / allowable values that the user can pick based on the currently
selected Types property type. Most of the logic associated with this control is done
via the SearchClauseViewModel. The SearchClauseControl is also called by
the parent SearchControl in order for the current search clause query to be added
to any other search clause query.
The property that provides the actual query string for a SearchClauseControl
single is as follows:
if (!IsFirst)
sb.Append(String.Format("{0} ", currentOperator));
if (IsCollectionProperty)
sb.Append(String.Format("{0}.Count ", currentProperty.Name));
else
{
if (IsString)
sb.Append(String.Format("{0}.", currentProperty.Name));
else
sb.Append(String.Format("{0} ", currentProperty.Name));
}
if (IsOperatorComparable)
{
sb.Append(String.Format("{0} ", currentCondition));
sb.Append(String.Format("@{0} ", ParameterNumber));
}
if (IsString)
{
sb.Append(String.Format("{0}(@{1})",
currentCondition,
ParameterNumber));
}
return sb.ToString();
}
}
SearchControl
CustomerResponse response =
(CustomerResponse)client.ExecuteRequest(request);
if (response.Customers != null)
CurrentCustomers =
new ObservableCollection<Customers>(response.Customers);
if (CurrentCustomers.Count > 0)
SelectedCustomer = CurrentCustomers[0];
What is actually happening here is that we are using the static method of the
Service<T> class, which is very cool class indeed. Lets see it.
namespace WpfClient.ServiceProxy
{
/// Service client delegate
public delegate void UseServiceDelegate<T>(T proxy);
/// <summary>
/// A helper class to run code using a WCF client proxy
///
/// This weird class came from
/// http://www.iserviceoriented.com/blog/post/Indisposable+-
+WCF+Gotcha+1.aspx
/// </summary>
public static class Service<T>
{
/// <summary>
/// Creates the WCF service proxy and runs the codeBlock delegate
/// using the WCF service proxy that is created
/// </summary>
/// <param name="codeBlock">The code to run using the WCF Proxy</param>
public static void Use(UseServiceDelegate<T> codeBlock)
{
IClientChannel proxy =
(IClientChannel)_channelFactory.CreateChannel();
bool success = false;
try
{
codeBlock((T)proxy);
proxy.Close();
success = true;
}
catch (FaultException<ApplicationException> dex)
{
Console.WriteLine("Client
FaultException<ApplicationException>");
}
catch (FaultException<ArgumentException> aex)
{
Console.WriteLine("Client FaultException<ArgumentException>");
}
catch (FaultException<Exception> ex)
{
Console.WriteLine("Client FaultException<Exception>");
}
catch (FaultException fEx)
{
Console.WriteLine("Client FaultException");
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
}
}
Pretty tidy, huh. This is how all the calls to the WCF service are done inside the
attached WPFClient code.
So now that we have an actual WCF proxy the lamda within the
CustomerOrdersViewModel.SearchCustomerWithOrders() is run, which
creates a new CustomerSearchRequest, which is then sent to the WCF service.
Lets examine the CustomerSearchRequest and see how that works.
//Linq2Entities
using LINQ2Entities;
using System.Data.Objects;
using System.Linq.Dynamic;
namespace Service.Portal
{
[DataContract]
public class CustomerSearchRequest : Request
{
[DataMember]
public List<Object> SearchParameters { get; set; }
[DataMember]
public String WhereClause { get; set; }
List<Customers> custs =
context.Customers.Include("Orders")
.Where(this.WhereClause,
this.SearchParameters.ToArray()).ToList();
return custs;
}
#endregion
}
}
That's It
That is about all I wanted to say, but if you liked this article please leave a vote and
a comment, thanks.
Contents
Introduction
A Note About the Demo Application
Prerequisites
A Brief Overview of the Demo Application and What We are Trying to Achieve
Some Key Concepts Explained
o WCF: the New Service-orientated Attributes
o WCF: the Use of Interfaces
o WCF: the Use of Callbacks
o WCF: Asynchronous Delegates
o WCF: Creating the Proxy
o WCF: Configuration
o WPF: Styles/Templates
o WPF: Animations
o WPF: Databinding
o WPF: Multithreading a WPF Application
How This All Works in the DEMO Application
That's It
Conclusion
History
Other Stuff
Introduction
For those that have read some of my other CodeProject.com articles, you will
probably know that I am not shy about trying out new technologies. One good thing
about that is that I generally share what I learn right here and this article is one of
the hardest ones I've done, IMHO. This article is about how to create a peer-to-peer
chat application using Windows Communication Foundation (WCF) and also how to
make it look nice using Windows Presentation Foundation (WPF).
When I first started reading about WCF, the first place I looked was the MSDN WCF
Samples (which I read a lot), but they weren't that great. I also found lots of chat
apps based on the MSDN versions, which were no good, as they could not return the
list of users within the chat application. I wanted to create a nice WPF styled app
with the list of connected chatters.
So I hunted around a bit more and eventually came across a damn fine/brilliant
article by Nikola Paljetak, which I have tailored for this article. I have OK'd this with
Nikola and the original article content is here. To be honest, the original article was
pure brilliance (it should be mentioned that Nikola is a Professor), but it took a while
for me to get what was going on, as the code wasn't commented. I have now
commented all code, so I think it will still make a very nice discussion/article for
those who are new to WCF/WPF. I was totally new to WCF before this article, so if I
can do it, so can all of you.
So that's what this article is all about. At the end of the article, I would hope you
understand at least some of the key WCF areas and possibly be inspired enough to
look at the WPF side of this article, also.
Before I start bombarding people with the inner mechanisms of the attached
WPF/WCF application, shall we have a quick look at the finished product? There are 3
main areas within the attached demo application:
A login screen
A main window, from which the user can choose who to chat with:
And a window where chatters may openly chat:
The application is based on using Visual Studio 2005 with The Visual Studio Designer
for WPF installed, or using Expression BLEND and Visual Studio 2005 combination, or
Wordpad if you prefer to write stuff in that. Obviously, as it's a WPF/WCF application,
you will also need the .NET 3.0 Framework. This application will cover the following
concepts:
WCF
o The new service orientated attributes
o The use of interfaces
o The use of callbacks
o Asynchronous delegates
o Creating the proxy
WPF
o Styles
o Templates
o Animations
o Databinding
o Multithreading a WPF application
However, this application is not really that orientated to WPF, as that is covered in
numerous other WPF articles at The Code Project. The WPF stuff is really just a
wrapper around the WCF article, which is the real guts of this article. Although there
is some nice WPF stuff going on, just to make the chat application look nicer than an
ordinary console application. I will, however, discuss interesting bits of the WPF
implementation.
Prerequisites
1. To run the code supplied with this article, you will need to install the May
2006 LINQ CTP, which is available here. There is a new March 2007 CTP
available, but it's about 4GB for the full install (as its not just LINQ but the
entire next generation of Visual Studio codenamed "Orcas") and quite fiddly,
and probably going to change anyway. So, the May 2006 LINQ CTP will be OK
for the purpose of what this article is trying to demonstrate.
2. The .NET 3.0 Framework, which is available for download here.
In the attached demo application, we are trying to carry our the following
functionality:
1. Allow chatters to pick a name for themselves and pick an image (Avatar) that
they would like to be presented by
2. Allow the chatters to join a peer-to-peer chat
3. Allow chatters to see who else is available to chat to
4. Allow chatters to send private message
5. Allow chatters to send global messages
6. Allow chatters to leave the chat environment
7. Make it all look pretty using WPF (not strictly a requirement for a chat app,
but I like WPF, so humour me)
In order to achieve all of this I have developed 3 separate assemblies, which by the
end I hope you will understand.
ChatService.Dll: the WCF chat server, that allows chat clients to connect and
is the main message router
Common.Dll: is a simple serializable class which is used by both the
ChatService.Dll and the WPFChatter.Dll files
WPFChatter.Dll: the pretty WPF wrapper around the client WCF functions
There are a number of key concepts that were mentioned earlier that need to be
explained in order for the full application (which covers a lot of ground) to be
understood. So I'll just explain each of these a little bit at a time, so the the final
application should be a little easier to explain (well that's the idea anyway).
There are a number of new attributes that may be used with WCF to adorn our NET
classes/interfaces, shown below are the ones that are used as part of the attached
demo application.
ServiceContractAttribute
Name Description
Gets or sets the type of callback contract
CallbackContract
when the contract is a duplex contract.
Gets or sets the name used to locate the
ConfigurationName
service in an application configuration file.
Gets a value that indicates whether the
HasProtectionLevel
member has a protection level assigned.
Gets or sets the name for the <portType>
Name element in Web Services Description
Language (WSDL).
Gets or sets the namespace of the
Namespace <portType> element in Web Services
Description Language (WSDL).
Specifies whether the binding for the
ProtectionLevel contract must support the value of the
ProtectionLevel property.
Gets or sets whether sessions are allowed,
SessionMode
not allowed or required.
TypeId (Inherited from Attribute)
OperationContractAttribute
ServiceBehaviorAttribute
Name Description
AddressFilterMode Gets or sets the AddressFilterMode that is
used by the dispatcher to route incoming
Name Description
messages to the correct endpoint.
Specifies whether to automatically close a
AutomaticSessionShutdown session when a client closes an output
session.
Gets or sets whether a service supports one
ConcurrencyMode
thread, multiple threads, or reentrant calls.
Gets or sets the value used to locate the
ConfigurationName service element in an application
configuration file.
Gets or sets a value that specifies whether to
IgnoreExtensionDataObject send unknown serialization data onto the
wire.
Gets or sets a value that specifies that
general unhandled execution exceptions are
to be converted into a
System.ServiceModel.FaultException
IncludeExceptionDetailInFaults of type
System.ServiceModel.ExceptionDetail
and sent as a fault message. Set this to true
only during development to troubleshoot a
service.
Gets or sets the value that indicates when
InstanceContextMode
new service objects are created.
Gets or sets the maximum number of items
MaxItemsInObjectGraph
allowed in a serialized object.
Gets or sets the value of the name attribute
Name in the service element in Web Services
Description Language (WSDL).
Gets or sets the value of the target
Namespace namespace for the service in Web Services
Description Language (WSDL).
Gets or sets a value that specifies whether
ReleaseServiceInstanceOnTransactionComplete the service object is released when the
current transaction completes.
Gets or sets a value that specifies whether
TransactionAutoCompleteOnSessionClose pending transactions are completed when the
current session closes without error.
TransactionIsolationLevel Specifies the transaction isolation level for
Name Description
new transactions created inside the service,
and incoming transactions flowed from a
client.
Gets or sets the period within which a
TransactionTimeout
transaction must complete.
TypeId (Inherited from Attribute)
UseSynchronizationContext Gets or sets a value that specifies whether to
use the current synchronization context to
choose the thread of execution.
ValidateMustUnderstand Gets or sets a value that specifies whether
the system or the application enforces SOAP
MustUnderstand header processing.
See the MSDN article or more details. Here is an example of how these new WCF
attributes are used within the demo application, Service project -> ChatService.cs.
"The notion of a contract is the key to building a WCF service. Those of you
that have a background in classic DCOM or COM technologies might be
surprised to know that WCF contracts are expressed using interface-based
programming techniques (everything old is new again!). While not
mandatory, the vast amount your WCF applications will begin by defining a
set of .NET interface types that are used to represent the set of members a
given WCF type will support. Specifically speaking, interfaces that represent
a WCF contract are termed service contracts. The classes (or structures)
that implement them are termed service types."
So there you are -- that's what a nice new book says -- but what does this look like
to us in code? Well the actual ChatService.cs class implements the IChat interface
just mentioned, and so looks like this:
Well we still need to define an interface to allow the callback to work, so an example
of this may be (as done in the demo app):
Collapse Copy Code
interface IChatCallback
{
[OperationContract(IsOneWay = true)]
void Receive(Person sender, string message);
[OperationContract(IsOneWay = true)]
void ReceiveWhisper(Person sender, string message);
[OperationContract(IsOneWay = true)]
void UserEnter(Person person);
[OperationContract(IsOneWay = true)]
void UserLeave(Person person);
}
The BeginInvoke method initiates the asynchronous call. It has the same
parameters as the method you want to execute asynchronously, plus two
additional optional parameters. The first parameter is an AsyncCallback
delegate that references a method to be called when the asynchronous call
completes. The second parameter is a user-defined object that passes
information into the callback method. BeginInvoke returns immediately and
does not wait for the asynchronous call to complete. BeginInvoke returns an
IAsyncResult, which can be used to monitor the progress of the
asynchronous call.
The EndInvoke method retrieves the results of the asynchronous call. It can
be called any time after BeginInvoke; if the asynchronous call has not
completed, EndInvoke blocks the calling thread until it completes. "
In order to for the client to communicate with a WCF service, we need a proxy
object. This can be quite a daunting task (and a little complicated to be honest).
Luckily like a lot of things in .NET 3.0/3.5 there are tools provided to make our lives
easier (you still have to know about them though), and WCF is no different. It has a
little tool called "svcutil" which comes to the rescue.
So how do we create a proxy for our little WCF service (ChatService.exe for the
demo app) using svcutil. Well I have read one thing that said you should be able to
just start the WCF service, point svcutil at the running WCF service, and be able to
create the client proxy that way. But I have to say, I could NOT get that to work at
all. It seems to a common gripe, if you search the internet. So the way I got it to
work was as follows:
1. Open a visual studio command prompt, and change to the directory that
contains the WCF service
2. Run the following command line: svcutil <YOUR_SERVICE.exe>
3. This will list a few files, namely *.wsdl, and *.xsd, and a
schemas.microsoft.com.2003.10.Serialization.xsd file
4. Next you need to run the following command line: svcutil *.wsdl
*.xsd /language:C# /out:MyProxy.cs /config:app.config
5. You now have 2 new client files, MyProxy.cs and app.config, so you can copy
these to your client application
To give you an idea of what svcutil.exe produces in terms of client files, let's have a
look. Here is the MyProxy.cs C# file that was auto-generated by svcutil.exe.
[System.CodeDom.Compiler.GeneratedCodeAttribute(
"System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute()]
public partial class Person : object,
System.Runtime.Serialization.IExtensibleDataObject
{
private System.Runtime.Serialization.ExtensionDataObject
extensionDataField;
[System.Runtime.Serialization.DataMemberAttribute()]
public string ImageURL
{
get
{
return this.ImageURLField;
}
set
{
this.ImageURLField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public string Name
{
get
{
return this.NameField;
}
set
{
this.NameField = value;
}
}
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",
"3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="IChat",
CallbackContract=typeof(IChatCallback),
SessionMode=System.ServiceModel.SessionMode.Required)]
public interface IChat
{
[System.ServiceModel.OperationContractAttribute(IsOneWay=true,
IsInitiating=false, Action="http://tempuri.org/IChat/Say")]
void Say(string msg);
[System.ServiceModel.OperationContractAttribute(IsOneWay=true,
IsInitiating=false, Action="http://tempuri.org/IChat/Whisper")]
void Whisper(string to, string msg);
[System.ServiceModel.OperationContractAttribute(
Action=http://tempuri.org/IChat/Join,
ReplyAction="http://tempuri.org/IChat/JoinResponse")]
Common.Person[] Join(Common.Person name);
[System.ServiceModel.OperationContractAttribute(IsOneWay=true,
IsTerminating=true, IsInitiating=false,
Action="http://tempuri.org/IChat/Leave")]
void Leave();
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",
"3.0.0.0")]
public interface IChatCallback
{
[System.ServiceModel.OperationContractAttribute(IsOneWay=true,
Action="http://tempuri.org/IChat/Receive")]
void Receive(Common.Person sender, string message);
[System.ServiceModel.OperationContractAttribute(IsOneWay=true,
Action="http://tempuri.org/IChat/ReceiveWhisper")]
void ReceiveWhisper(Common.Person sender, string message);
[System.ServiceModel.OperationContractAttribute(IsOneWay=true,
Action="http://tempuri.org/IChat/UserEnter")]
void UserEnter(Common.Person person);
[System.ServiceModel.OperationContractAttribute(IsOneWay=true,
Action="http://tempuri.org/IChat/UserLeave")]
void UserLeave(Common.Person person);
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",
"3.0.0.0")]
public interface IChatChannel : IChat, System.ServiceModel.IClientChannel
{
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",
"3.0.0.0")]
public partial class ChatClient :
System.ServiceModel.DuplexClientBase<IChat>,
IChat
{
</basicHttpBinding>
</bindings>
<client>
<endpoint binding="basicHttpBinding"
bindingConfiguration="DefaultBinding_IChat"
contract="IChat" name="DefaultBinding_IChat_IChat" />
</client>
</system.serviceModel>
</configuration>
So as you can see, these files can simply be used straightaway within your own
client application to communicate with the WCF service. But wait, we are still not
finished with svcutil.exe. Recall that I mentioned asynchronous delegates -- so why
did I do that? Well the svcutil.exe also allows us to create asynchronous proxy code,
using one of the command line switches. To do this, we use the following command
line (notice the /a option):
...instead of:
...which we used previously. This will then change the format of the C# (or VB .NET)
file we get out. What we get now for each WCF service method is an asynchronous
one. So, we would get the following:
[System.ServiceModel.OperationContractAttribute(IsOneWay=true,
IsInitiating=false, AsyncPattern=true,
Action="http://tempuri.org/IChat/Say")]
System.IAsyncResult BeginSay(string msg, System.AsyncCallback callback,
object asyncState);
...instead of:
Hopefully you can see where this ties in with the WCF: Asynchronous Delegates
section mentioned earlier. But just to be sure, here's a more detailed description of
what's going on. Using the /a switch of svcutil.exe, you can generate a proxy that
contains asynchronous methods in addition to the synchronous ones. For each
operation in the original contract, the asynchronous proxy and contract will contain
two additional methods of this form:
As the attached demo application makes use of asynchronous methods for the Join
operation, asynchronous delegates are using within the code to achieve this.
WCF: Configuration
As with all .NET applications, WCF applications allow the application to be configured
via a configuration file. This will be discussed later on, for now you just need to know
that the following items may be configured in an App.Config file for a WCF
application:
Service addresses
Service type
Behavior configuration
Endpoints
Binding types
Service security
WPF styles and templates allow us to change how standard components look. This is
quite a well documented feature, but what I will say is that by using a little bit of
styling one is able to convert a rather plain ListView into a ListView that looks
like the figure shown below. Quite nice, I think. So how is this done? Well, it's all
down to styling. The figure below shows a standard ListView item which is styled
and has some custom data templates assigned. Each item in the ListView is
actually a Common.Person object, which will be discussed later.
All that has been done is that I have applied a style to a standard .NET ListView
control. Here is the XAML that does this. However, I say I am not going to dwell on
these WPF features, as they are well documented and not really part of the main
thrust of this article. I just wanted those people reading this that hadn't come across
WPF before, to know what can be done with it.
<Setter.Value>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<LinearGradientBrush.GradientStops>
<GradientStop Color="#0E4791" Offset="0"/>
<GradientStop Color="#468DE2" Offset="1"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Black" />
</MultiTrigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="noTextHeaderTemplate"/>
<DataTemplate x:Key="textCellTemplate">
<TextBlock Margin="10,0,0,0" Text="{Binding}"
VerticalAlignment="Center"/>
</DataTemplate>
<DataTemplate x:Key="imageCellTemplate">
.....
.....
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="GridViewColumnHeader">
<Setter Property="Visibility" Value="Hidden" />
<Setter Property="Height" Value="0" />
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn Header="Image"
HeaderTemplate="{StaticResource noTextHeaderTemplate}"
Width="100" CellTemplate="{StaticResource imageCellTemplate}"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=Name}"
Header="First Name"
HeaderTemplate="{StaticResource textCellTemplate}" Width="100"/>
</GridView>
</ListView.View>
</ListView>
WPF: Animations
Animations are another element of WPF (again well documented, so I'll not go into it
too much). I have not gone too overboard with animations in the demo application,
but I do use animation twice (because one simply has to if they are developing WPF
stuff).
Once to load the ChatControl and once to hide the ChatControl. The only thing
that is special is the way that I am using animation. They are part of the
Window1.xaml but the triggers are performed in code behind logic. As this may be
useful to some people I'll give a small example.
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="ChatControl"
Storyboard.TargetProperty="(UIElement.RenderTransform).(
TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:001" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="ChatControl"
Storyboard.TargetProperty="(UIElement.RenderTransform).(
TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
WPF: Databinding
The styled ListView that I am using within Window1.xaml utilizes data binding in
order to bind a List of Person objects. This is done by using Templates as already
shown in the WPF : Styles/Templates section. But just in case you're not so sure
about all this WPF stuff, what actually happens is that I use a DataTemplate which
specifies just how the ListView will display its data. To do that, I define the
following DataTemplates and these DataTemplates include the Binding values.
This allows a collection of Person objects to be bound to the ListView.
<DataTemplate x:Key="imageCellTemplate">
<Border CornerRadius="2,2,2,2" Width="40" Height="40"
Background="#FFFFC934" BorderBrush="#FF000000" Margin="3,3,3,3">
</Border>
</DataTemplate>
Threading in WPF is quite similar to .NET 2.0/Win forms, you still have the issue of
threads that are not on the same owner thread as a UI component needing to be
marshaled to the correct thread. The only difference is the keywords. For example, in
.NET 2.0, one would probably have done:
...while in WPF we would (and I do) use the following syntax. Note :
CheckAccess() is marked as <BROWSABLE(false)> so don't be expecting to see it
using Intellisense. However, it does work.
/// <summary>
/// This method checks to see if the current thread needs to be
/// marshalled to the correct (UI owner) thread. If it does a new
/// delegate is created
/// which recalls this method on the correct thread
/// </summary>
/// <param name="sender"><see
/// cref="Proxy_Singleton">ProxySingleton</see></param>
Well, you know what, if you've got to this point without falling asleep, I think you're
ready to deal with the inner workings of the attached demo application(s). It should
be child's play now, as we've covered all the key elements. There's nothing new to
say, apart from how the demo app uses all this stuff (though, some of it we've
already discussed). So it should be just a question of explaining it all now.
Well how about we start with a sequence diagram (I know UML isn't that great for
distributed apps, so I've annotated it with comments, but I hope you get the general
idea).
I apologize that the text on this diagram is so small, but that's down to the
restrictions on image sizes here at The Code Project. I'll even give you some class
diagrams, for those that prefer them. Remember that there are 3 separate
assemblies (ChatService / Common / WPFChatter), which I talked about earlier:
ChatService
In order for this service to work correctly, there is a special configuration file. It
could also have been done in code, but App.Config is just more flexible. So, let's
have a look at the ChatService.exe App.Config, shall we? Well, it looks like this:
<services>
<service name="Chatters.ChatService" behaviorConfiguration="MyBehavior">
<endpoint address=""
binding="netTcpBinding"
bindingConfiguration="DuplexBinding"
contract="Chatters.IChat" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyBehavior">
<bindings>
<netTcpBinding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
As you can see, this App.Config file contains all the information required to enable
the service to operate. WCF supports a lot of different binding options, such as:
This is a very simple class that is used by the ChatService and WPFChatter
assemblies. This class represents a single chatter and may be sent over the WCF
channel due the special WCF annotations that have been applied to this class. The
entire class is listed below, as it's not so big:
namespace Common
{
#region Person class
/// <summary>
/// This class represnts a single chat user that can participate in
/// this chat application
/// This class implements INotifyPropertyChanged to support one-way
/// and two-way WPF bindings (such that the UI element updates when
/// the source has been changed dynamically)
/// [DataContract] specifies that the type defines or implements a
/// data contract and is serializable by a serializer, such as
/// the DataContractSerializer
/// </summary>
[DataContract]
public class Person : INotifyPropertyChanged
{
#region Instance Fields
private string imageURL;
private string name;
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Ctors
/// <summary>
/// Blank constructor
/// </summary>
public Person()
{
}
/// <summary>
[DataMember]
public string Name
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Name");
}
}
#endregion
#region OnPropertyChanged (for correct well behaved databinding)
/// <summary>
/// Notifies the parent bindings (if any) that a property
/// value changed and that the binding needs updating
/// </summary>
As you can see, this class has some special attributes which still haven't been talked
about. It also inherits from a strange interface. So what's that all about, then? Well,
[DataContract] specifies that the type defines or implements a data contract and
is serializable by a serializer such as DataContractSerializer. The
INotifyPropertyChanged interface is a special WPF interface that allows the class
to notify and binding container of a change in a value. Thus, when a property is
changed, the binding container will be informed. This applies to the Window1.xaml
ListView in my case, as described in the WPF: Databinding section.
WPFChatter
The app.config file and WCF proxy were created using the WCF scvutil.exe tool, as
mentioned in the WCF: Creating the Proxy section. The finished WPF application is as
shown below:
What I'm going to do now is explain a bit about how all the WCF/WPF stuff works in
conjunction with each other, using the attached demo app. I think the best place to
start would be to describe the actual ChatService (the server end) and why it's
adorned the way it is. I will not go into all the inner workings of the ChatService.cs
file just yet, as this will be visited later.
ChatService.cs
Collapse Copy Code
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(IChatCallback))]
interface IChat
{
[OperationContract(IsOneWay = true, IsInitiating = false,
IsTerminating = false)]
void Say(string msg);
interface IChatCallback
{
[OperationContract(IsOneWay = true)]
void Receive(Person sender, string message);
[OperationContract(IsOneWay = true)]
void ReceiveWhisper(Person sender, string message);
[OperationContract(IsOneWay = true)]
void UserEnter(Person person);
[OperationContract(IsOneWay = true)]
void UserLeave(Person person);
}
public class ChatEventArgs : EventArgs
{
public MessageType msgType;
public Person person;
public string message;
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ChatService : IChat
{
...
}
So that's the ChatService's WCF attributes explained, but what about the client-
side code? Remember that we used svcutil.exe to create a proxy object the way I
previously described. So I am not going to go into that in too much detail. However,
the full proxy code (ChatService.cs, in the WPFChatter project in the demo code) is
shown below. With the descriptions just given, and the previously mentioned
attribute tables, you should be able to see what's going on I hope.
/// <summary>
/// This class was auto generated by the svcutil.exe utility.
/// The www.codeprject.com article will explain how this class
/// was generated, for those readers that just need to know.
/// Basically, anyone like me.
/// </summary>
#region IChat interface
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",
"3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(CallbackContract = typeof(
IChatCallback), SessionMode = System.ServiceModel.SessionMode.Required)]
public interface IChat
{
[System.ServiceModel.OperationContractAttribute(AsyncPattern = true,
Action = "http://tempuri.org/IChat/Join">http://tempuri.org/IChat/Join,
ReplyAction = "http://tempuri.org/IChat/JoinResponse")]
System.IAsyncResult BeginJoin(Person name, System.AsyncCallback callback,
object asyncState);
[System.ServiceModel.OperationContractAttribute(IsOneWay = true,
IsInitiating = false, Action = "http://tempuri.org/IChat/Leave")]
void Leave();
[System.ServiceModel.OperationContractAttribute(IsOneWay = true,
IsInitiating = false, Action = "http://tempuri.org/IChat/Say")]
void Say(string msg);
[System.ServiceModel.OperationContractAttribute(IsOneWay = true,
IsInitiating = false, Action = "http://tempuri.org/IChat/Whisper")]
void Whisper(string to, string msg);
}
#endregion
#region IChatCallback interface
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",
"3.0.0.0")]
public interface IChatCallback
{
[System.ServiceModel.OperationContractAttribute(IsOneWay = true,
Action = "http://tempuri.org/IChat/Receive")]
void Receive(Person sender, string message);
[System.ServiceModel.OperationContractAttribute(IsOneWay = true,
Action = "http://tempuri.org/IChat/ReceiveWhisper")]
void ReceiveWhisper(Person sender, string message);
[System.ServiceModel.OperationContractAttribute(IsOneWay = true,
Action = "http://tempuri.org/IChat/UserEnter")]
void UserEnter(Person person);
[System.ServiceModel.OperationContractAttribute(IsOneWay = true,
Action = "http://tempuri.org/IChat/UserLeave")]
void UserLeave(Person person);
}
#endregion
#region IChatChannel interface
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",
"3.0.0.0")]
public interface IChatChannel : IChat, System.ServiceModel.IClientChannel
{
}
#endregion
#region ChatProxy class
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",
"3.0.0.0")]
public partial class ChatProxy : System.ServiceModel.DuplexClientBase<IChat>,
IChat
{
public ChatProxy(System.ServiceModel.InstanceContext callbackInstance)
:
base(callbackInstance)
{
}
OK, so now that I've described the skeleton, boiler plate code that both the
Server/Client use to carry out their communications, let's dive in to see how the
actual code utilizes the ChatService (server end) and the client side proxy object
also called ChatService. I think the best way to do this is to list out one of the
operations (such as Join) and follow it from start to finish, from client to server.
One thing to note though, before I start going into this stuff fully, is that I needed to
be able to call the client side proxy object methods from several different
windows/controls within the attached WPF project. To this end, there is a class called
Proxy_Singleton.cs that is really just a singleton wrapper object around all the
different operations that need to be carried out for the ChatService to work
correctly. It simply enables the client code to obtain the current singleton object and
call its methods without having to worry about instantiating a new proxy object, or
keeping track of the correct proxy object.
Join
This probably needs some code snippets to help explain, so let's start with the
Proxy_Singleton.cs Connect code:
if (list == null)
{
MessageBox.Show("Error: List is empty", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
ExitChatSession();
}
else
{
ProxyEventArgs e = new ProxyEventArgs();
e.list = list;
OnProxyEvent(e);
}
}
/// <summary>
So this ends up calling the Join method in the ChatService (server), which then
uses IChatCallback to call the client back using the appropriate IChatCallback
method. For example:
/// <summary>
/// This method is called when ever one of the chatters
/// ChatEventHandler delegates is invoked. When this method
/// is called it will examine the events ChatEventArgs to see
/// what type of message is being broadcast, and will then
/// call the correspoding method on the clients callback interface
/// </summary>
/// <param name="sender">the sender, which is not used</param>
/// <param name="e">The ChatEventArgs</param>
private void MyEventHandler(object sender, ChatEventArgs e)
{
try
{
switch (e.msgType)
{
case MessageType.Receive:
callback.Receive(e.person, e.message);
break;
case MessageType.ReceiveWhisper:
callback.ReceiveWhisper(e.person, e.message);
break;
case MessageType.UserEnter:
callback.UserEnter(e.person);
break;
case MessageType.UserLeave:
callback.UserLeave(e.person);
break;
}
}
catch
{
Leave();
}
}
.....
/// <summary>
///loop through all connected chatters and invoke their
///ChatEventHandler delegate asynchronously, which will firstly call
///the MyEventHandler() method and will allow a asynch callback to
///call
///the EndAsync() method on completion of the initial call
/// </summary>
/// <param name="e">The ChatEventArgs to use to send to all
/// connected chatters</param>
Say
The methodology for the Say operation is similar to the Join operation described
above, with the following key differences:
Whisper
The methodology for the Whisper operation is similar to the Join operation, with
the following key differences:
Leave
The methodology for the Leave operation is similar to the Join operation, with the
following key differences:
That's It
I hope this article shows that all these new Microsoft technologies can actually work
together quite well. I have to say, I think this application looks cool. I have been
messing around with WPF for a while, nothing serious, but with this one, I tried to
have a go. So I hope some of you like it.
I would just like to ask, if you liked the article, please vote for it. Also, leave some
comments, as it lets me know if the article was at the right level or not, and whether
it contained what people need to know.
Conclusion
I have enjoyed writing this WPF/WCF article and I have now become hopelessly
addicted to .NET 3.0/3.5. So if you want to keep a work/life balance, I would suggest
you stay far away from .NET 3.0/3.5 if at all possible, because once it's got you in its
little tentacles, there really is no escape. "Resistance Is Futile. You Will Be
Assimilated."
That said, once you get your head around the vast learning curve that is .NET 3.0,
it's really much better IMHO than previous .NET 2.0 offerings.
Introduction
This article includes WCF Introduction, Sample WCF Service Library, Hosting of WCF
Service at Windows Service and consuming the WCF Service at Web Application.
Background
WCF Introduction
This definition is from wikipedia and Microsoft, For more detail you can refer
http://en.wikipedia.org/wiki/Windows_Communication_Foundation
One of the good article is available at code project site. you can refer this article
WCFOverview.aspx
These are the steps needed to be followed to create a WCF Service Library with
Visual Studio 2008
We need to make the necessary changes in the above section of App.Config if we are
renaming the services; otherwise, external systems cannot identify the services.
IService1.cs
Collapse Copy Code
//<createdby> Murali Manohar Pareek </createdby>
//<date> Oct 23, 2008 </date>
//<description> WCF Service</description>
//<version> 1.0.0.0 </version>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCFServiceSample
{
// NOTE: If you change the interface name "IService1" here,
// you must also update the reference to "IService1" in App.config.
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
}
Service1.cs
Collapse Copy Code
namespace WCFServiceSample
{
// NOTE: If you change the class name "Service1" here,
you must also update the reference to "Service1" in App.config.
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
App.Config
Collapse Copy Code
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<!-- When deploying the service library project, the content of the config
file
must be added to the host's
app.config file. System.Configuration does not support config files for
libraries. -->
<system.serviceModel>
<services>
<service name="WCFServiceSample.Service1" behaviorConfiguration=
"WCFServiceSample.Service1Behavior">
<host>
<baseAddresses>
<add baseAddress = "http://localhost:8731/Design_Time_Addresses
/WCFServiceSample/Service1/" />
</baseAddresses>
</host>
<!-- Service Endpoints -->
<!-- Unless fully qualified, address is relative to base address
supplied
above -->
<endpoint address ="" binding="wsHttpBinding" contract=
"WCFServiceSample.IService1">
<!--
Upon deployment, the following identity element should be removed
or replaced to reflect the identity under which the deployed service runs.
If removed, WCF will infer an appropriate identity
automatically.
-->
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<!-- Metadata Endpoints -->
<!-- The Metadata Exchange endpoint is used by the service to describe
itself to clients. -->
<!-- This endpoint does not use a secure binding and should be secured
or removed before deployment -->
<endpoint address="mex" binding="mexHttpBinding"
contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WCFServiceSample.Service1Behavior">
<!-- To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint above
before deployment -->
<serviceMetadata httpGetEnabled="True"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
When we run this application as like simple windows or web application, it will host
the application automatically and run the Test Client. We can use Test Client as
follows:
Here we will host the WCF Service on Managed Windows Service. We will Add New
Project as follows:
Step 1: Add New Project
Step 5: Now we will open the WCF Service Host. To open the service
Host, we need System.ServiceModel Library as a reference into
the windows service.
Step 6: Windows service itself does not have any configuration file
related to WCF Service Configuration like endpoint, behavior
etc. so we have to include App.Config file into the windows
service. We simply add Application Configuration File and make
the same copy as App.Config file in WCFServcieSample
application.
Step 7: Now we add the code in Windows service to open the Host.
Step 8: Add Windows Service Code
Collapse Copy Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.ServiceModel;
using System.IO;
namespace WCFServiceSampleWindowsServiceHost
{
public partial class Service1 : ServiceBase
{
ServiceHost host;
FileStream fs;
StreamWriter m_streamWriter;
public Service1()
{
InitializeComponent();
}
try
{
if (host != null)
{
host.Close();
}
m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
m_streamWriter.WriteLine(DateTime.Now + " WCF: Host
open successfully \n");
if (host != null)
{
host = null;
}
}
finally
{
m_streamWriter.Flush();
m_streamWriter.Close();
m_streamWriter = null;
fs.Close();
fs = null;
}
}
try
{
if (host != null)
{
host.Close();
host = null;
m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
m_streamWriter.WriteLine(DateTime.Now + " WCF: Host close
successfully \n");
m_streamWriter.WriteLine(DateTime.Now + " WCF: Stopped
successfully \n");
}
}
catch (Exception ex)
{
m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
m_streamWriter.WriteLine(DateTime.Now + " WCF: Error in
closing Host : " + ex.Message + "\n");
}
finally
{
m_streamWriter.Flush();
m_streamWriter.Close();
m_streamWriter = null;
fs.Close();
fs = null;
}
}
}
}
Step 9: Add Installer. It will add Service Process Installer and Service
Installer. Service Process Installer takes care for Service Account
related information and Service Installer takes care of service
related information like ServiceName, DisplayName, StarupType
etc.
Step 10: Set the ServiceProcessInstaller Property as required.
Step 11: Set the ServiceInstaller Property as required and build the
application.
To run the windows service we have to install the windows service. There is two
option to install the windows service.
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe
D:\murali\projects\WCF
Demo\SampleWCFService\WCFServiceHost\bin\Debug\
WCFServiceSampleWindowsServiceHost.exe
Step 10: Now open Services using Control Panel -> Administrative Tools
-> Services. It will show the list of available service. Select our
“WCFServiceSample” Service and Start it.
Step 11: Starting Service.
Create the instance of the WCF client to communicate to the WCF service as follows:
Enter the input into the given text box and get the output by pressing the button.
Conclusion
This article includes WCF Introduction, Sample WCF Service Library, Hosting of WCF
Service at Windows Service and consuming the WCF Service at Web Application. I
will include details about configuring a service and a client for different types of
communication in my next article very soon.
History
Introduction
I am by no means a WCF expert, but I have been using it a bit at work of late, and I
have come across a few issues that I thought may be worth mentioning in an a top-
tips style article.
So that is what this article will be.
This article assumes you know a little bit about WCF, and that you know enough to
edit config files.
Please make sure to read the "Running the code" section before you try to run it.
One of the first things that you will most likely want to do is call a WCF service and
debug it. Now when I first started creating WCF services at my company I created
IIS web hosted services. While this may be a great end solution, it doesn't really
offer the opportunity to be debugged that easily. So my advice would be to create a
Console based implementation of your WCF service and use this for debugging
purposes, and then when you are happy you can push the tested code back into an
IIS web based service.
The attached demo app uses a Console hosted (self hosted) WCF service, which is
hosted as follows:
namespace WcfServiceLibrary1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("-------------------------------------------------------------
--------");
Console.WriteLine("This is the WCF Service hosting within a console
application");
Console.WriteLine("The service can also be hosted with a web
browser");
Console.WriteLine("");
Console.WriteLine("Initializing WCF Service...");
Console.WriteLine("Closing service...");
}
}
}
}
If we consider how the attached demo app WCF service is configured at the server
end.
netTcpBinding
HttpBinding
MexBinding
net.tcp://localhost:8000/ServiceHost/
http://localhost:8001/ServiceHost/
My personal experience was that, I could never find the running service if I tried to
add a service reference using the "tcp" host address. I ALWAYS had to use the "http"
host address to find the running service. This is shown below.
So this failed where I tried to use "Tcp" host address. But if I used the "Http" host
address all is cool
So I would recommend always creating a http binding to allow you to gain access to
the service eevn if you don't end up using the http binding.
Amendment
One of the readers of this article, Benoît Dion, suggested that I could fix this with
altering the mexBinding end point in the service end.
So this is what I did with this small App.Config change at the service end.
So thanks Benoît.
In the attached demo app, the WCF service definition looks like the following
namespace WcfServiceLibrary1
{
/// <summary>
/// The service contract
/// </summary>
[ServiceContract(
Name = "IService",
Namespace = "WcfServiceLibrary1.Service")]
public interface IService
{
[OperationContract]
[ReferencePreservingDataContractFormat]
List<Person> GetPeople();
[OperationContract]
List<Person> GetPeopleWithOutCicularReferencing();
int age = 0;
string name = string.Empty;
List<Person> children = new List<Person>();
Person parent = null;
[DataMember]
public int Age
{
get { return age; }
set { age = value; }
}
[DataMember]
public string Name
{
get { return name; }
set { name = value; }
}
[DataMember]
public List<Person> Children
{
get { return children; }
set { children = value; }
}
[DataMember]
public Person Parent
{
get { return parent; }
set { parent = value; }
}
}
}
It can be seen that the service contract operations (methods) return generic
List<T>, which is personal choice. But what you must make sure is that the proxy
(client) for the WCF service actually returns the same sort of objects. This is
achieved by configuring the WCF service reference when you add it.
To add a reference you must ensure that you have access to the running WCF
service host (ie the actual service), in the attached demo this is the Console app.
So providing you have access to the actual WCF service, you should ensure that you
configure the service to use the same types as the operation contracts that were
defined within the service contract interface. Which in the case shown above was
generic List<T>, so we can change this in the drop down combo boxes provided
within the Data Type section of the service reference settings dialog.
If you do not have a service reference and are in the process of adding it, this
service reference settings dialog, is accessable from the Advanced button of the add
service reference dialog.
Tip4 : Service Parameters
When the WCF service was eventually added it was found that some of the default
parameters just were not big enough. Shown below is the before and after
App.Config for the client.
So I changed some of these parameters to improve the service throughput
Tip5 : Circular References
As part of something we were doing I needed to have a circular reference, you know
when object A holds a reference to object B and object B holds a reference to object
A.
What was found was that the default DataContractSerializer couln't to set to
deal with serializing a cyclic object graph in a config file, even though it is totally
capable of doing this task. It appeared that some of the properties of the
DataContractSerializer can only be set via a constructor call, not via config
files. So I did a bit of research into this and found some interesting code that allowed
you to create a specialized DataContractSerializer that would allow cyclic
object graphs to be serialized. The code that I found allowed you to adorn your
service interface operation contracts with a custom attribute called
ReferencePreservingDataContractFormatAttribute
namespace WcfServiceLibrary1
{
/// <summary>
/// Allows us to adorn the IService contract with this
/// attribute to indicate that a specialized DataContractSerializer
/// should be used that has preserveObjectReferences set true
/// </summary>
public class ReferencePreservingDataContractFormatAttribute
: Attribute, IOperationBehavior
{
#region IOperationBehavior Members
public void AddBindingParameters(OperationDescription description,
BindingParameterCollection parameters)
{
}
public void ApplyClientBehavior(OperationDescription description,
System.ServiceModel.Dispatcher.ClientOperation proxy)
{
IOperationBehavior innerBehavior =
new
ReferencePreservingDataContractSerializerOperationBehavior(description);
innerBehavior.ApplyClientBehavior(description, proxy);
}
#endregion
}
}
The importance of this class may become clearer if we see an example screen shots
from the attached demo where the following applies:
[OperationContract]
List<Person> GetPeopleWithOutCicularReferencing();
So we can see that the 1st call worked just fine, as we used the
ReferencePreservingDataContractFormatAttribute, so is safe to return
circular references
But the next call failed completely, and resulted in a communication Exception. This
is because the standard DataContractSerializer, doesnt have the
preserveObjectReferences parameter turned on.
I can take no resposibility for creating either the
ReferencePreservingDataContractFormatAttribute or the
ReferencePreservingDataContractSerializerOperationBehavior code.
This is down to Sowmy Srinivasan, whos blog I found it on. I totally get what its
about though, as I hope you do now. You can read more at Sowmy Srinivasan's Blog
Wrap Up
I am fully aware that these tips may not be suitable for everyones WCF services, but
they are things that I found helped my own code, as such I thought it may be worth
sharing what I found. I hope at least one of these tips is useful to you.
You will need to change the App.Config in the ServiceClientTestApp project to use
your own PC user.
To be something like
<userPrincipalName value="YOUR_PC_NAME\YOUR_USER"/>
If you dont change this, the ServiceClientTestApp project will not work.
We're Done
If you liked this article and found it useful please leave a vote/comment.