在WCF中,如何返回包含System.Drawing.Image属性的类?

时间:2009-11-20 02:00:16

标签: wcf image

我有一个WCF服务,它公开了一个方法,该方法返回一个包含Image属性的对象数组(参见下面的代码)。在同一个解决方案中,我有一个类库项目,它有一个对我的WCF项目的服务引用。在类库中,当我尝试“更新服务引用”时,我的代理类变得不可用。当我从我的类中删除“Graphic”属性时,我没有任何困难更新类库中的服务引用,我的代码都编译并运行正常。我把“Graphic”属性重新放入,代理类再次变得不可用。更奇怪的是,服务引用所暴露的唯一类是“Image”。

我在这里俯瞰什么?

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


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

4 个答案:

答案 0 :(得分:5)

编辑:正如alexdej正确指出的那样,Image会正确地使用DCS序列化/反序列化,但您必须将DataContract类型更改为Bitmap或使用config在运行时为System.Drawing.Image指定一个KnownType of Bitmap(您不能归因于它,因为你没有Image类)。

Image类不适合DataContractSerializer进行序列化 - 它与GDI缓冲区和封装内容有各种联系。 DCS旨在表示控制类的整个结构的数据对象。之所以引起混淆,是因为在3.5SP1中,他们为DCS添加了序列化未使用DataContractAttribute标记的对象的能力(主要是为了方便那些懒得将其线类属性化的人)。不幸的副作用是序列化程序很乐意尝试序列化任何旧对象,但在许多情况下(如你的)无法产生有用的结果。

不管怎样,你需要把它变成一个byte []或一个Stream来通过线路,然后把它重新水合成一个图像。如果您在两侧使用WCF和相同的DataContract类型(例如,不是生成的类型),则可以将Graphic保留为属性,但不要使用DataMember标记它。通过将Image.Save调用到MemoryStream,然后转储byte [],使图形填充存储为另一个属性ImageBytes(标记为DataMember)。使ImageBytes上的set以相同的方式从传入的byte []加载图形属性的内部Image存储。当对象在另一端被反序列化时(例如,反序列化器调用ImageBytes setter),poof-你的Graphic属性的存储被填充,并且一切正常。全自动序列化/反序列化行为 - ImageBytes属性只是一个实现细节。

答案 1 :(得分:1)

尝试明确指定DataContract

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

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

<强>更新

这是用于测试序列化问题的一些代码。如果没有Bitmap类型(继承自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();
    }

答案 2 :(得分:1)

Image类与DCSerializer兼容 - Image只是序列化图像的原始byte []数据,实现了ISerializable和“做正确的事”。

您应该从“更新服务引用”过程中获取一些警告(在“错误列表”窗口中查找它们)。可能发生的事情是更新服务引用无法找到System.Drawing.Image类型,因为您的客户端项目没有对System.Drawing.dll的引用。尝试添加对System.Drawing.dll的程序集引用,并再次执行“更新服务引用”。

顺便说一下,你应该把[DataContract]和[DataMember]属性放在你的类型上,而不是[Serializable],正如另一张海报所提到的那样。或者,如果您使用的是3.5SP1,则可以完全保留属性。

答案 3 :(得分: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;
    }
}

}