Paolo Pialorsi - Bridge The Gap!

Living in a Service Oriented World
Microsoft SharePoint 2010 Developer Reference

Starting from today it is available my latest book about SharePoint 2010.

It has been a huge task to write this book and it took me almost 100% of the last year. However, I really like the result and I hope you will enjoy reading it. I’d like also to announce the availability of a new web site and blog about SharePoint 2010 development: http://www.sharepoint-reference.com/ .

Here you will find useful information and blog posts about developing solutions with Microsoft SharePoint 2010 and Microsoft Office 365.

 

In the mean time, I’m looking forward to meet you at the upcoming Microsoft SharePoint & Office Conference 2011 which will take place in Milan from 19th to 21st of April.

Posted: Apr 02 2011, 09:31 AM by paolopia | with no comments
Filed under:
Programming Microsoft LINQ in Microsoft .NET Framework 4

It is available my latest book about LINQ in .NET 4.0. You can find it here. It is a complete reviewed edition starting from the previous one. We (I and Marco Russo) wrote 6 new chapters and updated 5 already existing chapter.

The result is a book that I really like. Hope you will like it, too. Now I’m working on the SharePoint 2010 Developer Reference, which will be released in early 2011.

Enjoy your reading!

Posted: Dec 02 2010, 06:00 PM by paolopia | with no comments
Filed under: ,
My TechEd 2008 EMEA Session's demo files: OFC03-IS

Here you can find the demos of my last TechEd 2008 EMEA Session about "Deploying and Updating SharePont Solutions using features and templates". I hope you enjoy them.

Posted: Nov 24 2008, 09:44 AM by paolopia | with no comments
Filed under:
Amazing TechEd Online interview about being a book author

On Tuesday I and my friend Marco Russo have been interviewed by Ken Rosen about our book writing experience. In fact a couple of weeks ago became available our last book about LINQ. The interview focuses on the book itselft, but also on the experience to being an author for Microsoft Press.

If you are interested ... please have a look at the video (high res - low res) from TechEd Online officale web site and don't lose the chance to hear our strictly Italian English :-)

Posted: Jun 05 2008, 06:54 PM by paolopia | with no comments
Filed under:
WCF Security Guidance: Patterns & Practices

As you can argue reading this blog, I'm a WCF lover and in particular I'm really crazy for it's security infrastructure and architecture. Some Microsoft guys have made available a set of great contents ("how tos", "application scenarios", "guidelines", "practices" and "Q&A") indeed to help the community of WCF developers to build secure and interoperable WCF Services. The project is really interesting and I suggest you to take a look at it, before implementing a real WCF solution.

Posted: May 18 2008, 09:53 AM by paolopia | with 1 comment(s)
Filed under:
WCF configuration default limits, concurrency and scalability

Often I need to enumerate to my customers all the main configuration parameters related to default limits, concurrency and scalability of WCF, thus I decided to keep truck of all those parameters and features in order to have a unique place for reference.

From a configuration point of view, WCF provides some parameters that influence the availability and scalability of solutions. These parameters are:

  • configuration/system.serviceModel/behaviors/serviceBehaviors/behavior/serviceThrottling/@maxConcurrentCalls: defines the maximum number of messages actively processed by all the service instances of a ServiceHost. The default value is 16. Calls in excess of the limit are queued.
  • configuration/system.serviceModel/behaviors/serviceBehaviors/behavior/serviceThrottling/@maxConcurrentInstances: defines the maximum number of service instances that can execute at the same time. The default value is Int32.MaxValue. Requests to create additional instances are queued and complete when a slot below the limit becomes available.
  • configuration/system.serviceModel/behaviors/serviceBehaviors/behavior/serviceThrottling/@maxConcurrentSessions: defines the maximum number of sessions that a ServiceHost instace can accept at one time. The default value is 10. The service will accept connections in excess of the limit, but only the channels below the limit are active (messages are read from the channel).

