6 votes

Service WCF retournant un tableau de dictionnaire<chaîne, objet>.

J'ai essayé d'utiliser un client SilverLight pour appeler un service ASP.Net WCF qui renverrait un message de type Dictionary<string, object> . Cela fonctionnait bien lorsque les valeurs du dictionnaire étaient des types simples comme int , string o Guid .

Cependant, j'ai maintenant un scénario où j'ai besoin que l'une des valeurs soit un tableau de Dictionary<string, object> ! Tout se compile bien et la signature du service n'a pas changé mais l'appel de service échoue maintenant.

Une idée pour réparer ça ? J'ai essayé d'annoter la classe et les méthodes de mon service avec la balise KnownType y ServiceKnownType mais cela n'a pas fonctionné.

Voici un morceau de code :

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1
{
    [OperationContract]
    [ServiceKnownType(typeof(Dictionary<string, object>))]
    public Dictionary<string, object> GetObject()
    {
        return new Dictionary<string, object>()
            {
                { "pty1", 1 },
                { "pty2", Guid.NewGuid() },
                { "pty3", "blah" },
                { "pty4", new Dictionary<string, object>[]
                              {
                                  new Dictionary<string, object>()
                                      {
                                          { "pty1", 4 },
                                          { "pty2", Guid.NewGuid() },
                                          { "pty3", "blah" },
                                      }
                                   ,
                                   new Dictionary<string, object>()
                                      {
                                          { "pty1", 4 },
                                          { "pty2", Guid.NewGuid() },
                                          { "pty3", "blahblah" },
                                      }
                              }
            }
        };
    }
}

Merci pour vos réponses. J'ai activé le traçage WCF et, comme prévu, il y a un problème pendant la sérialisation. Le problème n'est pas la sérialisation de Dictionary<string, object> mais celle de Array de Dictionary<string, object> .

Ici l'exception enregistrée par le service de WCF.

Une erreur s'est produite lors de la tentative de sérialisation du paramètre :GetObjectResult. Le message InnerException était 'Type 'System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System. Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]][]' avec le nom de contrat de données 'ArrayOfArrayOfKeyValueOfstringanyType : http://schemas.microsoft.com/2003/10/Serialization/Arrays n'est pas attendu. Ajoutez tous les types non connus statiquement à la liste des types connus - par exemple, en utilisant l'attribut KnownTypeAttribute ou en les ajoutant à la liste des types connus transmis à DataContractSerializer.'. Veuillez consulter InnerException pour plus de détails.

J'ai également essayé de définir une nouvelle DataContract avec un seul membre de données mais cela a conduit à la même erreur.

Voici le code pour cela, suivi de l'exception enregistrée par le logging WCF.

[DataContract]
[KnownType(typeof(ObjectHolder))]
public class ObjectHolder
{
    [DataMember]
    public object Object { get; private set; }

    public ObjectHolder(object obj)
    {
        this.Object = obj;
    }
}

Une erreur s'est produite lors de la tentative de sérialisation du paramètre :GetObjectResult. Le message InnerException était 'Type 'System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[SilverlightApplication7. Web.ObjectHolder, SilverlightApplication7.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]][]' avec le nom de contrat de données 'ArrayOfArrayOfKeyValueOfstringObjectHolderWAwxSTlb : http://schemas.microsoft.com/2003/10/Serialization/Arrays n'est pas attendu. Ajoutez tous les types non connus statiquement à la liste des types connus - par exemple, en utilisant l'attribut KnownTypeAttribute ou en les ajoutant à la liste des types connus passés à DataContractSerializer.'. Veuillez consulter InnerException pour plus de détails.

Encore une fois, j'ai joué avec ServiceKnownType para ObjectHolder , ObjectHolder[] et même ObjectHolder[][] puisque l'exception mentionne un " ArrayOfArrayOfKeyValueOfstringObjectHolder ".

Toujours pas de solution.

8voto

Sergey Teplyakov Points 6556

Tout d'abord, vous devez configurer le traçage WCF dans le fichier app cofig qui peut vous aider à comprendre ce qui se passe sous le capot des communications de service. Dans ce cas, vous pouvez facilement obtenir toutes les erreurs qui se produisent pendant le processus de communication.

