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
 

WCF the Manual Way…the Right Way : Page 5

Don't be lured by Visual Studio's promise of simple templates for creating WCF services; here's why you should plan to create your services manually.


advertisement
The Consumer Side
The last step is to code the consumer side of the equation to show you how the specific component separation applies here as well.

Client Proxies
A client application uses client proxy classes to connect to a service and use its operations. Along with copies of data and service contracts, Visual Studio creates client proxies automatically when you use the "Add Service Reference" feature—but that comes with a lot of excess unneeded configuration. Besides, one point of separating the contracts into their own assembly is so I can reuse them. .NET ships with a utility called SvcUtil that you can use to create proxy classes and contract copies, given a services endpoint, but the results have the same problem as the "Add Service Reference" option. Therefore, not only will I create my client proxies manually, but I'll put them into their own project called ClientProxies. This lets me reuse them from any client application. I can certainly create more than one proxy client project if needed, but for simplicity, I'll build just one project here. I will have more than one class, though: ProductClient and AdminClient.

I'll use two different techniques to code the two proxy classes for reasons that I'll explain as I go along.

The ProductClient class uses the same technique that Visual Studio does when you use the "Add Service Reference" feature. The class needs to import the System.ServiceModel namespace, so the ClientProxies project needs to reference the System.ServiceModel assembly. The ProductClient class will inherit from the ClientBase class and pass as the generic argument the service contract I want this proxy to access, in this case, IProductBrowser. The result of this generic-oriented inheritance is that the ClientBase class's Channel property adopts all the operations of the IProductBrowser service contract.

The next thing that the class needs to do is to implement the IProductBrowser interface to inherit its operations. Putting these two characteristics together, you get the following:

using System; using System.ServiceModel; namespace CoDeMagazine.ServiceArticle { public class ProductClient : ClientBase<IProductBrowser>, IProductBrowser { #region IProductBrowser Members public ProductData GetProduct( Guid productID) { return Channel.GetProduct(productID); } public ProductData[] GetAllProducts() { return Channel.GetAllProducts(); } public ProductData[] FindProducts( string productNameWildcard) { return Channel.FindProducts( productNameWildcard); } #endregion } }

As you can see, the ProductClient class literally becomes a proxy to my service; an entry point into accessing the services implementation. Except for configuration, which will reside at the client application level, that's all there is to this technique of proxy class creation.

The second technique is handy because—as you can see—the ProductClient class inherits from ClientBase using up its sole base class candidate. Because .NET allows only single inheritance, I'm stuck with inheriting from this class if I want to access my channel. However, there may be situations where I want to set up an object model of my own, yet still have the ability to access a services channel, in which case I would want to inherit from a class in my object model, not the ClientBase class.

Another situation where the previous technique would not suffice is if I want to create a proxy class to handle more than one service contract. Inheriting from the ClientBase class restricts the client proxy class to only one service contract. Fortunately, there is a way to obtain a service's channel without automatically having a class like ClientBase create it for me.

This time, I'll use the ChannelFactory class to create a channel for me. When I instantiate this class, I pass it the service contract I want to represent, then call the CreateChannel method. The end result is a channel class returned as the type of the service contract interface I gave the ChannelFactory class. You can then use the variable holding the results of this call to access the service contract's operations. The proxy class is free to implement one or more service contract interfaces. Here's how you can use the channel object in method implementations:

using System; using System.ServiceModel; namespace CoDeMagazine.ServiceArticle { public class AdminClient : IProductAdmin { public AdminClient() { IProductAdmin productAdminChannel = new ChannelFactory<IProductAdmin>(). CreateChannel(); } IProductAdmin productAdminChannel = null; #region IProductAdmin Members public void UpdateProduct( ProductData product) { productAdminChannel.UpdateProduct( product); } public void DeleteProduct(Guid productID) { productAdminChannel.DeleteProduct( productID); } #endregion } }

The idea here is that if I were implementing the entire architecture illustrated in Figure 2, I would also have an OrderClient proxy class, which would be used to access the operations on the IOrderPlacement service contract. However, I would like the IOrderBrowser and IOrderAdmin service contracts to be handled by the AdminClient proxy client, in which case I couldn't inherit from ClientBase. Instead I would use the technique I've just illustrated and also implement the other two interfaces. I would, of course, have two more channel objects in the class.

Whether this is the ideal way of implementing the architecture is certainly a topic for discussion, but my primary intent here was to free you from the built-in templates, illustrate examples of multiple techniques, and provide reasons for using them.

With the proxy classes completed, I can compile this assembly and reference it from any client application I want. The client applications can instantiate ProductClient and AdminClient objects and use their methods the same way they'd use any other object.

Client Applications
I won't bore with details about how I would write my client applications, but it's worth repeating the main point: Any client application can access any of the service contracts merely by referencing the appropriate client proxy assembly and instantiating its proxy classes. However, I will give one example, primarily to show you how to configure the client.

The client applications require not only references to the proxy assemblies, but also some configuration information. The proxies are designed to access specific service contracts but the configuration defines where those contracts can be found. In the same way that service hosts expose endpoints in a <services> section, clients require the endpoint definitions in a <clients> section:



<system.serviceModel> <client> <endpoint address= "net.tcp://localhost:8002/ProductBrowser" binding="netTcpBinding" contract="CoDeMagazine.ServiceArticle. IProductAdmin" /> <endpoint address= http://localhost:9224/ProductService.svc binding="wsHttpBinding" contract="CoDeMagazine.ServiceArticle. IProductBrowser" /> </client> </system.serviceModel>

As you can see here, I'm defining two endpoints, one for each service contract. If you recall, each of these was implemented by the same service then hosted by two different hosting applications; so you can see the flexibility potential here.

If I want to use the operations defined in the IProductBrowser service contract, I simply need to instantiate whichever proxy class opens a channel to that endpoint. I this case, it would be the ProductClient class, and from here it's simply a matter of accessing its methods as so:

ProductClient proxy = new ProductClient(); Guid productID = Guid.NewGuid(); ProductData product = proxy.GetProduct(productID); MessageBox.Show("The product retrieved is: " + product.ProductName);

The preceding code makes it clear that the object can indeed be treated like any other object with methods that you've used in the past. In fact you can take a proxy object like the one stored in the proxy variable above and use it in data binding. I constantly hear arguments against service-oriented architecture saying that it goes against object-orientation because it's too procedural, or that you can't use it in data binding, or that you can't use business frameworks because most of them promote state and persistence and SOA does not. I'm here to tell you that this is absolutely not the case.

One of my areas of expertise is in the popular CSLA business framework developed by Rockford Lhotka. CSLA is a framework that offers much to both Windows and ASP.NET development, but due to the fundamental differences between these two platforms, lends itself to being more feature rich in a Windows platform. As an official CSLA instructor I often debate with students on the value of CSLA if they wish to implement a service-oriented architecture; their fears being that CSLA is designed to build business objects with advanced features like validation, authorization, and data binding. CSLA does indeed give you features like this but who says that the data-access portion of CSLA-based objects need to use ADO.NET (or a data-access-layer) in order for them to work? A CSLA business object's data methods can create service channels using the ChannelFactory class, access a service to retrieve information, build the rest of the business object, and still benefit from all the features that the framework offers. The technique required in such a situation is the one that uses ChannelFactory because CSLA, like most business frameworks, gives you base classes from which to inherit so using the ClientBase class is out of the question. Perhaps that's a topic for a future article.

Creating all the components of a WCF-based system manually is easy and allows for a more calculated design process, maximizing reusability and maintainability. Remember that assemblies should be logically grouped within a given category, meaning contracts, services, hosts, or proxies. But they should definitely not cover multiple categories, with the exception of data contracts and service contracts. Service orientation is not a replacement for object orientation or component orientation. Just like component-oriented architectures build on the principles of object-oriented programming, service-oriented designs use concepts of component design and bring other concepts on top of that, such as decoupled design, interoperability, and separation of concerns.

I hope I was successful in not coming across like I was bashing what Microsoft preaches for WCF and what Visual Studio does with it because I still think Visual Studio is the best IDE on the market and I think of WCF as one of the single greatest technologies ever to come out of our friends in Redmond.



Miguel A. Castro is President of InfoTek Consulting Group, Inc., a professional consulting firm that specializes in architecting, designing, and developing solutions using Microsoft .NET technologies. His VB background goes all the way back to 1.0. Miguel's focus for the last couple of years has been the .NET Framework and languages; he is fluent in both VB .NET and C#. His programming experience goes back 20 years when he started on TRS-80s, Apple IIs, and Atari 800 computers. Miguel lives in Lincoln Park, New Jersey with his wife Elena and his daughter Victoria.
Comment and Contribute

 

 

 

 

 


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

 

 

Sitemap