使用DataContractSerializer序列化接口列表

3

我有一个类,其中包含一些嵌套的类。我对它进行序列化并使用一个没有问题的方法将其发送到wcf服务。以下是该类:

public class ComputerDTO
{
    [DataMember]
    public ComputerTypeDTO Type { get; set; }

    [DataMember]
    public string ComputerName { get; set; }

    [DataMember]
    public MonitorDTO Monitor { get; set; }
}

这是方法:

public void Check()
{
    Computer c = new Computer();

    ISystemInfoOperations cli = GetServiceClient();

    Mapper.CreateMap<Monitor, MonitorDTO>();
    Mapper.CreateMap<IHardwarePart, IHardwarePartDTO>();

    Mapper.CreateMap<Computer, ComputerDTO>()
            .ForMember(s => s.Hardware, m => m.MapFrom(q => Mapper.Map<List<IHardwarePart>, List<IHardwarePartDTO>>(q.Hardware)));


    Mapper.AssertConfigurationIsValid();

    ComputerDTO dto = Mapper.Map<Computer, ComputerDTO>(c);

    string sendComputerInfo = cli.SendComputerInfo(dto);
}

但我还有一份需要发送的接口清单。所以我按照下面的代码进行更改,结果出现了错误。

public class ComputerDTO
{
    [DataMember]
    public ComputerTypeDTO Type { get; set; }

    [DataMember]
    public string ComputerName { get; set; }

    [DataMember]
    public MonitorDTO Monitor { get; set; }

    [DataMember]
    public List<IHardwarePartDTO> Hardware { get; set; }
}

public interface IHardwarePartDTO
{
    [DataMember]
    string Name { get; set; }

    [DataMember]
    HardwarePartTypeDTO PartType { get; set;  }
}

项目中硬件部分已经填好。但是当我尝试发送它时,出现了这个著名的错误:

类型 "Proxy" 的数据合同名称 "_x0030__Culture_x003D_neutral_PublicKeyToken_x003D_null_x003E_:http://schemas.datacontract.org/2004/07/Proxy%3CSystemInfo.DTO.IHardwarePartDTO_SystemInfo.DTO_Version=1.0.0" 不被预期。考虑使用DataContractResolver或将任何静态未知的类型添加到已知类型列表中,例如使用KnownTypeAttribute属性或将其添加到传递给DataContractSerializer的已知类型列表中。

注:本文涉及IT技术相关内容。

2
你尝试过使用KnownTypes吗?这篇文章应该能帮助你更好地理解正在发生的事情[已知类型和通用解析器](http://msdn.microsoft.com/en-us/magazine/gg598929.aspx)由Juval。 - Petar Vučetin
谢谢Petar。我正在看它。 - Kevin Callagher
1个回答

5

DataContractSerializer需要知道它可能返回的具体类型。接口无法序列化,因为它无法反序列化(没有具体实现,如何创建接口的实例)。

简单的解决方法是添加KnownTypes属性,像下面这样:

[KnownType(typeof(your hardware dto concrete type here))]
public class ComputerDTO
{
    [DataMember]
    public ComputerTypeDTO Type { get; set; }

    [DataMember]
    public string ComputerName { get; set; }

    [DataMember]
    public MonitorDTO Monitor { get; set; }

    [DataMember]
    public List<IHardwarePartDTO> Hardware { get; set; }
}

你可以添加任意数量的已知类型属性。
稍微复杂一些的是ServiceKnownTypes属性。这与之前类似,但你需要将其添加到服务类中。
除此之外,你可以使用数据合同解析器-但这非常复杂,需要花费一些时间来解释。
编辑:18/02/2013 15:11
你可能还需要查看Automapper,因为它当前会在你的Hardware列表中创建代理-而代理无法序列化。你需要告诉Automapper将它们序列化为什么类型-例如:
Mapper.CreateMap<Monitor, MonitorDTO>();
Mapper.CreateMap<Monitor, IHardwarePartDTO>().As<MonitorDTO>();

Mapper.CreateMap<Audio, AudioDTO>();
Mapper.CreateMap<Audio, IHardwarePartDTO>().As<AudioDTO>();

Mapper.CreateMap<CDROMDrive, CDROMDriveDTO>();
Mapper.CreateMap<CDROMDrive, IHardwarePartDTO>().As<CDROMDriveDTO>();
//you need entries like these for everythin that implements IHardwarePartDTO

这样,automapper就知道它需要创建什么了。

嗨,马特。谢谢你的回复。当我按照你说的添加KnownType属性时,它会出现错误。"Attribute 'KnownType'在此声明类型上无效。它只在'class, struct'声明上有效。" - Kevin Callagher
抱歉,我没有考虑清楚就写了那个!根据@JohnSaunders的评论,已经修正以显示正确的实现。 - Matt Whetton
没关系。实际上,我之前已经在类上添加了knowntype属性。它可以在没有该列表的情况下工作。但是我需要那个列表。不过,如果有该列表,它就无法工作。[DataContract] [KnownType(typeof(CPUDTO))] [KnownType(typeof(AudioDTO))] [KnownType(typeof(CDROMDriveDTO))] [KnownType(typeof(HardDriveDTO))] [KnownType(typeof(MemoryDTO))] [KnownType(typeof(MotherBoardDTO))] [KnownType(typeof(NetworkDTO))] [KnownType(typeof(SCSIDriveDTO))] [KnownType(typeof(USBDTO))] [KnownType(typeof(VideoDTO))] public class ComputerDTO { .... - Kevin Callagher
我认为问题更多地与automapper代码有关,它使用代理来处理您的接口类型 - WCF无法序列化代理。我认为您需要使用As<T>方法来强制它使用正确的对象。 - Matt Whetton
1
好的,所以我进行了另一个编辑,可能会更好地解释它。 你实际上需要首先将每个具体的硬件类型映射到IHardwarePartDto,并使用其DTO副本。同时将原始类型单独映射到DTO副本。 - Matt Whetton
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接