Maintenant, essayons de résoudre votre problème. Je suis presque sûr que ce problème est de type connu. Dans un monde orienté service, vous devez définir manuellement tous les types concrets qui peuvent participer au contrat de service, car DataContractSerializer ne fournit pas cette information dans les objets sérialisés.

Dans ce cas, cela signifie que vous devez faire ce qui suit :

[ServiceKnownType(typeof(string))] 
[ServiceKnownType(typeof(Guid))]
[ServiceKnownType(typeof(int))] // but I think DataContractSerializer can deal himself with primitives
[ServiceKnownType(typeof(YourClass))] //UserDefined types you should add manually
public Dictionary<string, object> GetObject()

Je vous recommande également de ne pas utiliser d'objet dans le contrat de service, car il est TRÈS sujet aux erreurs. Considérez que plus tard vous ou l'un de vos collègues modifie une ligne de code :

new Dictionary<string, object>()
{
 { "pty1", 4 },
 { "pty2", Guid.NewGuid() },
 { "pty3", new SomeClass() }, //OOPS!!!
}

Dans ce cas, lorsque votre service tente de renvoyer ce dictionnaire, vous rencontrez un échec d'exécution car DataContractSerializer n'attend pas SomeClass dans ce contrat.

Une façon de résoudre ce problème est de créer des types distincts :

[DataContract]
[KnownType(typeof(Guid))]
[KnownType(typeof(SomeClass1))]
[KnownType(typeof(SomeClass2))]
public class MyType
{
  private MyType(object obj) {
     Object = obj;
  }

  public static MyType FromSomeClass1(SomeClass1 c1) {
    return new MyType(c1);
  }

  public static MyType FromSomeClass2(SomeClass2 c2) {
    return new MyType(c2);
  }

  public static MyType FromGuid(Guid guid) {
    return new MyType(guid);
  }

  [DataMember]
  public object Object { get; private set; }
}

Dans ce cas, si vous voulez ajouter un nouveau type, vous devez ajouter la méthode factory et KnownTypeAttribute (cette approche est plus verbeuse, mais moins sujette aux erreurs).

Cependant, si votre client et service écrit sur WCF, vous pouvez sacrifier les principes principaux orientés vers le service et utiliser NetDataContractSerializer au lieu de DataContractSerializer . NetDataContractSerializer est conçu pour compléter DataContractSerializer. Vous pouvez sérialiser un type en utilisant NetDataContractSerializer et le désérialiser avec DataContractSerializer. Mais NetDataContractSerializer inclut les informations de type CLR dans le XML sérialisé, alors que DataContractSerializer ne le fait pas. Par conséquent, NetDataContractSerializer peut être utilisé pour la sérialisation et la désérialisation avec n'importe quel type CLR sans avoir à utiliser KnownTypes.

0voto

Shiraz Bhaiji Points 34901

Essayez de définir une classe qui n'a qu'une seule propriété. Cette propriété est un Dictionnaire de chaîne de caractères, objet.

Marquez la classe avec DataContract / DataMember. Définissez ensuite votre interface en utilisant cette classe.

0voto

Lightman Points 489

Cependant, j'ai maintenant un scénario dans lequel j'ai besoin que l'une des valeurs soit un tableau de dictionnaire !

Le problème est que WCF ne sait pas comment sérialiser / désérialiser un tableau d'objets qui n'est pas marqué avec DataMember et / ou n'est pas ajouté à l'attribut ServiceKnownType s.

Par exemple, s'il existe une méthode de service, qui prend un objet arbitraire comme paramètre

public interface IService
    function DoSomething(Parameter as Object) as integer
end interface

et vous voulez passer, disons un tableau d'entiers Integer() vous devez explicitement ajouter ce type de tableau d'entiers aux types de services connus.

<ServiceKnownType(GetType(Integer()))>
public interface IService
    function DoSomething(Parameter as Object) as integer
end interface

La méthode de service peut alors être appelée

dim Service as IService
dim Argument as Integer()
Service.DoSomething(Argument)

Comment le réparer ?

Essayez d'ajouter <ServiceKnownType(GetType(Dictionary(Of String, Object)()))> attribut.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X