Friday, January 25, 2013

Configuring a log4net rolling file appender

In log4net, we have the options to configure a rolling file appender either via code or config. We would see both approaches on how we can configure it.

Configuring rolling file appender via config:

1. Create a sample Windows application and add reference of the log4net.dll
2. Now open the app.config and add the below "configSections" element under the "configuration" element.


<configSections>    
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>

3. Now add the below "log4net" element just below the "configSections" element

<log4net>
<root>
<level value="ALL" />
<appender-ref ref="RollingLogFileAppender" />
</root>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="MySampleLogFile.log" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="Amazon" />
<acceptOnMatch value="false" />
</filter>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-5p %d %5rms %-22.22c{1} %-18.18M - %m%n" />
</layout>
</appender>
</log4net>

NOTE: The above configuration does write the log to the file on 2 parameters which are Date and FileSize

4. Now in the startup form add a private property as shown below.


Private Shared ReadOnly log As ILog = LogManager.GetLogger(GetType(FileSplitter).Name)

5. Now in your startup form of your project, add a constructor and in the constructor paste the below code. This piece of code configures log4net.

Public Sub New()
   ' This call is required by the designer.
InitializeComponent()
 
   ' Add any initialization after the InitializeComponent() call.
log4net.Config.XmlConfigurator.Configure()
log.Info("Logging has been configured")
End Sub

Now start your application. Open the folder from where the application is executing and you can see a log file created and when you open the log file it has the message "Logging has been configured"

Configuring rolling file appender via code:

1. Create a sample Windows application and add reference of the log4net.dll
2. Now open the app.config and add the below "configSections" element under the "configuration" element.

<configSections>    
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>

3. Now open the main Form of the project which is shown at startup (In our example it would be Form1.vb) and in the constructor paste the code below after "InitializeComponent()" method.

Dim fileappender = New log4net.Appender.RollingFileAppender()
fileappender.AppendToFile = True
fileappender.Threshold = log4net.Core.Level.Debug
fileappender.File = "MyLogFile_"
fileappender.DatePattern = "yyyyMMddhhmm"
fileappender.StaticLogFileName = False
fileappender.Layout = New log4net.Layout.SimpleLayout()
fileappender.RollingStyle = log4net.Appender.RollingFileAppender.RollingMode.Date
fileappender.ActivateOptions()
DirectCast(log4net.LogManager.GetRepository(), log4net.Repository.Hierarchy.Hierarchy).Root.AddAppender(fileappender)
log4net.Config.BasicConfigurator.Configure(fileappender)
log.InfoFormat("Hurray. I have configured a rolling file appender via code.")


Now when you run your project you can see that there is a MyLogFile_<todays date>.txt file that is created in the output directory and the message "Hurray. I have configured a rolling file appender via code." is written to it.


Thursday, July 5, 2012

How to create a class with properties at run time

We might come across scenarios where we have to add properties to a class very often that might involve changing code. Hence we might consider the option of creating such classes dynamically at runtime and adding properties to it to use them.

The way we would create a class dynamically at run time is using Reflection.Emit namespace in the .NET framework along with ILGenerator.

First we create a class that records on what properties would be needed in a class as shown below:


public class DynamicLibraryProperties
{
       public string PropName { get; set; }
       public Type PropType { get; set; }

       /// The default value for any property is specified as string Ex: For integer is specified as "0", for single specified as "-999"
       public string DefaultValue { get; set; }
}

Now the below method shows on how we can create the class dynamically


