WCF Known Type from System.Object in Config

前端 未结 2 1049
走了就别回头了
走了就别回头了 2020-12-16 21:49

I\'m trying to specify a known type in my config, but I\'m having problems with the fact that it derives from Object. I can make it work specifying the known type via attrib

相关标签:
2条回答
  • 2020-12-16 22:14

    I'm not sure if it is by design, but the KnownTypeHelper below won't throw an error if you've not declared a service contract with known types. (i.e. its optional to add known types to service contracts).

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Reflection;
    
    /// <summary>
    /// Helper for finding the known types for Wcf Services from a configuration file.
    /// </summary>
    public static class KnownTypeHelper
    {
        /// <summary>
        /// Gets the known types for the service from a configuration file.
        /// </summary>
        /// <param name="provider">
        /// The provider.
        /// </param>
        /// <returns>
        /// The known types for the service from a configuration file.
        /// </returns>
        public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider)
        {
            var result = new List<Type>();
    
            var serviceKnownTypes = (ServiceKnownTypesSection)ConfigurationManager.GetSection("serviceKnownTypes");
            if (serviceKnownTypes != null)
            {
                var service = serviceKnownTypes.Services[((Type)provider).AssemblyQualifiedName];
    
                if (service != null)
                {
                    foreach (ServiceKnownTypeElement knownType in service.KnownTypes)
                    {
                        result.Add(knownType.Type);
                    }
                }
            }
    
            return result;
        }
    }
    

    To save someone else the trouble of creating the configuration classes,

    Note: There is no validation of the assembly qualified type names. If someone wants to add the appropiate attributes to do this, please do.

    using System.Configuration;
    
    /// <summary>
    /// Section for configuration known types for services.
    /// </summary>
    public class ServiceKnownTypesSection : ConfigurationSection
    {
        /// <summary>
        /// Gets services.
        /// </summary>
        [ConfigurationProperty("declaredServices", IsDefaultCollection = false)]
        [ConfigurationCollection(typeof(DeclaredServiceElement), AddItemName = "serviceContract", CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap)]
        public DeclaredServiceElementCollection Services
        {
            get
            {
                return (DeclaredServiceElementCollection)base["declaredServices"];
            }
        }
    }
    
    /// <summary>
    /// Collection of declared service elements.
    /// </summary>
    public class DeclaredServiceElementCollection : ConfigurationElementCollection
    {
        /// <summary>
        /// Gets the service for which known types have been declared for.
        /// </summary>
        /// <param name="key">
        /// The key of the service.
        /// </param>
        public new DeclaredServiceElement this[string key]
        {
            get
            {
                return (DeclaredServiceElement)BaseGet(key);
            }
    
            set
            {
                var element = BaseGet(key);
                var index = this.BaseIndexOf(element);
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
    
                BaseAdd(index, value);
            }
        }
    
        /// <summary>
        /// When overridden in a derived class, creates a new <see cref="T:System.Configuration.ConfigurationElement"/>.
        /// </summary>
        /// <returns>
        /// A new <see cref="T:System.Configuration.ConfigurationElement"/>.
        /// </returns>
        protected override ConfigurationElement CreateNewElement()
        {
            return new DeclaredServiceElement();
        }
    
        /// <summary>
        /// Gets the element key for a specified configuration element when overridden in a derived class.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>.
        /// </returns>
        /// <param name="element">
        /// The <see cref="T:System.Configuration.ConfigurationElement"/> to return the key for. 
        /// </param>
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((DeclaredServiceElement)element).Type;
        }
    }
    
    /// <summary>
    /// The service for which known types are being declared for.
    /// </summary>
    public class DeclaredServiceElement : ConfigurationElement
    {
        /// <summary>
        /// Gets or sets Type.
        /// </summary>
        [ConfigurationProperty("type", IsRequired = true, IsKey = true)]
        public string Type
        {
            get
            {
                return (string) this["type"];
            }
    
            set
            {
                this["type"] = value;
            }
        }
    
        /// <summary>
        /// Gets KnownTypes.
        /// </summary>
        [ConfigurationProperty("knownTypes", IsDefaultCollection = false)]
        [ConfigurationCollection(typeof(DeclaredServiceElement), AddItemName = "knownType", CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap)]
        public ServiceKnownTypeElementCollection KnownTypes
        {
            get
            {
                return (ServiceKnownTypeElementCollection)base["knownTypes"];
            }
        }
    }
    
    /// <summary>
    /// A collection of known type elements.
    /// </summary>
    public class ServiceKnownTypeElementCollection : ConfigurationElementCollection
    {
        /// <summary>
        /// Gets an known type with the specified key.
        /// </summary>
        /// <param name="key">
        /// The key of the known type.
        /// </param>
        public new ServiceKnownTypeElement this[string key]
        {
            get
            {
                return (ServiceKnownTypeElement)BaseGet(key);
            }
    
            set
            {
                var element = BaseGet(key);
                var index = this.BaseIndexOf(element);
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
    
                BaseAdd(index, value);
            }
        }
    
        /// <summary>
        /// When overridden in a derived class, creates a new <see cref="T:System.Configuration.ConfigurationElement"/>.
        /// </summary>
        /// <returns>
        /// A new <see cref="T:System.Configuration.ConfigurationElement"/>.
        /// </returns>
        protected override ConfigurationElement CreateNewElement()
        {
            return new ServiceKnownTypeElement();
        }
    
        /// <summary>
        /// Gets the element key for a specified configuration element when overridden in a derived class.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>.
        /// </returns>
        /// <param name="element">
        /// The <see cref="T:System.Configuration.ConfigurationElement"/> to return the key for. 
        /// </param>
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((ServiceKnownTypeElement)element).Type;
        }
    }
    
    /// <summary>
    /// Configuration element for a known type to associate with a service.
    /// </summary>
    public class ServiceKnownTypeElement : ConfigurationElement
    {
        /// <summary>
        /// Gets or sets TypeString.
        /// </summary>
        [ConfigurationProperty("type", IsRequired = true, IsKey = true)]
        public string TypeString
        {
            get
            {
                return (string)this["type"];
            }
    
            set
            {
                this["type"] = value;
            }
        }
    
        /// <summary>
        /// Gets or sets Type.
        /// </summary>
        public Type Type
        {
            get
            {
                return Type.GetType(this.TypeString);
            }
    
            set
            {
                this["type"] = value.AssemblyQualifiedName;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-16 22:34

    The answer turns out to be it's not possible to do what I want to do in the config file alone. The config above corresponds to the [KnownType] attribute used on DataContracts. There appears to be no way to implement [ServiceKnownType] in the config.

    An alternate approach is to use [ServiceKnownType(methodName, type)] attribute with a custom configuration section. The new config looks like this:

    <configuration>
      <configSections>
        <section
          name="serviceKnownTypes"
          type="WpfApplication1.ServiceKnownTypesSection, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </configSections>
      <serviceKnownTypes>
        <declaredServices>
          <serviceContract type="WpfApplication1.IContract, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
            <knownTypes>
              <knownType type="WpfApplication1.MyData, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
            </knownTypes>
          </serviceContract>
        </declaredServices>
      </serviceKnownTypes>
    </configuration>
    

    The contracts:

    [ServiceContract]
    [ServiceKnownType("GetServiceKnownTypes", typeof(KnownTypeHelper))]
    public interface IContract
    {
        [OperationContract]
        void Send(object data);
    }
    
    [DataContract]
    public class MyData
    {
        [DataMember]
        public string Message { get; set; }
    }
    

    The helper class that contains the callback that returns the list of known types

    public static class KnownTypeHelper
    {
        public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider)
        {
            List<Type> result = new List<Type>();
    
            ServiceKnownTypesSection serviceKnownTypes = (ServiceKnownTypesSection)ConfigurationManager.GetSection("serviceKnownTypes");
            DeclaredServiceElement service = serviceKnownTypes.Services[((Type)(provider)).AssemblyQualifiedName];
    
            foreach (ServiceKnownTypeElement knownType in service.KnownTypes)
            {
                result.Add(knownType.Type);
            }
    
            return result;
        }
    }
    

    Information on creating custom config sections can be found here:

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

    http://msdn.microsoft.com/en-us/library/system.configuration.configurationcollectionattribute.aspx

    0 讨论(0)
提交回复
热议问题