Question

Comment puis-je dire au service WCF ce KnownTypes à utiliser lorsque les données passant au client?

Je sais que je peux utiliser l'attribut [ServiceKnownType], ce qui rend cependant l'appel de service à partir d'un beau parcours de test WCF Server, il échoue encore du client. Suis-je manque quelque chose ici?

[OperationContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
BaseClassZ GetObject();

Message d'erreur du client est:

  

{ "élément   'Http://schemas.datacontract.org/2004/07/BaseClassZ'   contient des données d'un type qui correspond à   le nom   'Http://schemas.datacontract.org/2004/07/SubClassA'.   Le désérialiseur n'a pas connaissance de   tout type qui correspond à ce nom.   Pensez à utiliser un DataContractResolver   ou ajouter le type correspondant à   « SubClassA » à la liste des types connus   - par exemple, en utilisant l'attribut KnownTypeAttribute ou par   ajouter à la liste des types connus   passé à DataContractSerializer. "}

Sérialisation / désérialisation l'objet sur le serveur WCF en utilisant un DataContractSerializer et une liste de KnownTypes fonctionne très bien.

Mise à jour: Il semble que je peux obtenir le client de lire l'objet correctement si j'ajoute KnownType attribue à la classe de base, mais je cherche toujours un moyen de contourner cela si possible depuis la classe de base est utilisé pour un grand nombre d'articles et je ne veux pas avoir à modifier les KnownType attributs de la classe de base à tout moment ajouter un nouvel élément.

[DataContract]
[KnownType(typeof(SubClassA))]
[KnownType(typeof(SubClassB))]
public class BaseClassZ 
{
    ...
}
Était-ce utile?

La solution

Pour éviter dissuadant votre code de service mis les types connus dans web.config du service:

<system.runtime.serialization>
    <dataContractSerializer>
        <declaredTypes>
            <add type="SomeNs.BaseClassZ, SomeAssembly">
                <knownType type="SomeNs.SubClassA, SomeAssembly" />
                <knownType type="SomeNs.SubClassB, SomeAssembly" />
            </add>
        </declaredTypes>
    </dataContractSerializer>
</system.runtime.serialization>

Si vous voulez le faire par le code que vous devez utiliser cet attribut sur l'interface de service et non pas sur la méthode de fonctionnement, mais je préférerais l'approche déclarative:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IFoo
{
    [OperationContract]
    BaseClassZ GetObject();
}

Mise à jour:

Je l'ai mis en place un exemple de projet illustrant l'utilisation de web.config pour configurer les types connus qui est mon approche privilégiée. Et un autre exemple de projet démontrant la deuxième approche.


Mise à jour 2:

Après avoir regardé votre code mis à jour avec le client application Silverlight on remarque la définition suivante:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
public interface IService1 {

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
    System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);

    System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
    System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);

    MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}

Remarquez comment la méthode BeginGetSingle contient le type connu attributs tandis que la méthode BeginGetMany ne fonctionne pas. En fait, ces attributs doivent être placés sur la définition du service afin que les regards de classe comme celui-ci.

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
public interface IService1
{
    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
    System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);

    System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
    System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);

    MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}

Comme il est une classe autogenerated il pourrait y avoir un bug dans le SLsvcUtil.exe et svcutil.exe car il présente le même comportement. Mettre les attributs de type connu sur leur place permet de résoudre corriger le problème. Le problème est que cette classe est générée automatiquement par un outil et si vous essayez de le régénérer à partir du WSDL il gâchera à nouveau.

Il semble donc que si vous avez la définition de service suivant:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IService1
{
    [OperationContract]
    BaseClassZ[] GetMany();

    [OperationContract]
    BaseClassZ GetSingle();
}

Et les 3 contrats de données utilisés ici sont partagées entre le client et le serveur lors de l'importation de la définition du service, la méthode qui retourne une collection ne soit pas le type connu correcte des attributs dans le proxy client généré. Peut-être cela est normal.

Autres conseils

Je passais des heures aujourd'hui sur ce, mieux que je peux dire, est exactement le même problème. La solution pour moi était d'utiliser la méthode de AddGenericResolver de la bibliothèque ServiceModelEx de IDesign.

NOTE: .NET 4.0 nécessaire car il utilise DataContractResolver

Vous pouvez trouver sur IDesign Téléchargements .

Tout ce que je devais faire dans mon cas était d'ajouter la ligne de code suivante:

Client.AddGenericResolver( typeof ( K2Source ) );

J'espère que cela aide quelqu'un d'autre là-bas, sauf quelques heures!

Vous pouvez trouver plus d'informations dans le livre "Programmation WCF Services: Mastering WCF et le service Azure AppFabric Bus" par Juval Lowy

Il y a une autre façon de le faire. Plutôt que d'utiliser vous le code « référence de service, ajouter » les classes proxy. Il est un peu plus de codage au départ, mais vous donne une beaucoup plus stable et d'une solution robuste. Nous avons trouvé que cela nous fait gagner du temps à long terme.

Voir: http://www.dnrtv.com/default.aspx?showNum= 122

Note:. Cela ne fonctionne que si vous avez le contrôle à la fois le serveur et le client

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top