none
Deleted RRS feed

Answers

  • Hello,

    You can use a Data Contract Surrogate to change the type from IList<T> to List<T>. To make your service(s) use the surrogate, you need to apply a service behaviour that does that.

    Here is the code for such behavior. Note that the class implements both IServiceBehavior and IDataContractSurrogate interfaces. You can split the implementation as two classes if you want.

    using System;
    using System.CodeDom;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Configuration;
    using System.ServiceModel.Description;
    using System.Web;
    
    namespace MyServiceBehaviors
    {
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
        public class ChangeCollectionTypeBehaviorAttribute : Attribute, IServiceBehavior, IDataContractSurrogate
        {
            #region IServiceBehavior Members
            public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
            {
                foreach (var endpoint in serviceDescription.Endpoints)
                {
                    foreach (var operation in endpoint.Contract.Operations)
                    {
                        var serializationBehavior = operation.Behaviors.Find<DataContractSerializerOperationBehavior>();
                        if (serializationBehavior == null)
                        {
                            serializationBehavior = new DataContractSerializerOperationBehavior(operation);
                            operation.Behaviors.Add(serializationBehavior);
                        }
                        serializationBehavior.DataContractSurrogate = this;
                    }
                }
            }
    
            public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
            {
                // DO Nothing
            }
    
            public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
            {
                // DO Nothing
            }
            #endregion IServiceBehavior Members
    
    
            public object GetCustomDataToExport(Type clrType, Type dataContractType)
            {
                return null;
            }
    
            public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
            {
                return null;
            }
    
            public Type GetDataContractType(Type type)
            {
                // Changes the IList<T> to List<T>
                if (type.IsGenericType && !type.IsGenericTypeDefinition
                    && type.GetGenericTypeDefinition() == typeof(IList<>))
                {
                    return typeof(List<>).MakeGenericType(type.GetGenericArguments()[0]);
                }
                return type;
            }
    
            public object GetDeserializedObject(object obj, Type targetType)
            {
                return obj;
            }
    
            public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
            {
                
            }
    
            public object GetObjectToSerialize(object obj, Type targetType)
            {
                return obj;
            }
    
            public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
            {
                return null;
            }
    
            public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
            {
                return null;
            }
        }
    }

    To apply this behavior to your service you can this using two ways:

    1- Add the [ChangeCollectionTypeBehaviorAttribute] to the interface or class that defines your service contract.

    2- You can change it via configuration by creating a BehaviorExtensionElement derived class and referencing it in web.config (or app.config)

    The class can be implemented as follows:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.ServiceModel;
    using System.ServiceModel.Configuration;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MyServiceBehaviors
    {
        public class ChangeCollectionTypeBehaviorExtensionElement : BehaviorExtensionElement
        {
            public override Type BehaviorType
            {
                get
                {
                    return typeof(ChangeCollectionTypeBehaviorAttribute);
                }
            }
    
            protected override object CreateBehavior()
            {
                return new ChangeCollectionTypeBehaviorAttribute();
            }
        }
    }
    

    To apply this element to your configuration:

    <configuration>
      <system.serviceModel>
        <extensions>
          <behaviorExtensions>
            <!-- define the change collection type behavior 
              note that the name attribute must match the tag name in the serviceBehaviors
              Also change the Namespace and Assembly name if you put use a different namespace/assembly name for behavior
            -->
            <add name="changeCollectionType" type="MyServiceBehaviors.ChangeCollectionTypeBehaviorExtensionElement, MyServiceBehaviors"/>
          </behaviorExtensions>
        </extensions>
        <behaviors>
          <serviceBehaviors>
            <behavior name="">
              <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
              <serviceDebug includeExceptionDetailInFaults="false" />
              <!-- Apply the change collection type behavior to the behavior configuration -->
              <changeCollectionType />
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
            multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
    </configuration>
    

    I hope this helps :)

    Best regards,

    Sherif


    http://sherifelmetainy.blogspot.com/

    Thursday, February 28, 2013 12:09 PM
  • Hi,

    Sorry, now I understood. You need to use Surrogate.

    http://msdn.microsoft.com/en-us/library/ms751540.aspx

    Here is the sample 

     public class MySurrogate : IDataContractSurrogate
       {
          public Type GetDataContractType(Type type)
          {
             return type;
          }
    
          public object GetDeserializedObject(object obj, Type targetType)
          {
             if(obj is Employee)
             {
                return obj;
             }
             if(obj is Employee[])
             {
                var list = (obj as Employee[]).ToList();
                return list;
             }
    
             return obj;
          }

    Leave the rest of the implementation to default.

    And you need to hook this surrogate to you host.

     foreach (ServiceEndpoint ep in host1.Description.Endpoints)
             {
                foreach (OperationDescription op in ep.Contract.Operations)
                {
                   DataContractSerializerOperationBehavior dataContractBehavior =
                       op.Behaviors.Find<DataContractSerializerOperationBehavior>();
                   if (dataContractBehavior != null)
                   {
                      dataContractBehavior.DataContractSurrogate = new MySurrogate();
                   }
                   else
                   {
                      dataContractBehavior = new DataContractSerializerOperationBehavior(op);
                      dataContractBehavior.DataContractSurrogate = new MySurrogate();
                      op.Behaviors.Add(dataContractBehavior);
                   }
                }
             }

    This way you always convert the Employee[] to List<Employee>. No need to do the change in you Operation Contract implementation.

    /Srinivas.

    • Proposed as answer by SVentrapragada Thursday, February 28, 2013 12:05 PM
    Thursday, February 28, 2013 12:05 PM

All replies

  • Hi, I am confused on your issue, do you mean you can pass an IList interface and return an IList type data, but cannot pass List and returen List type? But you also said it is the workaround mentioned in Point 1 in your post.

    What do you mean with "Is it possible to configure how the server should deserialize the collections from inbound messages "?


    • Edited by MiniPeter Thursday, February 28, 2013 9:31 AM
    Thursday, February 28, 2013 9:24 AM
  • Deleted
    Thursday, February 28, 2013 10:15 AM
  • Hi,

    I just tried a sample, it worked for me without any workaround. I used .net 4.0.

    What is the exact error you are getting? When you put IList<Item>.

    /Srinivas

    Thursday, February 28, 2013 11:06 AM
  • Deleted
    Thursday, February 28, 2013 11:44 AM
  • Hi,

    Sorry, now I understood. You need to use Surrogate.

    http://msdn.microsoft.com/en-us/library/ms751540.aspx

    Here is the sample 

     public class MySurrogate : IDataContractSurrogate
       {
          public Type GetDataContractType(Type type)
          {
             return type;
          }
    
          public object GetDeserializedObject(object obj, Type targetType)
          {
             if(obj is Employee)
             {
                return obj;
             }
             if(obj is Employee[])
             {
                var list = (obj as Employee[]).ToList();
                return list;
             }
    
             return obj;
          }

    Leave the rest of the implementation to default.

    And you need to hook this surrogate to you host.

     foreach (ServiceEndpoint ep in host1.Description.Endpoints)
             {
                foreach (OperationDescription op in ep.Contract.Operations)
                {
                   DataContractSerializerOperationBehavior dataContractBehavior =
                       op.Behaviors.Find<DataContractSerializerOperationBehavior>();
                   if (dataContractBehavior != null)
                   {
                      dataContractBehavior.DataContractSurrogate = new MySurrogate();
                   }
                   else
                   {
                      dataContractBehavior = new DataContractSerializerOperationBehavior(op);
                      dataContractBehavior.DataContractSurrogate = new MySurrogate();
                      op.Behaviors.Add(dataContractBehavior);
                   }
                }
             }

    This way you always convert the Employee[] to List<Employee>. No need to do the change in you Operation Contract implementation.

    /Srinivas.

    • Proposed as answer by SVentrapragada Thursday, February 28, 2013 12:05 PM
    Thursday, February 28, 2013 12:05 PM
  • Hello,

    You can use a Data Contract Surrogate to change the type from IList<T> to List<T>. To make your service(s) use the surrogate, you need to apply a service behaviour that does that.

    Here is the code for such behavior. Note that the class implements both IServiceBehavior and IDataContractSurrogate interfaces. You can split the implementation as two classes if you want.

    using System;
    using System.CodeDom;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Configuration;
    using System.ServiceModel.Description;
    using System.Web;
    
    namespace MyServiceBehaviors
    {
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
        public class ChangeCollectionTypeBehaviorAttribute : Attribute, IServiceBehavior, IDataContractSurrogate
        {
            #region IServiceBehavior Members
            public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
            {
                foreach (var endpoint in serviceDescription.Endpoints)
                {
                    foreach (var operation in endpoint.Contract.Operations)
                    {
                        var serializationBehavior = operation.Behaviors.Find<DataContractSerializerOperationBehavior>();
                        if (serializationBehavior == null)
                        {
                            serializationBehavior = new DataContractSerializerOperationBehavior(operation);
                            operation.Behaviors.Add(serializationBehavior);
                        }
                        serializationBehavior.DataContractSurrogate = this;
                    }
                }
            }
    
            public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
            {
                // DO Nothing
            }
    
            public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
            {
                // DO Nothing
            }
            #endregion IServiceBehavior Members
    
    
            public object GetCustomDataToExport(Type clrType, Type dataContractType)
            {
                return null;
            }
    
            public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
            {
                return null;
            }
    
            public Type GetDataContractType(Type type)
            {
                // Changes the IList<T> to List<T>
                if (type.IsGenericType && !type.IsGenericTypeDefinition
                    && type.GetGenericTypeDefinition() == typeof(IList<>))
                {
                    return typeof(List<>).MakeGenericType(type.GetGenericArguments()[0]);
                }
                return type;
            }
    
            public object GetDeserializedObject(object obj, Type targetType)
            {
                return obj;
            }
    
            public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
            {
                
            }
    
            public object GetObjectToSerialize(object obj, Type targetType)
            {
                return obj;
            }
    
            public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
            {
                return null;
            }
    
            public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
            {
                return null;
            }
        }
    }

    To apply this behavior to your service you can this using two ways:

    1- Add the [ChangeCollectionTypeBehaviorAttribute] to the interface or class that defines your service contract.

    2- You can change it via configuration by creating a BehaviorExtensionElement derived class and referencing it in web.config (or app.config)

    The class can be implemented as follows:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.ServiceModel;
    using System.ServiceModel.Configuration;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MyServiceBehaviors
    {
        public class ChangeCollectionTypeBehaviorExtensionElement : BehaviorExtensionElement
        {
            public override Type BehaviorType
            {
                get
                {
                    return typeof(ChangeCollectionTypeBehaviorAttribute);
                }
            }
    
            protected override object CreateBehavior()
            {
                return new ChangeCollectionTypeBehaviorAttribute();
            }
        }
    }
    

    To apply this element to your configuration:

    <configuration>
      <system.serviceModel>
        <extensions>
          <behaviorExtensions>
            <!-- define the change collection type behavior 
              note that the name attribute must match the tag name in the serviceBehaviors
              Also change the Namespace and Assembly name if you put use a different namespace/assembly name for behavior
            -->
            <add name="changeCollectionType" type="MyServiceBehaviors.ChangeCollectionTypeBehaviorExtensionElement, MyServiceBehaviors"/>
          </behaviorExtensions>
        </extensions>
        <behaviors>
          <serviceBehaviors>
            <behavior name="">
              <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
              <serviceDebug includeExceptionDetailInFaults="false" />
              <!-- Apply the change collection type behavior to the behavior configuration -->
              <changeCollectionType />
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
            multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
    </configuration>
    

    I hope this helps :)

    Best regards,

    Sherif


    http://sherifelmetainy.blogspot.com/

    Thursday, February 28, 2013 12:09 PM
  • Deleted
    Thursday, February 28, 2013 12:18 PM
  • God Job!!!

    Thanks!!


    Oscar Alvarez Guerras - Arquitecto Software en I3B Blog:http://geeks.ms/blogs/oalvarez Por favor marca como respuesta si te ha ayudado esta respuesta

    Tuesday, October 14, 2014 2:19 PM