These configuration parameters can also be configured by code using the ServiceThrottlingBehavior configuration.

Another set of interesting configuration parameters are those related to the default limits of messages, serialization measures, etc. of the various bindings. Here are the main ones, with the default values for each of the main bindings:

 

Parameter Description basicHttpBinding
basicHttpContextBinding
netMsmqBinding netNamedPipeBinding netTcpBinding netTcpContextBinding webHttpBinding wsHttpBinding wsHttpContextBinding wsDualHttpBinding ws2007HttpBinding
maxBufferPoolSize An integer value that specifies the maximum amount of memory that is allocated for use by the manager of the message buffers that receive messages from the channel. The default is 524,288 bytes (512 * 1024 = 0x80000 = 512Kb).  The default is 524,288 bytes (512 * 1024 = 0x80000 = 512Kb).  The default is 8 bytes. The default is 524,288 bytes (512 * 1024 = 0x80000 = 512Kb).  The default is 524,288 bytes (512 * 1024 = 0x80000 = 512Kb).  The default is 524,288 bytes (512 * 1024 = 0x80000 = 512Kb).  The default is 524,288 bytes (512 * 1024 = 0x80000 = 512Kb).  The default is 524,288 bytes (512 * 1024 = 0x80000 = 512Kb).  The default is 524,288 bytes (512 * 1024 = 0x80000 = 512Kb).  The default is 524,288 bytes (512 * 1024 = 0x80000 = 512Kb).  The default is 524,288 bytes (512 * 1024 = 0x80000 = 512Kb). 
maxBufferSize  An integer value that specifies the maximum size, in bytes, of a buffer that stores messages while they are processed for an endpoint configured with this binding. This value cannot be less than the next maxReceivedMessageSize attribute. The default is 65,536 bytes (64Kb). The default is 65,536 bytes (64Kb). Not Available The default is 65,536 bytes (64Kb).

The default is 65,536 bytes (64Kb). If the transferMode attribute equals to Buffered, this attribute should be equal to the maxReceivedMessageSize attribute value.

If the transferMode attribute equals to Streamed, this attribute cannot be more than the maxReceivedMessageSize attribute value, and should be at least the size of the headers.

The default is 65,536 bytes (64Kb). If the transferMode attribute equals to Buffered, this attribute should be equal to the maxReceivedMessageSize attribute value.

If the transferMode attribute equals to Streamed, this attribute cannot be more than the maxReceivedMessageSize attribute value, and should be at least the size of the headers.

The default is 524,288 bytes (512 * 1024 = 0x80000 = 512Kb).  Not Available Not Available Not Available Not Available
maxRetryCycles
An integer that indicates the number of retry cycles used by the poison-message detection feature. A message becomes a poison message when it fails all delivery attempts of all cycles.  Not Available Not Available The default is 2. Not Available Not Available Not Available Not Available Not Available Not Available Not Available Not Available
maxReceivedMessageSize A positive integer that defines the maximum message size, in bytes, including headers, for a message that can be received on a channel configured with this binding. The sender receives a SOAP fault if the message is too large for the receiver. The receiver drops the message and creates an entry of the event in the trace log. This bound on message size is intended to limit exposure to Denial of Service (DoS) attacks. The default is 65,536 bytes (64Kb). The default is 65,536 bytes (64Kb). The default is 65,536 bytes (64Kb). The default is 65,536 bytes (64Kb). The default is 65,536 bytes (64Kb). The default is 65,536 bytes (64Kb). The default is 65,536 bytes (64Kb). Increasing this value alone is not sufficient in ASP.NET compatible mode. You should also increase the value of httpRuntime. The default is 65,536 bytes (64Kb). The default is 65,536 bytes (64Kb). The default is 65,536 bytes (64Kb). The default is 65,536 bytes (64Kb).
maxConnections

An integer that specifies the maximum number of outbound and inbound connections the service will create/accept. Incoming and outgoing connections are counted against a separate limit specified by this attribute.

