2009-11-20 64 views
2

我有一個WCF服務公開了一個方法,該方法返回一個包含Image屬性的對象數組(參見下面的代碼)。在相同的解決方案中,我有一個類庫項目,它具有對我的WCF項目的服務引用。在類庫中,當我嘗試「更新服務引用」時,我的代理類變爲不可用。當我從我的類中刪除「圖形」屬性時,我沒有更新類庫中的服務引用的困難,我的代碼都編譯並運行良好。我把「圖形」屬性放回來,代理類再次變得不可用。更奇怪的是,服務引用公開的唯一類是「Image」。在WCF中,如何返回包含System.Drawing.Image屬性的類?

我在這裏忽略了什麼?

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


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

回答

5

編輯:由於alexdej正確地指出,圖像將系列化/ DCS與反序列化正確的,但你必須要麼改變DataContract類型位圖或使用配置來指定位圖的用於爲System.Drawing.Image一個KnownType在運行時(你不能將它歸因於你不擁有Image類)。

Image類不適合通過DataContractSerializer進行序列化 - 它具有各種與GDI緩衝區和封面內容有關的關係。 DCS旨在表示控制整個班級結構的數據對象。混淆是因爲在3.5SP1中他們增加了DCS序列化沒有用DataContractAttribute標記的對象的能力(主要是爲了方便那些懶得指定他們的線類的人)。不幸的副作用是序列化程序會高興地嘗試序列化任何舊對象,但在許多情況下(如你的)會無法產生有用的結果。不管怎麼樣,你需要將它變成一個byte []或者一個Stream,以便通過網絡獲得它,並且將它作爲一個圖像進行補充。如果您在兩側使用WCF和相同的DataContract類型(例如,不是生成的類型),則可以將Graphic作爲屬性保留,但不要使用DataMember對其進行標記。通過將Image.Save調用到MemoryStream,然後轉儲字節[],使Graphic上的集合爲另一個特性ImageBytes(即帶標記的DataMember)填充存儲。使ImageBytes上的設置以相同的方式從傳入的byte []加載圖形屬性的內部圖像存儲。當對象在另一端反序列化時(例如,反序列化器調用ImageBytes設置器),poof-你的Graphic屬性的存儲被填充,並且這一切都可以工作。全自動序列化/反序列化行爲 - ImageBytes屬性只是一個實現細節。

1

嘗試明確指定你的DataContract

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

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

UPDATE

繼承人的測試序列化問題在一些代碼。如果沒有位圖類型(它繼承自Image,並且我使用Image.FromFile()來加載png文件),那麼這個類型會破壞已知類型的列表。在此上下文中引發的異常表明需要將Bitmap作爲已知類型。序列化應該工作。

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

都能跟得上。這是行不通的。我玩過這些,還有擺弄[KnownType(typeof(System.Drawing.Image))]]屬性,但沒有太多的運氣。 一些帖子指出,如果我的圖形屬性是一個字節[],它會工作,我懷疑這是真的。但是,服務調用任何一方的代碼都希望使用Graphic屬性作爲Image。一種選擇是提供另一個屬性 byte [] GraphicAsByte {get;組; } 或類似的東西,將圖像轉換成一個字節[],但這似乎是一種黑客。 – 2009-11-20 03:01:17

+0

看到我的更新,用於調試序列化問題的不錯代碼... – 2009-11-20 03:41:11

1

Image類與DCSerializer兼容 - 圖像實現了ISerializable,並且在圖像的原始字節數據序列化方面做了「正確的事情」。

您應該從更新服務引用過程中獲得一些警告(在錯誤列表窗口中查找它們)。可能發生的情況是Update Service Reference無法找到System.Drawing.Image類型,因爲您的客戶端項目沒有對System.Drawing.dll的引用。嘗試將一個程序集引用添加到System.Drawing。dll並再次執行更新服務參考。

順便說一句,您應該將[DataContract]和[DataMember]屬性放在您的類型上,而不是[Serializable],如其他海報所述。或者,如果您使用3.5SP1,則可以完全關閉這些屬性。

+0

+1讓我再次閱讀DCS上的文檔,以便在ISerializable支持上調用BS,但您是對的!我想你每天都會學到新的東西。 :) – nitzmahone 2009-11-21 22:28:11

0

謝謝,nitzmahone。不過,這正是我所害怕的。我希望避免必須有,因爲它似乎有點「臭」同場相關聯的第二屬性,但可能是最簡單的辦法:

[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是對的 - DCS實際上會用Image做正確的事情,因爲它是ISerializable。看到我上面修改的答案。 – nitzmahone 2009-11-21 22:22:44