public static void GenerateLegacyStructureObject(string libraryName, 
                                       string className, 
                                       List<DynamicLibraryProperties> properties)
{
       ILGenerator ilgen = default(ILGenerator);
       string library = string.Concat(libraryName, ".dll");
       AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
                                       new AssemblyName(libraryName), 
                                       AssemblyBuilderAccess.RunAndSave);
       ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule(libraryName, library);
       TypeBuilder legacyBuilder = modBuilder.DefineType(
                                   string.Concat(libraryName, ".", className), 
                                   TypeAttributes.Class | TypeAttributes.Public);
 
       //Field Builder - Based on number of months add so many fields
       foreach (DynamicLibraryProperties p in properties) 
       {
             FieldBuilder field = legacyBuilder.DefineField(
                                      string.Concat("_", p.PropName), 
                                      p.PropType, 
                                      FieldAttributes.Private);
             PropertyBuilder nameProp = legacyBuilder.DefineProperty(p.PropName, 
                                              PropertyAttributes.HasDefault, 
                                              p.PropType, null);
 
             Type[] types = new Type[] { p.PropType };
             dynamic typeConvertor = TypeDescriptor.GetConverter(p.PropType);
             dynamic defaultValue = typeConvertor.ConvertFromString(p.DefaultValue);
             ConstructorInfo ctor = typeof(DefaultValueAttribute).GetConstructor(types);
             CustomAttributeBuilder customAttrib = new CustomAttributeBuilder(
                                                        ctor, 
                                                        new object[] { defaultValue });
             nameProp.SetCustomAttribute(customAttrib);
 
             MethodAttributes getAttr = MethodAttributes.Public | 
                                          MethodAttributes.HideBySig | 
                                          MethodAttributes.SpecialName;
             MethodBuilder getNameBuilder = legacyBuilder.DefineMethod(
                                             string.Concat("get_", p.PropName),
                                             getAttr,
                                             p.PropType, Type.EmptyTypes);
             ilgen = getNameBuilder.GetILGenerator();
             ilgen.Emit(OpCodes.Ldarg_0);
             ilgen.Emit(OpCodes.Ldfld, field);
             ilgen.Emit(OpCodes.Ret);
 
             MethodBuilder setNameBuilder = legacyBuilder.DefineMethod(
                                               string.Concat("set_", p.PropName),
                                               getAttr, null
                                               new Type[] { p.PropType });
             ilgen = setNameBuilder.GetILGenerator();
             ilgen.Emit(OpCodes.Ldarg_0);
             ilgen.Emit(OpCodes.Ldarg_1);
             ilgen.Emit(OpCodes.Stfld, field);
             ilgen.Emit(OpCodes.Ret);
 
             nameProp.SetGetMethod(getNameBuilder);
             nameProp.SetSetMethod(setNameBuilder);
       }
 
       Type objType = Type.GetType("System.Object");
       ConstructorInfo objCtor = objType.GetConstructor(Type.EmptyTypes);
 
       ilgen.Emit(OpCodes.Ldarg_0);
       ilgen.Emit(OpCodes.Call, objCtor);
 
       ilgen.Emit(OpCodes.Ret);
 
       legacyBuilder.CreateType();
 
       asmBuilder.Save(library);
}

Now we use the above object in a List, Dictionary as shown in the below code:


List<DynamicLibProperties> props = new List<DynamicLibProperties>();
props.Add(new DynamicLibProperties {
                                         PropName = "201203",
                                         PropType = typeof(float)
                                   });
props.Add(new DynamicLibProperties {
                                         PropName = "201204",
                                         PropType = typeof(float)
                                   });
GenerateDynamicClass("DynamicLibrary1", "Legacy", props);
dynamic assemblyFileName = typeof(Form1).Assembly.CodeBase.Replace("file:///", "");
dynamic assemblyInfo = new FileInfo(assemblyFileName);
 
Assembly asm = Assembly.LoadFile(string.Concat(assemblyInfo.Directory,
                                         "\\DynamicLibrary1.dll"));
Type legacyType = asm.GetType("DynamicLibrary1.Legacy");
 
//Creating instance of the dynamically created class 
dynamic legacyObj = Activator.CreateInstance(legacyType);
 
//Creating a dictionary of integer, type of object created dynamically
dynamic genericDictionay = typeof(Dictionary<, >);
List<Type> p = new List<Type>();
p.Add(typeof(int));
p.Add(legacyType);
dynamic specificDic = genericDictionay.MakeGenericType(p.ToArray());
dynamic dicOfLegacyStructure = Activator.CreateInstance(specificDic);
dicOfLegacyStructure.Add(1, legacyObj);
 
//We set the value of a property as shown below
dynamic legObj = dicOfLegacyStructure(1);
if (legObj != null) {
PropertyInfo propInfo = legObj.GetType().GetProperty("201203");
propInfo.SetValue(legObj, 1020, null);
}

Thursday, May 10, 2012

Hosting a secured RESTful WCF Service (https) with and without IIS


Create a new console app project in VS.NET 2010. Now add reference to the dll's

     a.  System.ServiceModel
     b.  System.ServiceModel.Web
     c.  System.Runtime.Serialization

The Program.cs Main method has the following code


public class Program
{
public static void Main(string[] args)
{
Uri baseAddress = new Uri("https://"+Environment.MachineName+":54321/hello");
using (ServiceHost host = new ServiceHost(typeof(HelloWorldService), baseAddress))
{                
WebHttpBinding web = new WebHttpBinding();
web.Security.Mode = WebHttpSecurityMode.Transport;                
host.AddServiceEndpoint(typeof(IHelloWorldService), web, "").Behaviors.Add(new WebHttpBehavior());                                
host.Credentials.ServiceCertificate.Certificate = (X509Certificate2)GetX509Certificate();                               
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();                
host.Close();
}
}

private static X509Certificate GetX509Certificate()
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly);
X509Certificate certificate = null;
X509Certificate2Collection cers = store.Certificates.Find(X509FindType.FindBySubjectName, "localhost", false);            
if (cers.Count > 0)
{
certificate = cers[0];
}
store.Close();
return certificate;
}
}
 
[ServiceContract]
public interface IHelloWorldService
{
[WebGet(UriTemplate="SayHello/{name}")]
string SayHello(string name);
}

public class HelloWorldService : IHelloWorldService
{
public string SayHello(string name)
{
return string.Format("Hello, {0}", name);
}
}


Now we create certificate by creating a batch file of the below commands (taken from MSDN) and executing it from VS.NET command prompt:

echo off
setlocal

call :setscriptvariables %1
IF NOT DEFINED SUPPORTED_MODE call :displayusage
IF DEFINED SUPPORTED_MODE call :cleancerts
IF DEFINED SETUP_SERVICE call :setupservice
IF DEFINED SETUP_CLIENT call :setupclient
GOTO end

:cleancerts
REM cleans up certs from previous runs.  
certmgr.exe -del -r CurrentUser -s My -c -n %CLIENT_NAME%
certmgr.exe -del -r CurrentUser -s TrustedPeople -c -n localhost
certmgr.exe -del -r LocalMachine -s My -c -n localhost
certmgr.exe -del -r LocalMachine -s TrustedPeople -c -n %CLIENT_NAME%
certmgr.exe -put -r LocalMachine -s My -c -n %COMPUTER_NAME% computer.cer
IF %ERRORLEVEL% EQU 0 (
DEL computer.cer      
pause
certmgr.exe -del -r LocalMachine -s My -c -n %COMPUTER_NAME%
)

:cleanupcompleted  

GOTO :EOF

:setupclient
makecert.exe -sr CurrentUser -ss MY -a sha1 -n CN=%CLIENT_NAME% -sky exchange -pe

IF DEFINED EXPORT_CLIENT (      
certmgr.exe -put -r CurrentUser -s My -c -n %CLIENT_NAME% client.cer
) ELSE (      
certmgr.exe -add -r CurrentUser -s My -c -n %CLIENT_NAME% -r LocalMachine -s TrustedPeople
)
GOTO :EOF

:setupservice
makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=%SERVER_NAME% -sky exchange -pe

IF DEFINED EXPORT_SERVICE (      
certmgr.exe -put -r LocalMachine -s My -c -n %SERVER_NAME% service.cer
) ELSE (      
certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
)
GOTO :EOF

:setscriptvariables
REM Parses the input to determine if we are setting this up for a single machine, client, or server
REM sets the appropriate name variables
call :setcomputername
IF [%1]==[] CALL :singlemachine
IF [%1]==[service] CALL :service
IF [%1]==[client] CALL :client

set CLIENT_NAME=client.com

GOTO :EOF

:singlemachine  
SET SUPPORTED_MODE=1
SET SETUP_CLIENT=1
SET SETUP_SERVICE=1
SET SERVER_NAME=localhost
GOTO :EOF

:service  
SET SUPPORTED_MODE=1
SET SETUP_SERVICE=1
SET EXPORT_SERVICE=1
SET SERVER_NAME=%COMPUTER_NAME%
GOTO :EOF

:client  
SET SUPPORTED_MODE=1
SET SETUP_CLIENT=1
SET EXPORT_CLIENT=1
GOTO :EOF

:setcomputername
REM Puts the Fully Qualified Name of the Computer into a variable named COMPUTER_NAME
for /F "delims=" %%i in ('cscript /nologo GetComputerName.vbs') do set COMPUTER_NAME=%%i
GOTO :EOF

:displayusage
ECHO Correct usage:
ECHO     Single Machine - Setup.bat
ECHO     Client Machine - Setup.bat client
ECHO     Service Machine - Setup.bat service
:end


Now open Microsoft Management Console and Select File --> Add/Remove Snap-in to add the Certificates - Current User and Certificate - Local Machine stores

Navigate to Certificate - Local Machine Personal Store to find a server certificate called localhost (self signed) created and installed.

Now Open your IIS and right click on the default website to add HTTPS binding to it with your port number matching that you have defined in your console app (for my its 54321) and select the certificate to be "localhost" ( the certificate that was created in the above steps) and click "OK" and "Close"



Now start your Console app to have your service running and now open fiddler and perform a GET request as shown:

GET https://rajeshwin7:54321/hello/sayhello/rajesh HTTP/1.1
User-Agent: Fiddler
Host: rajeshwin7:54321

Now you get back a response as below:

HTTP/1.1 200 OK
Content-Length: 90
Content-Type: application/xml; charset=utf-8
Server: Microsoft-HTTPAPI/2.0
Date: Fri, 04 May 2012 14:51:25 GMT

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Hello, rajesh</string>

When no IIS is present open up command prompt and enter the following at the command prompt to do a http port mapping for ssl certificate using the netsh tool in winvista and above OS and httpcfg for winxp.

C:\> netsh http add sslcert ipport=0.0.0.0:54321 certhash=6797aea29440de9389bc636e15a35b741d8c22a3 appid={2e80948d-9ae6-42c9-ad33-294929333965}

certhash -- Thumbprint id of the certificate created above. The thumbprint id can be obtained by opening up the Microsoft Management Console - Add/remove snap in for certificate store of computer account on local machine and then navigate to personal store to find the certificate (assuming it has been installed as given above) and then  double click on the certificate and navigate to the details tab to find the thumbprint id as one of the properties(just copy it to use in the above netsh command by removing the spaces)

appid -- is the guid associated with your application that can be found in your assembly.cs file in your project properties folder as shown below:



Now to clean up the certificate create a bathc file with the below commands and execute it using Vs.NET command prompt:

echo off
setlocal
set CLIENT_NAME=client.com
call :setcomputername
call :cleancerts
DEL client.cer > NUL 2>&1
DEL service.cer > NUL 2>&1
GOTO end

:cleancerts
REM cleans up certs from previous runs.
certmgr.exe -del -r CurrentUser -s My -c -n %CLIENT_NAME%
certmgr.exe -del -r CurrentUser -s TrustedPeople -c -n localhost

certmgr.exe -del -r LocalMachine -s My -c -n localhost
certmgr.exe -del -r LocalMachine -s TrustedPeople -c -n %CLIENT_NAME%
certmgr.exe -put -r LocalMachine -s My -c -n %COMPUTER_NAME% computer.cer
IF %ERRORLEVEL% EQU 0 (
DEL computer.cer
pause
certmgr.exe -del -r LocalMachine -s My -c -n %COMPUTER_NAME%
)

:cleanupcompleted
GOTO :EOF

:setcomputername
REM Puts the Fully Qualified Name of the Computer into a variable named COMPUTER_NAME
for /F "delims=" %%i in ('cscript /nologo GetComputerName.vbs') do set COMPUTER_NAME=%%i
GOTO :EOF

:end

You can remove the ssl certificate mapping to a port using netsh command as below:

c:\> netsh http delete sslcert ipport:0.0.0.0:54321

Wednesday, December 21, 2011

When SSL termination occurs at Load balancer we can use ClearBinding for passing UserName or Certificate over Http

We might encounter scenarios where the Load balancer takes care of securing the transport channel and terminates the SSL at the load balancer and decrypts the traffic and redirects to the appropriate server. Now when we want to implement username password authentication on a WCF Service in such scenarios we need to use clear binding. The following article explains on how to create clear binding and provides support for both custom username password and client certificate validation on such scenarios.

First create a class that inherits CustomBinding and let us call it ClearBinding.cs:


using System.ServiceModel;
using System.ServiceModel.Channels;
 
namespace ClearBindingSample
{
    public class ClearBinding : CustomBinding
    {
        private MessageVersion messageVersion = MessageVersion.None;
        private MessageCredentialType clientCredentialType;
 
        public void SetMessageVersion(MessageVersion value)
        {
            messageVersion = value;
        }
 
        public void SetClientCredentialType(MessageCredentialType messageCredentialType)
        {
            clientCredentialType = messageCredentialType;
        }
 
        public override BindingElementCollection CreateBindingElements()
        {
            var res = new BindingElementCollection {new TextMessageEncodingBindingElement() {MessageVersion = messageVersion}};
            
            var securityBindingElement = GetTransport();
            
            if (securityBindingElement != null)
            {
                res.Add(securityBindingElement);
            }
            
            res.Add(new AutoSecuredHttpTransportElement());
            
            return res;
        }
 
        public override string Scheme
        {
            get
            {
                return "http";
            }
        }
 
        private TransportSecurityBindingElement GetTransport()
        {
            if (clientCredentialType == MessageCredentialType.Certificate)
            {
                return SecurityBindingElement.CreateCertificateOverTransportBindingElement();
            }
            if (clientCredentialType == MessageCredentialType.UserName)
            {
                return SecurityBindingElement.CreateUserNameOverTransportBindingElement();
            }
            return null;
        }
    }
}

Now we need to create a class that inherits the StandardBindingElement and let us call it ClearBindingElement.cs:


using System;
using System.Configuration;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
 
namespace ClearBindingSample
{
    internal class ClearBindingElement : StandardBindingElement
    {
        private ConfigurationPropertyCollection properties;
 
        protected override void OnApplyConfiguration(Binding binding)
        {
            var clearBinding = binding as ClearBinding;
            clearBinding.SetMessageVersion(MessageVersion);
            clearBinding.SetClientCredentialType(ClientCredentialType);
        }
 
        protected override Type BindingElementType
        {
            get { return typeof(ClearBinding); }
        }
 
        protected override ConfigurationPropertyCollection Properties
        {
            get
            {
                if (this.properties == null)
                {
                    var properties = base.Properties;
                    properties.Add(new ConfigurationProperty("messageVersion", typeof(MessageVersion), MessageVersion.Soap11, new MessageVersionConverter(), null, ConfigurationPropertyOptions.None));
                    properties.Add(new ConfigurationProperty("clientCredentialType", typeof(MessageCredentialType), MessageCredentialType.None.ToString(), new ClientCredentialTypeConvertor(), null, ConfigurationPropertyOptions.None));
                    this.properties = properties;
                }
                return this.properties;
            }
        }
 
        public MessageVersion MessageVersion
        {
            get
            {
                return (MessageVersion)base["messageVersion"];
            }
            set
            {
                base["messageVersion"] = value;
            }
        }
 
        public MessageCredentialType ClientCredentialType
        {
            get { return (MessageCredentialType)base["clientCredentialType"]; }
            set { base["clientCredentialType"] = value; }
        }
    }
}

Now we inherit the StandardBindingCollectionElement and let us call it ClearCollectionElement.cs


using System.ServiceModel.Configuration;
 
namespace ClearBindingSample
{
    internal class ClearCollectionElement : StandardBindingCollectionElement<ClearBinding, ClearBindingElement>
    {
    }
}

Now we need a type convertor that converts the attribute client credentials to the appropriate type and let us call it ClientCredentialTypeConvertor.cs


using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Globalization;
using System.ServiceModel;
 
namespace ClearBindingSample
{
    internal class ClientCredentialTypeConvertor : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return ((typeof(string) == sourceType) || base.CanConvertFrom(context, sourceType));
        }
 
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return ((typeof(InstanceDescriptor) == destinationType) || base.CanConvertTo(context, destinationType));
        }
 
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (!(value is string))
            {
                return base.ConvertFrom(context, culture, value);
            }
            var str = ((string)value).ToUpper();
            switch (str)
            {
                case "USERNAME":
                    return MessageCredentialType.UserName;
                case "CERTIFICATE":
                    return MessageCredentialType.Certificate;
                case "NONE":
                    return MessageCredentialType.None;
                default:
                    return MessageCredentialType.None;
            }
        }
 
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if ((typeof(string) != destinationType) || !(value is MessageCredentialType))
            {
                return base.ConvertTo(context, culture, value, destinationType);
            }
            var credentialType = (MessageCredentialType)value;
            if (credentialType == MessageCredentialType.UserName)
            {
                return "UserName";
            }
            if (credentialType == MessageCredentialType.Certificate)
            {
                return "Certificate";
            }
            if (credentialType == MessageCredentialType.None)
            {
                return "None";
            }
            return "None";
        }
    }
}

Similarly, we need a convertor that translates message version into an appropriate type understood by the system and let us call it MessageVersionConvertor.cs:


using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Globalization;
using System.ServiceModel.Channels;
 
namespace ClearBindingSample
{
    internal class MessageVersionConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return ((typeof(string) == sourceType) || base.CanConvertFrom(context, sourceType));
        }
 
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return ((typeof(InstanceDescriptor) == destinationType) || base.CanConvertTo(context, destinationType));
        }
 
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (!(value is string))
            {
                return base.ConvertFrom(context, culture, value);
            }
            var str = (string)value;
            switch (str)
            {
                case "Soap11WSAddressing10":
                    return MessageVersion.Soap11WSAddressing10;
 
                case "Soap12WSAddressing10":
                    return MessageVersion.Soap12WSAddressing10;
 
                case "Soap11WSAddressingAugust2004":
                    return MessageVersion.Soap11WSAddressingAugust2004;
 
                case "Soap12WSAddressingAugust2004":
                    return MessageVersion.Soap12WSAddressingAugust2004;
 
                case "Soap11":
                    return MessageVersion.Soap11;
 
                case "Soap12":
                    return MessageVersion.Soap12;
 
                case "None":
                    return MessageVersion.None;
 
                case "Default":
                    return MessageVersion.Default;
            }
            throw new ArgumentOutOfRangeException("messageVersion", str, "The argument must be of type System.ServiceModel.Channels.MessageVersion");
        }
 
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if ((typeof(string) != destinationType) || !(value is MessageVersion))
            {
                return base.ConvertTo(context, culture, value, destinationType);
            }
            var version = (MessageVersion)value;
            if (version == MessageVersion.Default)
            {
                return "Default";
            }
            if (version == MessageVersion.Soap11WSAddressing10)
            {
                return "Soap11WSAddressing10";
            }
            if (version == MessageVersion.Soap12WSAddressing10)
            {
                return "Soap12WSAddressing10";
            }
            if (version == MessageVersion.Soap11WSAddressingAugust2004)
            {
                return "Soap11WSAddressingAugust2004";
            }
            if (version == MessageVersion.Soap12WSAddressingAugust2004)
            {
                return "Soap12WSAddressingAugust2004";
            }
            if (version == MessageVersion.Soap11)
            {
                return "Soap11";
            }
            if (version == MessageVersion.Soap12)
            {
                return "Soap12";
            }
            if (version != MessageVersion.None)
            {
                throw new ArgumentOutOfRangeException("messageVersion", value, "The argument must be of type System.ServiceModel.Channels.MessageVersion");
            }
            return "None";
        }
    }
}

In order for your Username password to be passed over the channel we need to create a class that supports ITransportTokenAssertionProvider and let us call it AutoSecuredHttpSecurityCapabilities.cs:


using System.Net.Security;
using System.ServiceModel.Channels;
 
namespace ClearBindingSample
{
    public class AutoSecuredHttpSecurityCapabilities : ISecurityCapabilities
    {
        public ProtectionLevel SupportedRequestProtectionLevel
        {
            get { return ProtectionLevel.EncryptAndSign; }
        }
 
        public ProtectionLevel SupportedResponseProtectionLevel
        {
            get { return ProtectionLevel.EncryptAndSign; }
        }
 
        public bool SupportsClientAuthentication
        {
            get { return true; }
        }
 
        public bool SupportsClientWindowsIdentity
        {
            get { return false; }
        }
 
        public bool SupportsServerAuthentication
        {
            get { return true; }
        }
    }
}


Now we need a HttpTransportBindingElement that supports the provision of specifying the transport capabilities and let us call it AutoSecuredHttpTransportElement.cs:


using System.ServiceModel.Channels;
 
namespace ClearBindingSample
{
    public class AutoSecuredHttpTransportElement : HttpTransportBindingElement, ITransportTokenAssertionProvider
    {
        public override T GetProperty<T>(BindingContext context)
        {
            if (typeof(T) == typeof(ISecurityCapabilities))
                return (T)(ISecurityCapabilities)new AutoSecuredHttpSecurityCapabilities();
 
            return base.GetProperty<T>(context);
        }
 
        public System.Xml.XmlElement GetTransportTokenAssertion()
        {
            return null;
        }
    }
}


Now we have created a new binding element called ClearBinding. Now open your web.config and add this new extension as shown below:


<system.serviceModel>
    <extensions>
        <bindingExtensions>
            <add name="clearBinding" type="ClearBindingSample.ClearCollectionElement, ClearBindingSample"/>
        </bindingExtensions>
    </extensions>
    <bindings>
        <clearBinding>
            <binding name="clearUsername" messageVersion="Soap11" clientCredentialType="Username"/>
            <binding name="clearCertificate" messageVersion="Soap11" clientCredentialType="Certificate"/>
        </clearBinding>
    </bindings>
</system.serviceModel>

Now you can use this binding in your endpoint and you have achieved passing username password over http without any issues.

How to flatten your Wsdl?

When creating web services that are to be interoperable (easily accessbile across various platforms) the wsdl cannot have any xsd:import elements. In order to achieve this we need to flatten the wsdl. We do this by creating our own ServiceHostFactory.

First we create a class that defines the new ServiceHostFactory called FlatWsdlServiceHostFactory :


using System;
 
namespace FlatWsdl
{
    public sealed class FlatWsdlServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
    {
        public override System.ServiceModel.ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
        {
            return base.CreateServiceHost(constructorString, baseAddresses);
        }
 
        protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            return new FlatWsdlServiceHost(serviceType, baseAddresses);
        }
    }
}

Now we create a Custom ServiceHost that performs the action of flattening the wsdl:


using System;
using System.ServiceModel.Description;
 
namespace FlatWsdl
{
    public class FlatWsdlServiceHost : System.ServiceModel.ServiceHost
    {
        public FlatWsdlServiceHost() { }
 
        public FlatWsdlServiceHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses)
        {
            
        }
 
        public FlatWsdlServiceHost(object singletonInstance, params Uri[] baseAddresses) : base(singletonInstance, baseAddresses)
        {
            
        }
 
        /// <summary>
        /// Just Inject Flat WSDL
        /// </summary>
        protected override void ApplyConfiguration()
        {
            Console.WriteLine("ApplyConfiguration (thread {0})", System.Threading.Thread.CurrentThread.ManagedThreadId);
            base.ApplyConfiguration();
 
            InjectFlatWsdlExtension();
        }
 
        private void InjectFlatWsdlExtension()
        {
            foreach (ServiceEndpoint endpoint in this.Description.Endpoints)
            {
                endpoint.Behaviors.Add(new FlatWsdl());
            }
        }
    }
}

Now we have the actual class that inspects your wsdl and injects the xsd's into the wsdl by replacing the xsd:import elements in your wsdl.


using System.Collections;
using System.Collections.Generic;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Xml.Schema;
using ServiceDescription = System.Web.Services.Description.ServiceDescription;
 
namespace FlatWsdl
{
    public class FlatWsdl: IWsdlExportExtension, IEndpointBehavior
    {
        public void ExportContract(WsdlExporter exporter, WsdlContractConversionContext context) { }
 
        public void ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context)
        {
            XmlSchemaSet schemaSet = exporter.GeneratedXmlSchemas;
            foreach (ServiceDescription wsdl in exporter.GeneratedWsdlDocuments)
            {
                List<XmlSchema> importsList = new List<XmlSchema>();
                foreach (XmlSchema schema in wsdl.Types.Schemas)
                    AddImportedSchemas(schema, schemaSet, importsList);
                
                if (importsList.Count == 0)
                    return;
 
                wsdl.Types.Schemas.Clear();
 
                foreach (XmlSchema schema in importsList)
                {
                    RemoveXsdImports(schema);
                    wsdl.Types.Schemas.Add(schema);
                }
            }
        }
 
        private void AddImportedSchemas(XmlSchema schema, XmlSchemaSet schemaSet, List<XmlSchema> importsList)
        {
            foreach (XmlSchemaImport import in schema.Includes)
            {
                ICollection realSchemas = schemaSet.Schemas(import.Namespace);
                
                foreach (XmlSchema ixsd in realSchemas)
                {
                    if (!importsList.Contains(ixsd))
                    {
                        importsList.Add(ixsd);
                        AddImportedSchemas(ixsd, schemaSet, importsList);
                    }
                }
            }
        }
 
        private void RemoveXsdImports(XmlSchema schema)
        {
            for (int i = 0; i < schema.Includes.Count; i++)
            {
                if (schema.Includes[i] is XmlSchemaImport)
                    schema.Includes.RemoveAt(i--);
            }
        }
 
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            
        }
 
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
 
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
 
        public void Validate(ServiceEndpoint endpoint) { }
    }
}

Now your FlatWsdlServiceHostFactory is ready which would flatten your wsdl. Create a new WCF Service called FaltWsdlService.svc and open the markup of this file which the following code


<%@ ServiceHost Language="C#" Debug="true" Service="Sample.FlatWsdlService" CodeBehind="FlatWsdlService.svc.cs" %>

Replace the above with the code below which states the Service needs to use the FlatWsdlServiceHostFactory instead of the default ServiceHostFactory from WCF.


<%@ ServiceHost Language="C#" Debug="true" Factory="FlatWsdl.FlatWsdlServiceHostFactory" Service="SampleService.FlatWsdlService" CodeBehind="FlatWsdlService.svc.cs" %>

Now when you browse to your wsdl you not find any xsd:import elements rather you would have one single wsdl.

Monday, December 5, 2011

Create custom config section handler

In order to create a custom config section handler we would use the following namespaces:

System.Configuration
System.Collections
System.Collections.Generic
System.Collections.Specialized

Now a sample xml for which we intend to create a config section handler:

<TransformerConfigurationSection MaxSlotsPerMachine="5">
    <!-- Remote Service Machines-->
    <servicemachine id="01">
      <Id value="1" />
      <Name value="server1" />     
    </servicemachine>
    <servicemachine id="02">
      <Id value="2" />
      <Name value="server2" />
    </servicemachine>
</TransformerConfigurationSection>

Now first we create a ConfigurationSection Class that reads this section from a config file. The code looks as follows:


public class TransformerConfigurationSection : ConfigurationSection 
{
    // 1. MaxSlotsPerMachine -- attribute
    // 2. serviceMachine -- Element collection
 
    public const string SectionXPath = "TransformerConfigurationSection";
    public TransformerConfigurationSection()
    {
        Properties.Add(new ConfigurationProperty("MaxSlotsPerMachine", typeof(string), null));
    }
 
    public static TransformerConfigurationSection GetSection()
    {
        return (TransformerConfigurationSection)ConfigurationManager.GetSection(SectionXPath);
    }
 
    [ConfigurationProperty("MaxSlotsPerMachine")]
    public string MaxSlotsPerMachine
    {
        get
        {
            return (string)this["MaxSlotsPerMachine"];
        }
    }
 
    [ConfigurationProperty("", Options = ConfigurationPropertyOptions.IsDefaultCollection)]
    public MachineElementCollection ServiceMachines
    {
        get
        {
            return (MachineElementCollection)this[""];
        }
    }
}

Now we create the class MachineElementCollection that represents a ConfigurationElementCollection.

public class MachineElementCollection : MachineConfigurationElementCollection<ServiceMachineElement>
{
    public MachineElementCollection()
        :base("servicemachine")
    {  
    }
 
    protected override object  GetElementKey(ConfigurationElement element)
    {
        return ((ServiceMachineElement) element).Id;
    }
}
Now create a base class that allows you to reuse the ConfigurationElementCollection to be of any type.

public abstract class MachineConfigurationElementCollection<T> : ConfigurationElementCollection where T: ConfigurationElement, new()
{
    private ConfigurationElementCollectionType collectionType;
    private string elementName;
    public override ConfigurationElementCollectionType  CollectionType
    {
        get 
        { 
                return collectionType;
        }
    }
 
    protected override string  ElementName
    {
        get
        {
            return String.IsNullOrEmpty(elementName) ? base.ElementName : elementName;
        }
    }
 
    public virtual T this[object key]
    {
        get
        {
            var configurationElementType = (T)BaseGet(key);
            return (object)configurationElementType != null ? configurationElementType : null;
        }
        set
        {
            if (IsReadOnly())
                Add(value);
            if (GetElementKey(value).ToString().Equals((string)key, StringComparison.Ordinal))
            {
                if (BaseGet(key) != null)
                    BaseRemove(key);
                Add(value);
            }
        }
    }
 
    public T this[int index]
    {
        get
        {
            return (T)BaseGet(index);
        }
        set
        {
            if (!IsReadOnly() && !ThrowOnDuplicate && BaseGet(index) != null)
                BaseRemoveAt(index);
            BaseAdd(index, value);
        }
    }
 
    internal MachineConfigurationElementCollection()
        : this(ConfigurationElementCollectionType.AddRemoveClearMap, null)
    {
    }
 
    internal MachineConfigurationElementCollection(string elementname)
        : this(ConfigurationElementCollectionType.AddRemoveClearMap, elementname)
    {
    }
 
    internal MachineConfigurationElementCollection(ConfigurationElementCollectionType collectionType, string elementName)
    {
        this.collectionType = collectionType;
        this.elementName = elementName;
        if (string.IsNullOrEmpty(elementName))
            return;
        AddElementName = elementName;
    }
 
    internal MachineConfigurationElementCollection(ConfigurationElementCollectionType collectionType, string elementName, IComparer comparer)
        : base(comparer)
    {
        this.collectionType = collectionType;
        this.elementName = elementName;
    }
 
    protected override void BaseAdd(ConfigurationElement element)
    {
      if (!IsReadOnly() && !ThrowOnDuplicate)
      {
        object elementKey = this.GetElementKey(element);
        if (ContainsKey(elementKey))
          BaseRemove(elementKey);
      }
 
      base.BaseAdd(element);
    }
 
    public void Add(T element)
    { 
      BaseAdd(element);
    }
 
    public void Clear()
    {
      BaseClear();
    }
 
    public virtual bool ContainsKey(object key)
    {
      if (key != null)
        return null != BaseGet(key);
      var list = new List<string>();
 
      foreach (PropertyInformation propertyInformation in (NameObjectCollectionBase) CreateNewElement().ElementInformation.Properties)
      {
        if (propertyInformation.IsKey)
          list.Add(propertyInformation.Name);
      }
        return false;
    }
 
    protected override ConfigurationElement CreateNewElement()
    {
      return Activator.CreateInstance<T>();
    }
}
Now since the ConfigurationElementCollection has been created now we create the actual ConfigurationElement.

public class ServiceMachineElement : ConfigurationElement
{
    public ServiceMachineElement()
    {
        Properties.Add(new ConfigurationProperty(ServiceMachineIdKey, typeof(string), ""));
        Properties.Add(new ConfigurationProperty(IdKey, typeof(IdElement), null));
        Properties.Add(new ConfigurationProperty(NameKey, typeof(NameElement), null));
    }
 
    private const string ServiceMachineIdKey = "id";
    [ConfigurationProperty(ServiceMachineIdKey, IsRequired = false)]
    public string ServiceMachineId
    {
        get { return (String)this[ServiceMachineIdKey]; }
        set { this[ServiceMachineIdKey] = value; }
    }    
 
    private const string IdKey = "Id";
    [ConfigurationProperty(IdKey, IsRequired = true)]
    public IdElement Id
    {
        get { return (IdElement)this[IdKey]; }
        set { this[IdKey] = value; }
    }
 
    private const string NameKey = "Name";
    [ConfigurationProperty(NameKey, IsRequired = true)]
    public NameElement Name
    {
        get { return (NameElement)this[NameKey]; }
        set { this[NameKey] = value; }
    }    
}
Since, we have the servicemachine element with child elements of type NameElement and IdElement we create these classes.

public class IdElement : ConfigurationElement
{
    public IdElement()
    {
        Properties.Add(new ConfigurationProperty(ValueKey, typeof(string), null));
    }
 
    private const string ValueKey = "value";
    [ConfigurationProperty(ValueKey, IsRequired = true)]
    public string Value
    {
        get { return (string)this[ValueKey]; }
        set { this[ValueKey] = value; }
    }
}

public class NameElement : ConfigurationElement
    {
        public NameElement()
        {
            Properties.Add(new ConfigurationProperty(ValueKey, typeof(string), null));
        }
 
        private const string ValueKey = "value";
 
        [ConfigurationProperty(ValueKey, IsRequired = true)]
        public string Value
        {
            get { return (string)this[ValueKey]; }
            set { this[ValueKey] = value; }
        }
    }

That is it. The config section reader is done. Now just add the above xml to your app.config or web.config and define the configsection as shown below:

<configuration>
  <configSections>
    <section name="TransformerConfigurationSection" type="Sample.TransformerConfigurationSection, Sample"/>
  </configSections>
</configuration>