Inbound connections in excess of the limit are queued until a space below the limit becomes available.

Outbound connections in excess of the limit are queued until a space below the limit becomes available.

Not Available Not Available Not Available The default is 10. The default is 10. The default is 10. Not Available Not Available Not Available Not Available Not Available
openTimeout A TimeSpan value that specifies the interval of time provided for an open operation to complete. This value should be greater than or equal to Zero.  The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute).
closeTimeout
A TimeSpan value that specifies the interval of time provided for a close operation to complete. This value should be greater than or equal to Zero. Not Available Not Available The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute).
receiveRetryCount
An integer that specifies the maximum number of times the queue manager should attempt to send a message before transferring it to the retry queue.
Not Available Not Available The default is 5. Not Available Not Available Not Available Not Available Not Available Not Available Not Available Not Available
receiveTimeout A TimeSpan value that specifies the interval of time provided for a receive operation to complete. This value should be greater than or equal to Zero. The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes).
sendTimeout A TimeSpan value that specifies the interval of time provided for a send operation to complete. This value should be greater than or equal to Zero.  The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute). The default is 00:01:00 (1 minute).
timeToLive  A TimeSpan value that specifies how long the messages are valid before they are expired and put into the dead-letter queue.  Not Available Not Available The default is 1.00:00:00.  Not Available Not Available Not Available Not Available Not Available Not Available Not Available Not Available
reliableSession/@inactivityTimeout

A TimeSpan that specifies the maximum duration the channel is going to allow the other communicating party not to send any messages before faulting the channel.

Activity on a channel is defined as receiving an application or infrastructure message. If no activity is detected for the time specified by this attribute, the session is aborted by the infrastructure and the channel faults. The reliable session is aborted.

Not Available Not Available Not Available Not Available The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). Not Available The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes). The default is 00:10:00 (10 minutes).
readerQuotas/@maxDepth A positive integer that specifies the maximum nested node depth per read.  The default is 32.  The default is 32.  The default is 32.  The default is 32.  The default is 32.  The default is 32.  The default is 32.  The default is 32.  The default is 32.  The default is 32.  The default is 32. 
readerQuotas/@maxStringContentLength A positive integer that specifies the maximum characters allowed in XML element content. The default is 8192. The default is 8192. The default is 8192. The default is 8192. The default is 8192. The default is 8192. The default is 8192. The default is 8192. The default is 8192. The default is 8192. The default is 8192.
readerQuotas/@maxArrayLength A positive integer that specifies the maximum allowed array length. 
The default is 16384.
The default is 16384.
The default is 16384.
The default is 16384.
The default is 16384.
The default is 16384.
The default is 16384.
The default is 16384.
The default is 16384.
The default is 16384.
The default is 16384.
readerQuotas/@maxBytesPerRead A positive integer that specifies the maximum allowed bytes returned per read. The default is 4096. The default is 4096. The default is 4096. The default is 4096. The default is 4096. The default is 4096. The default is 4096. The default is 4096. The default is 4096. The default is 4096. The default is 4096.
readerQuotas/@maxNameTableCharCount A positive integer that specifies the maximum characters allowed in a table name. The default is 16384.  The default is 16384.  The default is 16384.  The default is 16384.  The default is 16384.  The default is 16384.  The default is 16384.  The default is 16384.  The default is 16384.  The default is 16384.  The default is 16384. 

Sorry for the big table layout. Many of these parameters are taken "as they are" from the product documentation. The only thing I've done is to write them in a unique and common place, for further reference. The red values seem to be different (using .NET Reflector) from the MSDN documented values. I hope that I didn't make mistakes, otherwise please drop me a comment ... thanks.

