2009-11-20 5 views
2

J'ai un service WCF qui expose une méthode qui renvoie un tableau d'objets qui contiennent une propriété Image (voir le code ci-dessous). Dans la même solution, j'ai un projet de bibliothèque de classes qui a une référence de service à mon projet WCF. Dans la bibliothèque de classes, lorsque je tente de mettre à jour la référence du service, ma classe proxy devient indisponible. Lorsque je supprime la propriété "Graphic" de ma classe, je n'ai aucune difficulté à mettre à jour la référence de service dans la bibliothèque de classes et mon code se compile et s'exécute correctement. Je replace la propriété "Graphic" et la classe proxy redevient indisponible. Et ce qui est encore plus étrange, c'est que la seule classe exposée par la référence de service est "Image".Dans WCF, comment puis-je renvoyer une classe qui contient une propriété System.Drawing.Image?

Qu'est-ce que je néglige ici?

[Serializable] 
public class PhotoDTO 
{ 
    public Guid Id { get; set; } 
    public Image Graphic { get; set; } 
} 


[ServiceContract] 
public interface IGeneralService 
{ 
    [OperationContract] 
    PhotoDTO[] GetPhotos(Guid subsectionId); 
} 

Répondre

5

EDIT: Comme alexdej souligne à juste titre, l'image sera linéariser/désérialiser avec DCS correctement, mais vous devez soit modifier le type de DataContract à Bitmap ou utilisez config pour spécifier un KnownType de Bitmap pour System.Drawing.Image à l'exécution (vous ne pouvez pas l'attribuer car vous ne possédez pas la classe Image).

La classe Image n'est pas adaptée à la sérialisation par DataContractSerializer: elle contient toutes sortes de liens vers les tampons GDI et autres sous les couvertures. DCS est destiné à représenter des objets de données dans lesquels vous contrôlez la structure entière de la classe. La confusion vient parce que dans 3.5SP1 ils ont ajouté la possibilité pour DCS de sérialiser des objets qui ne sont pas étiquetés avec DataContractAttribute (surtout comme une commodité pour les gens trop paresseux pour attribuer leurs classes de fils). L'effet secondaire malheureux est que le sérialiseur essaiera heureusement de sérialiser n'importe quel objet ancien, mais échouera à produire un résultat utile dans de nombreux cas (comme le vôtre).

D'une façon ou d'une autre, vous devrez le transformer en byte [] ou en Stream pour l'avoir sur le réseau et le réhydrater en tant qu'image. Si vous utilisez WCF et les mêmes types DataContract des deux côtés (par exemple, pas un type généré), vous pouvez laisser Graphic comme une propriété, mais ne le marquez pas avec DataMember. Faire le jeu sur le graphique peuplent le stockage pour un autre ImageBytes proprty (qui est étiqueté DataMember) en appelant Image.Save à un MemoryStream, puis vider le byte []. Rendre l'ensemble sur ImageBytes charger le stockage d'image interne de la propriété graphique de l'octet [] passé de la même manière. Lorsque l'objet est désérialisé à l'autre extrémité (par exemple, le désérialiseur appelle le paramètre ImageBytes), l'espace de stockage de la propriété poof- Graphic est renseigné et tout fonctionne. Comportement de sérialisation/désérialisation entièrement automatique: la propriété ImageBytes est juste un détail d'implémentation.

1

Essayez spécifiant explicitement votre DataContract

[DataContract()] 
public class PhotoDTO 
{ 
    [DataMember()] 
    public Guid Id { get; set; } 

    [DataMember()] 
    public Image Graphic { get; set; } 
} 

MISE À JOUR

Heres un code pour les questions des tests de sérialisation. Sans le type Bitmap (qui hérite de Image et j'utilise Image.FromFile() pour charger un fichier png) dans la liste des types connus cela casse. L'exception déclenchée dans ce contexte indique que Bitmap doit être un type connu. La sérialisation devrait alors fonctionner.

public static void LogDTO<T>(T dto) 
{ 
     DataContractSerializer ser = 
      new DataContractSerializer(typeof(T), 
       new []{typeof(System.Drawing.Image), 
         typeof(System.Drawing.Bitmap)}); 

     FileStream writer = new FileStream("C:\\temp\\" + dto.ToString() + ".xml", 
                  FileMode.Create); 
     ser.WriteObject(writer, dto); 
     writer.Close(); 
    } 
+0

Nope. ça ne marche pas. J'ai joué avec ça, ainsi qu'avec un tripotage avec l'attribut [KnownType (typeof (System.Drawing.Image))] mais sans trop de chance. Certains messages indiquent que si ma propriété graphique était un octet [] cela fonctionnerait et je suppose que c'est vrai. Cependant, le code de chaque côté de l'appel de service veut fonctionner avec la propriété graphique en tant qu'image. Une option serait de fournir une autre propriété byte [] GraphicAsByte {get; ensemble; } ou quelque chose comme ça qui convertirait l'image en octet [], mais cela ressemble à une sorte de piratage. –

+0

voir ma mise à jour, bon code pour déboguer les problèmes de sérialisation ... –

1

La classe Image est compatible avec DCSerializer - Image implémente ISerializable et «fait les bons choix» en termes de sérialisation des données raw byte [] de l'image.

Vous devriez recevoir des avertissements du processus de référence du service de mise à jour (recherchez-les dans la fenêtre Liste d'erreurs). Ce qui se passe probablement, c'est que Update Service Reference ne parvient pas à trouver le type System.Drawing.Image car votre projet client ne fait pas référence à System.Drawing.dll. Essayez d'ajouter une référence d'assembly à System.Drawing.dll et faites de nouveau Update Service Reference.

En fait, vous devriez mettre des attributs [DataContract] et [DataMember] sur votre type, pas [Serializable], comme mentionné par une autre affiche. Sinon, si vous utilisez 3.5SP1, vous pouvez laisser les attributs complètement désactivés.

+0

+1 pour me faire aller lire les docs sur DCS à nouveau pour appeler BS sur le support ISerializable, mais vous avez raison! Je suppose que vous apprenez quelque chose de nouveau chaque jour. :) – nitzmahone

0

Merci, nitzmahone. C'est ce dont j'avais peur, cependant. J'espérais éviter d'avoir à avoir une deuxième propriété associée au même domaine car il semble un peu « puant », mais peut-être la solution la plus simple:

[DataContract] 
public class PhotoDTO 
{ 
    private Guid _id = Guid.Empty; 
    private Image _graphic; 


    [DataMember] 
    public Guid Id 
    { 
     get { return _id; } 
     set { _id = value; } 
    } 

    [DataMember] 
    public byte[] GraphicBytes 
    { 
     get 
     { 
      if (_graphic == null) return new byte[0]; 

      using (MemoryStream ms = new MemoryStream()) 
      { 
       _graphic.Save(ms, ImageFormat.Png); 
       return ms.ToArray(); 
      } 
     } 

     set 
     { 
      if (value.Length > 0) 
      { 
       using (MemoryStream ms = new MemoryStream(value)) 
       { 
        _graphic = Image.FromStream(ms); 
       } 
      } 
      else 
      { 
       _graphic = null; 
      } 
     } 
    } 

    public Image Graphic 
    { 
     get { return _graphic; } 
     set { _graphic = value; } 
    } 


    public PhotoDTO() 
     : this(Guid.Empty, null) 
    { } 

    public PhotoDTO(Guid id, Image graphic) 
    { 
     _id = id; 
     _graphic = graphic; 
    } 
} 

}

+0

alexdej est juste- DCS va faire la bonne chose avec Image parce que c'est ISerializable. Voir ma réponse révisée ci-dessus. – nitzmahone

Questions connexes