Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

The Baker's Dozen: 13 Miscellaneous C#, SQL, and Business Intelligence Development Tips : Page 2

Windows Communication Foundation is one of the most important features in the history of .NET. Not only does WCF provide a unified programming model for communications, it also promotes the use of interfaces and various recommended programming practices.


advertisement

Tip 1: Creating Basic WCF Factory Classes, Building Contracts (Part 1 of 3)

To begin this three-part tip, I'll dive right in and discuss the fairly simple requirements for this little WCF exercise: a back-end function to validate a user ID and password. While that may seem like a mundane function, the process allows you to see the basics of WCF in action. So I'll cover those basic steps in this order:

  1. Create a class called LoginResult as a WCF DataContract, with WCF DataMember properties for Full User Name, ActiveFlag, LoginMessage, and a Boolean property that controls whether a user should be allowed in the system. The LoginResult class will reside in a separate assembly with other DataContract classes.
  2. Create an interface called IUser as a WCF ServiceContract, with a WCF OperationContract called ValidateUserID.
  3. Create a service that implements the IUser Service Contract, and then host the service in IIS.
  4. Create a client-side remote factory called GetRemoteFactoryChannel that returns a WCF channel for the type of service being referenced.
This tip will cover steps 1 and 2 of the exercise and then the next two tips will cover steps 3 and 4.

I'll start by creating a class library project called DataContracts. In the project, I'll add a .NET reference to System.RunTime.Serialization. This reference is required as the DataContract represents the default serialization model in WCF. In the project, I'll add the class below, which is essentially a basic class with four properties, all marked with WC DataContract and DataMember attributes. I'll compile this as DataContracts.DLL:

// Basic entity class for result set, as a WCF DataContract using System; using System.Collections.Generic; using System.Runtime.Serialization; namespace StoneWorksJob.ResultSets { [DataContract] public class clsLoginResult { [DataMember] public string UserName { get; set; } [DataMember] public int ActiveFlag {get; set; } [DataMember] public string LoginMessage {get; set; } [DataMember] public bool OKToLogin { get; set; } } }

Next, I'll create a second class library project called ServiceContracts. In the project, I'll add a .NET reference to the DataContracts.DLL assembly, as well as the main WCF namespace, System.ServiceModel. In this project, I'll add the following class, which is essentially a .NET interface with a function call to ValidateUserID of type LoginResult. Similar to the preceding class, I've marked the interface and method with WCF ServiceContract and OperationContract attributes. I'll compile this as ServiceContracts.DLL:

// Interface, as a WCF Service Contract using System; using System.Collections.Generic; using System.ServiceModel; using System.ServiceModel.Channels; using StoneWorksJob.ResultSets; namespace StoneWorksJob.Contracts { // WCF ServiceContract and OperationContract attributes [ServiceContract] public interface IUser { [OperationContract] clsLoginResult ValidateUserID(string UserID, string PassWord, int DatabaseKey); } }

Tip 2: Creating Basic WCF Factory Classes, Hosting a Service in IIS (Part 2 of 3)

Now that I've built a data contract and service contract, I can create a service that implements the data contract IUser. To do this, I'll create a third class library project called Services. In this project, I'll add .NET references to the DataContracts.DLL and ServiceContracts.DLL assemblies, as well as the main WCF namespace, System.ServiceModel.

In the project, I'll add the following class, named svcUser, which implements the IUser contract, and contains the OperationContract method ValidateUserID. I'll compile this as Services.DLL:



// Basic service that implements the IUser ServiceContract using System; using System.Collections.Generic; using System.ServiceModel; using System.ServiceModel.Channels; using StoneWorksJob.ResultSets; namespace StoneWorksJob.Services { public class svcUser : IUser { public clsLoginResult ValidateUserID (string UserID, string Password, int DatabaseKey) { bzUser User = new bzUser(); clsLoginResult LoginResult = User.ValidateUserID(UserID, Password, DatabaseKey); return LoginResult; } } }

