2010-06-30 52 views
8

J'essaie de spécifier un type connu dans ma config, mais j'ai des problèmes avec le fait qu'il dérive d'Object. Je peux le faire fonctionner en spécifiant le type connu via l'attribut. Mais dans ce cas, je dois le faire fonctionner à partir de la config.Type WCF connu de System.Object dans Config

Voici un exemple. Les travaux suivants fins:

[ServiceContract] 
[ServiceKnownType(typeof(MyData))] 
public interface IContract 
{ 
    [OperationContract] 
    void Send(object data); 
} 

[DataContract] 
public class MyData 
{ 
    [DataMember] 
    public string Message { get; set; } 
} 

Mais si je supprime l'attribut ServiceKnownType et mettre ce qui suit dans la config:

<system.runtime.serialization> 
    <dataContractSerializer> 
    <declaredTypes> 
     <add type="System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <knownType type="WpfApplication1.MyData, WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> 
     </add> 
    </declaredTypes> 
    </dataContractSerializer> 
</system.runtime.serialization> 

je reçois un ConfigurationErrorsException avec le message « La valeur de la propriété « type » n'est pas valide L'erreur est: Le type System.Object ne peut pas être utilisé comme un type déclaré dans config. "

Est-il possible de faire ce travail via la configuration?

Répondre

9

La réponse est qu'il n'est pas possible de faire ce que je veux faire dans le fichier de configuration seul. La config ci-dessus correspond à l'attribut [KnownType] utilisé sur DataContracts. Il semble qu'il n'y ait aucun moyen d'implémenter [ServiceKnownType] dans la configuration.

Une autre approche consiste à utiliser l'attribut [ServiceKnownType (nomMéthode, type)] avec une section de configuration personnalisée. La nouvelle configuration ressemble à ceci:

<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> 

Les contrats:

[ServiceContract] 
[ServiceKnownType("GetServiceKnownTypes", typeof(KnownTypeHelper))] 
public interface IContract 
{ 
    [OperationContract] 
    void Send(object data); 
} 

[DataContract] 
public class MyData 
{ 
    [DataMember] 
    public string Message { get; set; } 
} 

La classe d'aide qui contient le rappel qui renvoie la liste des types connus

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; 
    } 
} 

Informations sur la création de configuration personnalisée sections peuvent être trouvés ici:

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

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

2

Je ne suis pas sûr que ce soit par la conception, mais le KnownTypeHelper ci-dessous ne lancera pas d'erreur si vous ne l'avez pas déclaré un contrat de service avec des types connus. (c'est-à-dire qu'il est facultatif d'ajouter des types connus aux contrats de service).

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; 
    } 
} 

Pour sauver quelqu'un d'autre la peine de créer des classes de configuration,

Note: Il n'y a pas de validation des noms d'assemblage de type qualifié. Si quelqu'un veut ajouter les attributs appropriés pour le faire, s'il vous plaît faire.

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; 
     } 
    } 
}