One last set of configuration parameters that in general influence the scalability of a WCF solution are the InstanceContextMode and ConcurrencyMode properties of the ServiceBehavior. These parameters are configurable only within the service code, and not in XML configuration, because they relate to the runtime behavior of the service and the service developer should be aware of their values. The InstanceContextMode parameter determines how many instances of the service have to be created by the WCF runtime. The possible values are:

  • PerCall: a new InstanceContext object is created for each call.
  • PerSession: a new InstanceContext object is created for each session. If the channel does not create a session this value behaves as if it were PerCall.This is the default value.
  • Single: only one InstanceContext object is used for all incoming calls and is not recycled subsequent to the calls. If a service object does not exist, one is created.

The ConcurrencyMode property relates to the threading behavior of the service. In fact it defines how the service behaves related to multi-threaded scenarios. Here are the possible values:

  • Single: the service instance is single-threaded and does not accept reentrant calls. If the InstanceContextMode property is Single, and additional messages arrive while the instance services a call, these messages must wait until the service is available or until the messages time out. This is the default value.
  • Reentrant: the service instance is single-threaded and accepts reentrant calls. The reentrant service accepts calls when you call another service; it is therefore your responsibility to leave your object state consistent before callouts and you must confirm that operation-local data is valid after callouts. Note that the service instance is unlocked only by calling another service over a WCF channel. In this case, the called service can reenter the first service via a callback. If the first service is not reentrant, the sequence of calls results in a deadlock.
  • Multiple: the service instance is multi-threaded. No synchronization guarantees are made. Because other threads can change your service object at any time, you must handle synchronization and state consistency at all times.

In the following table you can see the matrix of available configurations and behaviors for InstanceContextMode and ConcurrencyMode:

  ConcurrencyMode.Single ConcurrencyMode.Reentrant ConcurrencyMode.Multiple
InstanceContextMode.Single Only one service instance is created and only one thread per-time accesses that instance. Requests are handled using a FIFO approach. Only one service instance is created and only one thread per-time accesses that instance. Requests are handled using a FIFO approach.

The single thread can leave the service code and come back later (for instance to make a callback or something like that).
Only one service instance is created and multiple threads can access that instance. Service code has to be synchronized in order to be thread safe.
InstanceContextMode.PerCall ConcurrencyMode does not matter because each call is handled by its own instance and thread ConcurrencyMode does not matter because each call is handled by its own instance and thread ConcurrencyMode does not matter because each call is handled by its own instance and thread
InstanceContextMode.PerSession (default) One service instance is created for each active session and only one thread for each session  accesses that per-session instance. Multiple concurrent requests related to the same session, and so to the same service instance, are handled using a FIFO approach. One service instance is created for each active session and only one thread for each session  accesses that per-session instance. Multiple concurrent requests related to the same session, and so to the same service instance, are handled using a FIFO approach.

The single thread can leave the service code and come back later (for instance to make a callback or something like that).
One service instance is created for each active session and multiple threads can access that instance. Service code has to be synchronized in order to be thread safe.

That's all. I hope you will enjoy this reference post.

Handling custom SOAP headers via WCF Behaviors

A few days ago a customer of mine asked me how to define a WCF behavior to add a custom SOAP Header to sent/received messages.

The solution is not so far from what I've shown in the previous "Writing a WCF Message Inspector" post. In fact one way of working is to define a custom message inspector that writes/reads the custom SOAP Header.

So first of all we need a SOAP Header. Here is the code to define a custom header to handle a random key (as a Guid) injected in every request sent from the consumer to the service:

public class CustomHeader : MessageHeader
{
    private String _key;

    public String Key
    {
        get
        {
            return (this._key);
        }
    }

    public CustomHeader(String key)
    {
        this._key = key;
    }

    public override string Name
    {
        get { return (CustomHeaderNames.CustomHeaderName); }
    }

    public override string Namespace
    {
        get { return (CustomHeaderNames.CustomHeaderNamespace); }
    }

    protected override void OnWriteHeaderContents(System.Xml.XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        // Write the content of the header directly using the XmlDictionaryWriter
        writer.WriteElementString(CustomHeaderNames.KeyName, this.Key);
    }

    public static CustomHeader ReadHeader(XmlDictionaryReader reader)
    {
        // Read the header content (key) using the XmlDictionaryReader
        if (reader.ReadToDescendant(CustomHeaderNames.KeyName, CustomHeaderNames.CustomHeaderNamespace))
        {
            String key = reader.ReadElementString();
            return (new CustomHeader(key));
        }
        else
        {
            return null;
        }
    }

}

public static class CustomHeaderNames
{
    public const String CustomHeaderName = "CustomHeader";

    public const String KeyName = "Key";

    public const String CustomHeaderNamespace = "http://schemas.devleap.com/CustomHeader";

}

As you can see it is a type inheriting from MessageHeader class. Notice the OnWriteHeaderContents override, which is invoked by WCF infrastructure to serialize the SOAP Header, and the ReadHeader static method that we will use later.

Such a SOAP Header need to be added by the consumer and read by the service. To do this we need a MessageInspector like the following one:

public class CustomMessageInspector : IDispatchMessageInspector, IClientMessageInspector
{
    #region Message Inspector of the Service

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        // Look for my custom header in the request
        Int32 headerPosition = request.Headers.FindHeader(CustomHeaderNames.CustomHeaderName, CustomHeaderNames.CustomHeaderNamespace);

        // Get an XmlDictionaryReader to read the header content
        XmlDictionaryReader reader =
request.Headers.GetReaderAtHeader(headerPosition);

        // Read it through its static method ReadHeader
        CustomHeader header = CustomHeader.ReadHeader(reader);

        // Add the content of the header to the IncomingMessageProperties dictionary
      
OperationContext.Current.IncomingMessageProperties.Add("key", header.Key);

        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
    }

    #endregion

    #region Message Inspector of the Consumer

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        // Prepare the request message copy to be modified
        MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
        request = buffer.CreateMessage();

        // Simulate to have a random Key generation process
        request.Headers.Add(new CustomHeader(Guid.NewGuid().ToString()));

        return null;
    }

    #endregion
}

As you can see from the code sample above, we use the IClientMessageInspector implementation to handle the addition of the header in the consumer-side code, while we use the IDispatchMessageInspector on the service side, to extract the header. It is interesting the FindHeader method of the MessageHeaders collection, as well as the method GetReaderAtHeader, provided by the same collection of SOAP Headers. The result of this last method is an XmlDictionaryReader that we use to read our custom header content, through the ReadHeader static method we've already introduced.

The service will be able to read the Key provided throught the custom SOAP header simply querying the IncomingMessageProperties dictionary:

OperationContext.Current.IncomingMessageProperties["key"]

Of course this custom MessageInspector needs to be plugged into the WCF pipeline using a custom behavior like the following one:

[AttributeUsage(AttributeTargets.Class)]
public class CustomBehavior : Attribute, IEndpointBehavior
{
    #region IEndpointBehavior Members

    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
    {
        CustomMessageInspector inspector = new CustomMessageInspector();
        clientRuntime.MessageInspectors.Add(inspector);
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    {
        ChannelDispatcher channelDispatcher = endpointDispatcher.ChannelDispatcher;
        if (channelDispatcher != null)
        {
            foreach (EndpointDispatcher ed in channelDispatcher.Endpoints)
            {
                CustomMessageInspector inspector = new CustomMessageInspector();
                ed.DispatchRuntime.MessageInspectors.Add(inspector);
            }
        }
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }

    #endregion
}

We also need an ExtensionElement to configure the behavior:

public class CustomBehaviorExtensionElement : BehaviorExtensionElement
{
    protected override object CreateBehavior()
    {
        return new CustomBehavior();
    }

    public override Type BehaviorType
    {
        get
        {
            return typeof(CustomBehavior);
        }
    }
}

At last we can configure in the .config file of our service and consumer the behavior. Here is the service side configuration:

<system.serviceModel>

  <extensions>
    <behaviorExtensions>
      <add name="customBehavior" type="DevLeap.WCF.Behaviors.Extensions.CustomBehaviorExtensionElement, DevLeap.WCF.Behaviors.Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </behaviorExtensions>
  </extensions>

<services>
  <service name="DevLeap.WCF.Behaviors.Services.ServiceOne" behaviorConfiguration="serviceBehavior">
    <endpoint
     address="net.tcp://localhost:35001/ServiceOne/"
     binding="netTcpBinding"
     contract="DevLeap.WCF.Behaviors.Contracts.IServiceOne"
    behaviorConfiguration="endpointBehavior" />
  </service>
</services>

<behaviors>
  <endpointBehaviors>
    <behavior name="endpointBehavior">
     
<customBehavior />
    </behavior>
  </endpointBehaviors>

  </behaviors>

</system.serviceModel>

And here is the configuration of the consumer-side:

<system.serviceModel>

    <extensions>
      <behaviorExtensions>
        <add name="customBehavior" type="DevLeap.WCF.Behaviors.Extensions.CustomBehaviorExtensionElement, DevLeap.WCF.Behaviors.Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </behaviorExtensions>
    </extensions>

    <client>
            <endpoint name="serviceOneEndpoint"
                address="net.tcp://localhost:35001/ServiceOne/"
                binding="netTcpBinding"
                contract="DevLeap.WCF.Behaviors.Contracts.IServiceOne"
                behaviorConfiguration="serviceOneBehavior" />
        </client>

    <behaviors>
      <endpointBehaviors>
        <behavior name="serviceOneBehavior">
          <customBehavior />
        </behavior>
      </endpointBehaviors>
    </behaviors>

</system.serviceModel>

That's all! Enjoy your custom SOAP header defining infrastructural protocols, but don't forget to check the wide range of WS-* specifications before inventing your own :-) ...

Posted: Feb 25 2008, 11:13 AM by paolopia | with 15 comment(s) |
Filed under: , ,
WCF Security Full Demo

Here you can find the last version of a sample application showing many of the security features and configuration of WCF in the fields of security.
I've just updated it in order to release it during my last WebCast about WCF Security for MSDN Italy.

In this sample you can see (adding/removing endpoints and configuration elements from the config file):

  • Windows Authentication and Windows Authorization via transport level security on basicHttpBinding
  • Windows Authentication and Windows Authorization via message level security on wsHttpBinding
  • UsernamePasswordToken Authentication with ASP.NET Membership and ASP.NET Role Authorization via message level security on wsHttpBinding
  • UsernamePasswordToken Authentication with custom validator via message level security on wsHttpBinding
  • Authorization using a custom Authorization Policy
  • Impersonation using Windows credentials
  • Custom impersonation of custom Principal and Identity
  • Handling of multiple identities (one Primary plus others)
  • A quick and basic sample of interoperability with ASMX consumers using a custom UsernamePasswordToken over SSL, in the respect of WS-Security and WSS UsernameToken Profile 1.0 by Oasis, without using WSE

Enjoy and feel free to give me your feedbacks or further suggestions.

SharePoint custom authentication with Windows CardSpace

Today I and Roberto Brunetti (Italian Blog) worked on one of the speeches we're going to held at the upcoming SharePoint Conference 2007.

The session is about SharePoint Custom Authentication and we really enjoyed playing with a CardSpace based authentication solution. It tooks a little bit more than half a day to make it working, but the result is really brilliant!

We defined a Publishing Portal with anonymous access and Forms Based Authentication, able to map an InfoCard to the FBA idenditity managed by the configured Membership Provider. Many thanks to Dominick Baier for his great CardSpace Control for ASP.NET that we referenced from our code, in order to make simpler the implementation of this solution.

Here are some screenshoots:

 

And a brief video of the result:

Writing a WCF Message Inspector

A WCF MessageInspector is a kind of a "message filter" that we can develop on the service or on the consumer side, in order to intercept and inspect the messages coming in or going out of the service layer infrastructure.

In order to define a Message Inspector on the consumer side we need to implement the IClientMessageInspector interface, while on the service side we need to implement the IDispatchMessageInspector interface. Here are their definitions:

public interface IClientMessageInspector
{
    void AfterReceiveReply(ref Message reply, object correlationState);
    object BeforeSendRequest(ref Message request, IClientChannel channel);
}
public interface IDispatchMessageInspector
{
    object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext);
    void BeforeSendReply(ref Message reply, object correlationState);
}

As you can see both these interfaces define a couple of methods that allow to access the Message (System.ServiceModel.Channels.Message) just before sending it, regardless it is a Request (IClientMessageInspector) or a Response (IDispatchMessageInspector), and just after receiveing it, again regardless its direction.

It's very important to underline that the message provided to this methods is a "by reference" parameter, because this allows our Message Inspector implementations to change the message while it is moving along the service model pipeline. In fact the ref Message parameter can be used to read the SOAP message using one of the methods of the Message type (like ToString(), GetBody<T>(), GetReaderAtBodyContents(), etc.) or can be completely changed using a new Message instance, written through the writing methods of the Message type (WriteBody(...), WriteBodyContents(...), WriteMessage(...), etc.).
One of the most useful methods of the Message type is the CreateBufferedCopy one, which allows to create a MessageBuffer instance that is a buffered copy of the source message useful to XPath navigate its content. The MessageBuffer type allows also to recreate a Message instance from the buffer using the CreateMessage() method.

Here is an example of a service-side Message Inspector used to output to the Console any received and sent message:

public class ConsoleOutputMessageInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
        request = buffer.CreateMessage();
        Console.WriteLine("Received:\n{0}", buffer.CreateMessage().ToString());
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
        reply = buffer.CreateMessage();
        Console.WriteLine("Sending:\n{0}", buffer.CreateMessage().ToString());
    }
}

As you can see I create a copy of the message instance, using the CreateBufferedCopy() method, and the I write it using the ToString() of the Message type.

Another example of Message Inspector could be the following one, used to write to the console every single SOAP Header contained in the message that moves through the message pipeline:

public class ConsoleOutputHeadersMessageInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
        request = buffer.CreateMessage();
        Message originalMessage = buffer.CreateMessage();
        foreach (MessageHeader h in originalMessage.Headers)
        {
            Console.WriteLine("\n{0}\n", h);
        }
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        MessageBuffer buffer = reply.CreateBufferedCopy(0x7fffffff);
        reply = buffer.CreateMessage();
        Message originalMessage = buffer.CreateMessage();
        foreach (MessageHeader h in originalMessage.Headers)
        {
            Console.WriteLine("\n{0}\n", h);
        }
    }
}

Here I walk through each MessageHeader contained within the source Message browsing the Headers collection. One more time I work on a buffered copy of the message.

In order to configure these message inspectors we can use a custom behavior. Behaviros are classes that extend the service model defining custom extensions for: contracts, endpoints, services, operations. In these examples I defined two different kind of behaviors: one endpoint behavior and one servicebehavior.

Let's start from the EndpointBehavior:

public class ConsoleOutputBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        throw new Exception("Behavior not supported on the consumer side!");
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        ConsoleOutputMessageInspector inspector = new ConsoleOutputMessageInspector();
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
}

As you can see I implement the IEndpointBehavior interface, which defines three methods (AddBindingParameter, ApplyClientBehavior, ApplyDispatchBehavior). The one I'm interested on is the ApplyDispatchBehavior that relates to the service-side. This method receives a parameter of type EndpointDispatcher that allows to add custom Message Inspectors instance to the service dispatching environment. Because we're defining an Endpoint Behavior, this behavior affects a single endpoint of a service. To map the behavior to the service endpoint we can use a custom configuration element in the configuration file of the service host. Otherwise we could apply the behavior directly through the ServiceHost instance. In this sample I used a custom configuration element. To do that we need a custom type describing the configuration element. It is a type inherited from BehaviorExtensionElement, like the following one:

public class ConsoleOutputBehaviorExtensionElement : BehaviorExtensionElement
{
    protected override object CreateBehavior()
    {
        return new ConsoleOutputBehavior();
    }

    public override Type BehaviorType
    {
        get
        {
            return typeof(ConsoleOutputBehavior);
        }
    }
}

The implementation of the behavior extension element is really simple, it defines just the CreateBehavior method, used to create an instance of the behavior, and the BehaviorType property, to return the type of the behavior it defines and creates. In reality this class can define also custom properties useful to configure the behavior. In our example we don't do that, but we could add some configuration properties, too.
The previously declared extension element can be used in the .config file of the service host application, like in the following excerpt:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <system.serviceModel>
        <services>
            <service name="DevLeap.WCF.MessageInspectors.Services.OrderService">
                <endpoint
                    behaviorConfiguration="devleapBehavior"
                    address="http://localhost:8000/OrderService"
                    binding="wsHttpBinding" bindingConfiguration="devleapWsHttpBinding"
                    contract="DevLeap.WCF.MessageInspectors.Contracts.IOrderService" />
            </service>   
        </services>

        <extensions>
            <behaviorExtensions>
                <add name="consoleOutputBehavior" type="DevLeap.WCF.MessageInspectors.Extensions.ConsoleOutputBehaviorExtensionElement, DevLeap.WCF.MessageInspectors.Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
            </behaviorExtensions>
        </extensions>

        <behaviors>
            <endpointBehaviors>
                <behavior name="devleapBehavior">
                    <consoleOutputBehavior />
                </behavior>
            </endpointBehaviors>
        </behaviors>

        <bindings>
            <wsHttpBinding>
                <binding name="devleapWsHttpBinding">
                    <security mode="None" />
                </binding>
            </wsHttpBinding>
        </bindings>

    </system.serviceModel>

</configuration>

First of all we define the behaviorExtension element, inside which we define the new extension, through the add element. Keep in mind that we need to declare the fully qualified name of the extension element type inside the type attribute.
Then we declare the new custom behavior within the behaviors section of the configuration file.

While an Endpoint Behavior applies only to a single endpoint, we can also define a custom Service Behavior that applies to every single endpoint of a service. To do that we need to define a class that implements the IServiceBehavior interface. Here is an example:

[AttributeUsage(AttributeTargets.Class)]
public class ConsoleHeaderOutputBehavior : Attribute, IServiceBehavior
{
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++)
        {
            ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher;
            if (channelDispatcher != null)
            {
                foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
                {
                    ConsoleOutputHeadersMessageInspector inspector = new ConsoleOutputHeadersMessageInspector();
                    endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
                }
            }
        }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
}

The IServiceBehavior interface looks like the IEndpointBehavior, except the fact that it provides a different ApplyDispatchBehavior method definition. In fact a Service Behavior should apply its behavior to every single insatnce and endpoint published by the service to which it is applied. In this example I inherited the behavior class from the Attribute base class too, targeting it to class definitions. This way we can apply the behavior directly to the service definition, like shown in the following excerpt:

[ConsoleHeaderOutputBehavior]
public class OrderService : IOrderService
{
    public OrderConfirmation InsertOrder(Order order)
    {
        OrderConfirmation result = new OrderConfirmation();
        result.IdOrder = order.IdOrder;
        result.ShipDateTime = DateTime.Now.AddDays(2);
        return result;
    }
}

So far you have seen how to define custom Message Inspector and how to map it to a single endpoint, using and Endpoint Behavior, or how to map it to an entire service, using a Service Behavior. You have also seen how to declare the behaviors using a custom configuration element or a custom behavior attribute. Hope you enjoyed this article, Here you can find the code sample used and described in this post.

More Posts Next page »