So at this point I have three DLLs: DataContracts.DLL, ServiceContracts.DLL, and Services.DLL. Next I'll use IIS to host the Services.DLL so I can access the service from a client.

Hosting a WCF service in IIS is a breeze—most of the time! A while back, I had to host a WCF service on a shared web hosting service provider. When I tried to test my service, I received the following error: This collection already contains an address with scheme http. There can be at most one address per scheme in this collection.

Many web service providers have more than one host header bound to the site. Fortunately, two blogs http://www.darkside.co.za/Default.aspx and http://www.robzelt.com/blog) offer excellent workarounds for this problem. The workaround is to override the ServiceHostFactory with code to check the number of Uri base addresses.

The following code shows a class called CustomServiceHostFactory that overrides the system ServiceHostFactory class, and creates a new ServiceHost object from either the first base address or the second base address. You may want to modify this class so it iterates through the baseAddresses collection for the one you want:

// Custom class that inherits from ServiceHost using System; using System.ServiceModel; using System.ServiceModel.Activation; class CustomServiceHostFactory : ServiceHostFactory { protected override ServiceHost CreateServiceHost (Type serviceType, Uri[] baseAddresses) { ServiceHost serviceHost; if (baseAddresses.Length > 1) serviceHost = new ServiceHost( serviceType, baseAddresses[1]); else serviceHost = new ServiceHost( serviceType, baseAddresses[0]); return serviceHost; } }

The Service.SVC file for the web project that references the CustomServiceHostFactory is as follows:

<%@ ServiceHost Language="C#" Debug="true" Service="StoneWorksJob.Services.svcUser" Factory="CustomServiceHostFactory" %>

Tip 3: Creating Basic WCF Factory Classes, The Client (Part 3 of 3)

At this point, I can build a client piece to call the WCF service. In this example, I'll call the service from a .NET Windows Forms application. In the past, I might have done something like this:

ChannelFactory<IUser> UserFactory = new ChannelFactory<IUser>("User"); IUser userProxy = UserFactory.CreateChannel();

For years, some have made fun of me for trying to combine two lines of code into one. Guilty as charged! I'm usually interested in simplifying things, especially if I can create a simpler interface where others (or I myself) don't have to worry about the exact syntax. So given that any client-side call to the WCF service requires two lines of code above, I'd like to combine that into one, for all the different services that I might call.

Here's a small factory class (in namespace ClientRemoteAccess) called GetRemoteFactoryChannel:

// Factory class to create a "generic" WCF Factory channel public T GetRemoteFactoryChannel<T>() { Binding oBinding = this.GetBinding(); ChannelFactory<T> oChannelFactory = new ChannelFactory<T>(oBinding); oChannelFactory.Endpoint.Address = new EndpointAddress(this.ServerURL + "\\" + this.ServiceName); oChannelFactory.Credentials.Windows.ClientCredential.UserName = this.UserName; oChannelFactory.Credentials.Windows.ClientCredential.Password = this.PassWord; return oChannelFactory.CreateChannel(); } private Binding GetBinding() { if (this.UsingTCP == true) return new NetTcpBinding(); else return new WSHttpBinding(); }

All I have to do is pass it the name of the contract interface that I'm using and the function will return a factory channel that I can use to call my service in a strongly-typed fashion:

// Using the Factory class to call the back-end service // Example of using the Factory class public clsLoginResult ValidateUserID(string UserID, string Password, int DatabaseKey) { // Define object of the type of the interface (contract) IUser oUser; // Create instance of the Remote Access library ClientRemoteAccess oRemoteAccess = new ClientRemoteAccess(); // Create instance of oUser, by calling the factory, // and specifying the type of IUser oUser = (IUser)oRemoteAccess. GetRemoteFactoryChannel<IUser>(); // Now that we have oUser as a proxy class, we can call it clsLoginResult oResult = oUser.ValidateUserID( UserID, Password, DatabaseKey); return oResult; }



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap