Friday, December 28, 2007

WCF Notes

WCF Security

Microsoft WCF is designated to develop Service Oriented Architecture (SOA) application. Under SOA, modules of an application are services, that most likely are deployed in different hosts. The major type of communication within the system are communications between service providers and service consumers, and security is one of the most concerning aspects.

There are two type of plot to ensure securityc ommunication, message based security and transport layer based security. Both can be implemented with configuration settings, i.e., it is transparent to service provider and service consumer, developers don't need to write any code for implementing security. .Net implements them bebind the scene.

In order to indicate .Net what security messure to use, you need to customize the service binding entries in application's configuration file. Note that the configuration file can be web.config, app.config or theApplicationName.config based on the type of application.

The following snippet shows an example of a service configuration.

<services>
<service type="DerivativesCalculator.DerivativesCalculatorServiceType,DerivativesCalculatorService">
<endpoint
address=""
binding="basicHttpBinding"
contract="DerivativesCalculator.IDerivativesCalculator,DerivativesCalculatorService" />
</service>
</services>

By setting the value of binding attribute to "basicHttpBinding", it indicates .Net to uses WS-I Basic Profile 1.1 protocol for communication. This protocol, dominately used in web service, defines the HTTP elements and the XMLInfoSet message encoding features. By default, it doesn't have security messures applied.


The following snippet changes the default value of bindingConfiguration attribute of the system default binding object. The value of "SecureBasicHttpBinding", which refers to the binding entry with security mode set to "Transport". This will let .Net implement encryption on network transportation layer.

<service type="DerivativesCalculatorService.DerivativesCalculatorServiceType,DerivativesCalculatorService">
<endpoint contract="DerivativesCalculatorService.IDerivativesCalculator,DerivativesCalculatorService=" binding="basicHttpBinding" address="" bindingconfiguration="SecureBasicHttpBinding">
</service>

<bindings>
<basichttpbinding>
<binding name="SecureBasicHttpBinding">
<security mode="Transport">
<transport clientcredentialtype="None">
</security>
</binding>
</basichttpbinding>
</bindings>

Because it uses transport layer security, we need to configure IIS (the service host) to support SSL/TLS - Secure Socket Layer/ Transport Layer Security. Also, on the service consumer end, the address attribute of the endpoint entry should use "HTTPS:" scheme instead of "HTTP:", thereby redirecting the request and response through SSL/TSL socket.

Another way to ensure security is through message based encryption scheme - WS-Security. It is done just by making a bit change in the configure file. Notice the following configuration snippet:

<service type="DerivativesCalculator.DerivativesCalculatorServiceType,DerivativesCalculatorService">
<endpoint contract="DerivativesCalculator.IDerivativesCalculator,DerivativesCalculatorService" binding="wsHttpBinding" address="Calculator">
</service>

On the client side:

<client>
<endpoint name="IISHostedEndpoint" contract="IDerivativesCalculator" binding="wsHttpBinding" address="http://localhost:8000/Derivatives/Calculator">
</client>

Noticablly, what really changed is the value of binding attribute from "basicHttpBinding" to "wsHttpBinding", which indicates .Net using WS-Security scheme for the communication.

Message Logging
WCF comes with message logging facilities. It paves an easy way for tracing and administration. The facilities generate messages and output a stream, so we need also to configure a trace listener, a reguler feature in System.Diagnositics namespace, to receive it. Fortunately, what we need to do for all these is to customize the configuration file.

<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging" switchvalue="Verbose">
<listeners>
<add type="System.Diagnostics.XmlWriterTraceListener" name="xml" initializedata="c:\logs\message.log">
</listeners>
</source>
</sources>
<trace autoflush="true">
</system.diagnostics>

Above is a .Net recognizable configuration section of "system.diagostics". The TCF message logging facility, defined as a source, has one listener of XmlWriterTraceListener. The listener writes received messages to the specified file.

The following snippet shows the System.serviceModel configuration section, where WCF message logging feature is turned on and customized.

<system.servicemodel>
<diagnostics>
<messagelogging logentiremessage="true" maxmessagestolog="300" logmessagesatservicelevel="false" logmalformedmessages="true" logmessagesattransportlevel="true">
</diagnostics>
</system.servicemodel>

Generate WCF client
No matter how WCF service is hosted, to consume the service, the consumer needs to have a proxy class to access the functions exposed by the service. In addition, we need to configure client side with the right endpoint settings, which includes address, binding and contract information.

There are three ways for creating the proxy. First, use the commandline tool svcutil, which is coming with WCF. The following shows how to use it.

svcutil http://localhost:8000/Derivatives/ /out:Client.cs /config:app.config

Note, svcutil doesn't check the address, it just use the parameter to generate the proxy. So developer needs to make sure the address information is correct.

Once executed, this command
will generate the proxy class (Client.cs) and configuration entries (in app.config). Of course we can change the names of the proxy class and the config file.


The second way is to write the proxy class manually. See snippet bellow.

public partial class DerivativesCalculatorProxy :
ClientBase, IDerivativesCalculator
{
public decimal CalculateDerivative(string[] symbols,
decimal[] parameters,
string[] functions)
{
return base.InnerProxy.CalculateDerivative(symbols,
parameters,
functions);
}
}

Obviously, the proxy class is nothing but a class derives from ClientBase, and implements the service contract interface. In the body of interface implementation, It just passes the call to base class's InnerProxy.

There is still another way to create a client. Instead of creating a proxy class, it directly creates a proxy instance with ChannelFactory class. To consume the service, just make request to the proxy instance. Bellow shows how it is like.

IDerivativesCalculator proxy =
new ChannelFactory(“EndpointName”).
CreateChannel();

.svc extention
.Net descriminates different type of request by file extentions. For example, requesting a .asmx file is recognized as a web service request. However this mapping between file extention and handler class can be customized.


This feature brings benefits in the circumstance that we need to switch a web service to WCF service, for example, what we need to do is just change the mapped handler to WCF service handler for .asmx file extention.

In the .Net installation folder, Config subfolder, there is a web.config file, that is where the mapping is stored. The following snippet shows an adoption of the full mapping list.

<buildproviders>
<add type="System.Web.Compilation.PageBuildProvider" extension=".aspx">
<add type="System.Web.Compilation.UserControlBuildProvider" extension=".ascx">
<add type="System.Web.Compilation.MasterPageBuildProvider" extension=".master">
<add type="System.Web.Compilation.WebServiceBuildProvider" extension=".asmx">
<add type="System.Web.Compilation.ForceCopyBuildProvider" extension=".js">
<add type="System.ServiceModel.Activation.ServiceBuildProvider, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" extension=".svc">
</buildproviders>

In addition to builderProviders, there is an expressionBuilder section, see bellow,

<expressionbuilders>
<add type="System.Web.Compilation.ResourceExpressionBuilder" expressionprefix="Resources">
<add type="System.Web.Compilation.ConnectionStringsExpressionBuilder" expressionprefix="ConnectionStrings">
<add type="System.Web.Compilation.AppSettingsExpressionBuilder" expressionprefix="AppSettings">
</expressionbuilders>

It helps to understand that a lot of .Net features are actually customizable, which is great.

Monday, December 24, 2007

Module, Multifile Assembly

Module is a concept of component that composes an assembly.
it has a file name extention of "netmodule".

Generate a module:

csc /t:module Stringer.cs

/t: indicates the compiler to generate a module rather than an assembly. Stringer.netmodule is produced. A module can be added to an assembly.

Create references between modules

the following line creates a module with reference to another module:

csc /addmodule:Stringer.netmodule /t:module Client.cs


Create multifile assembly

The following line create a two file assmbly:

csc /out:Client.exe Client.cs /out:Stringer.netmodule Stringer.cs

In addition to modules, an assembly has manifest information (meta data).

Assembly Linker (al.exe) is used to create a assembly from a collection of modules
al Client.netmodule, Stringer.netmodule /main:TheClassName.Main /out:MyAssembly.exe /target:exe

Aside from "exe", the target can be "win" and "lib"

MSIL Disassembler
.Net tool ildasm.exe (MSIL Disassembler) can examine the content and recognize a module or an assembly.


Reflecting module info with reflection code
(source:
http://msdn2.microsoft.com/en-us/library/system.reflection.module.aspx)

using System.Reflection;
using System;
public class Program {

public static void Main() {
Class1 c1 = new Class1();
// Show the current module.
Module m = c1.GetType().Module;
Console.WriteLine("The current module is {0}.", m.Name);

// List all modules in the assembly.
Assembly curAssembly = Assembly.GetExecutingAssembly();
Console.WriteLine("The current executing assembly is {0}.", curAssembly);

Module[] mods = curAssembly.GetModules();
foreach (Module md in mods) {
Console.WriteLine("This assembly contains the {0} module", md.Name);
}
Console.ReadLine();
}
}
class Class1 {
}