我有一个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);
}
答案 0 :(得分:5)
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;
}
}
}