Philippe Truche’s Blog

14 July 2008

Using the Enterprise Library Validation Block with WCF Asynchronous Operations

Filed under: .NET, WCF — Tags: , , , , — Philippe Truche @ 3:58

I was recently surprised to find out first hand that the Enterprise Library Validation Block throws a somewhat obscure exception: ArgumentNullException(“operation.SyncMethod”) when the WCF AsyncPattern is employed.

By debugging into the Enterprise Library source code (EntLib 3.1), I came to understand that the Validation Block was coded to handle synchronous service operations only (using the FaultContract<ValidationFault> attribute on the service operation signature .  In our case, some of our operations also implement the asynchronous pattern and we have Begin and End method pairs for each.

While I was aware that one-way operations do not allow faults to be returned, I was’t sure about asynchronous service operations (implemented as asynchronous on the service side).  I checked the .NET documentation and was able to confirm that FaultContract<T> is supported on service operations implemented asynchronously using the AsyncPattern property.   A difficult to find comment exists in the documentation of the FaultException(TDetail) Generic Class: “The FaultContractAttribute can be used to specify SOAP faults for both two-way service methods and for asynchronous method pairs.”  See http://msdn.microsoft.com/en-us/library/ms576199.aspx.

With this information in hand, I reasonned that I should be able to modify the validation block.  I made the code changes, ran the NUnit tests included in the validation block, and employed the modified assembly on our asynchronous service operations.  I made modifications to ValidationParameterInspector.cs and ValidationBehavior.cs.  The scope of the modifications included the following methods:

  • ValidationParameterInspector.ValidationParameterInspector
  • ValidationParameterInspector.BeforeCall
  • ValidationBehavior.HasValidationAssertions

Note that the modifications apply to both EntLib 3.1 and EntLib 4.0.  The modifications are included below:

Changes to ValidationParameterInspector.cs

/// <summary>

/// Constructor to initialize <see cref=”InputValidators”/>.

/// </summary>

/// <remarks>

/// When the operation is a <see cref=”OperationDescription.BeginMethod”/> because it implements the

/// the <see cref=”OperationContractAttribute.AsyncPattern”/>,

/// it will cause the <see cref=”InputValidators”/> to have empty validators for the

/// <see cref=”AsyncCallback”>callback</see> and <see cref=”Object”>state</see> method

/// arguments.

/// </remarks>

/// <param name=”operation”>The <see cref=”OperationDescription”/> of the operation.</param>

/// <param name=”ruleSet”>A <see cref=”String”/> naming the ruleset to be used.</param>

public ValidationParameterInspector(OperationDescription operation, string ruleSet)

{

// Check the synchronous method of the service first. If the operation implements the

// OperationContractAttribute.AsyncPattern property, set the method to the BeginMethod.

MethodInfo method = operation.SyncMethod;

if (method == null) method = operation.BeginMethod;

 

foreach (ParameterInfo param in method.GetParameters())

{

switch (param.Attributes)

{

case ParameterAttributes.Out:

case ParameterAttributes.Retval:

break;

default:

inputValidators.Add(CreateInputParameterValidator(param, ruleSet));

inputValidatorParameterNames.Add(param.Name);

break;

   }

}

}

 

/// <summary>

/// Performs the validations.

/// </summary>

/// <param name=”operationName”>A <see cref=”String”/> containing the name of the operation.</param>

/// <param name=”inputs”>

/// An <see cref=”object”/> array containing the input parameters of the method and expected as described on

/// the operation, and thus excluding input parameters resulting from the service’s implementation of the

/// <see cref=”OperationContractAttribute.AsyncPattern”/> property.

/// </param>

/// <returns>null</returns>

public object BeforeCall(string operationName, object[] inputs)

{

ValidationFault fault = new ValidationFault();

// Because there could be fewer inputs than inputValidators, iterate

// through the inputs to run the corresponding validations. This accounts

// for asynchronous methods where the callback and state are defined in the

// method but are not exposed outside the boundary of the service.

for (int i = 0; i < inputs.Length; ++i)

{

ValidationResults results = inputValidators[i].Validate(inputs[i]);

AddFaultDetails(fault, inputValidatorParameterNames[i], results);

}

 

if (!fault.IsValid)

{

   throw new FaultException<ValidationFault>(fault);

}

 

return null;

}

 

Changes to ValidationBehavior.cs

/// <summary>

/// Returns a <see cref=”Boolean”/> to indicate whether or not an operation has validation assertions.

/// </summary>

/// <remarks>

/// If the method implements the <see cref=”OperationContractAttribute.AsyncPattern”/> property,

/// the BeginMethod is used instead of the SyncMethod because the SyncMethod is null.

/// </remarks>

/// <param name=”operation”></param>

/// <returns></returns>

private bool HasValidationAssertions(OperationDescription operation)

{

if (operation == null) throw new ArgumentNullException(“operation”);

 

// Check the synchronous method of the service first. If the operation implements the

// OperationContractAttribute.AsyncPattern property, set the method to the BeginMethod.

MethodInfo methodInfo = operation.SyncMethod;

if (methodInfo == null) methodInfo = operation.BeginMethod;

 

return methodInfo.GetCustomAttributes(typeof(ValidatorAttribute), false).Length > 0 ||

   HasParametersWithValidationAssertions(methodInfo.GetParameters());

} 

 

I created a copy of this post on CodePlex.  See http://www.codeplex.com/entlib/Thread/View.aspx?ThreadId=31493.

2 June 2008

WCF, Conditional Compilation, and language differences

Filed under: .NET, WCF, Web Services — Tags: — Philippe Truche @ 11:46

Perhaps you might have defined WCF message contracts lately, and you want to be explicit about the encryption level you require for your message parts.  I am taking the example of a message contract, but really my point is applicable to any element to which you wish to apply the EncryptionLevel enumeration.

In C#:

[MessageHeader(Name = "Environment", MustUnderstand = true
#if !DEBUG
                     , ProtectionLevel = ProtectionLevel.Sign
#endif
)] 
    public String Environment
    {
        get { return _environment; }
        set { _environment = value; }
    }

In VB.NET:

#If CONFIG = “Debug”
    <MessageHeader(Name:=”Environment”, MustUnderstand:=True)> _
    Public Property Environment() As String
#Else
    <MessageHeader(Name:=”Environment”, MustUnderstand:=True, ProtectionLevel:=ProtectionLevel.Sign)> _
    Public Property Environment() As String
#End If
         Get
             Return _environment
         End Get
         Set(ByVal Value As String)
            _environment = Value
         End Set
    End Property 

As you can see, there are some key differences in how attributes are specified using conditional compilation:

  • In C#, I can toggle parts of the same attribute on and off.  In VB.NET, I must repeat the entire attibute (no parts allowed by the compiler).
  • Because of line continuation constraints in VB.NET, the line following the line being continued must be included inside the conditional compilation block.
  • Using VB.NET in Visual Studio (2005 or 2008), it is not clear which part of the conditional compilation is “active” based on the selected configuration.  In C#, the inactive block is grayed out.

Keep in mind these “consequences” as you proceed with WCF attributes in the language you write with.

Blog at WordPress.com.