【问题标题】:Avoid serializing certain properties in REST services避免序列化 REST 服务中的某些属性
【发布时间】:2011-04-16 03:32:01
【问题描述】:

我在客户端和服务器端有一个 .Net 应用程序,服务器提供 REST 服务(使用 WCF)。我有这样的服务定义:

[WebGet(UriTemplate = "/Customers/{id}")]
Customer GetCustomerById(string id);

[WebGet(UriTemplate = "/Customers")]
List<Customer> GetAllCustomers();

使用 Fluent NHibernate 和延迟加载将 Customer 类及其朋友映射到数据库。如果我从会话范围之外的服务返回,则服务调用将失败,因为它无法序列化引用的延迟加载的 Orders 属性(请参阅最后的类 def)。问题是我需要延迟加载,因为我不希望我的GetAllCustomers-service 获取所有引用的订单。所以我想做的是以某种方式通知序列化程序,这样它就不会尝试在 GetAll 上序列化 Orders。但请注意,必须在 GetCustomerById 上序列化相同的属性 - 所以我必须在服务上指定它。这可以吗?!

类:

public class Customer
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Order> Orders { get; set; }
}

public class Order
{
    public virtual int Id { get; set; }
    // ++ 
}

【问题讨论】:

    标签: .net wcf nhibernate rest serialization


    【解决方案1】:

    如果您使用 WCF 的默认序列化 - 我认为您会这样做 - 您将明确标记要通过网络发送的属性并保留其余的。这是使用[DataMember] 完成的,我假设您这样做:

    [DataContract]
    public class Customer
    {
        [DataMember]
        public virtual int Id { get; set; }
    
        [DataMember]
        public virtual string Name { get; set; }
    
        // not decorate 
        public virtual IList<Order> Orders { get; set; }
    } 
    

    更新 好的,您需要有时发送,而不是其他时间发送。显然,您可以拥有 CustomerBase 类(没有订单),然后是 Customer(未装饰的订单)和 CustomerWithOrders(装饰的订单)。从您的每个操作中发送每个。

    如果这不适合您,请在此处查看使用 DataContractSerializerOperationBehaviorIDataContractSurrogate 的自定义序列化:

    http://msdn.microsoft.com/en-us/library/system.runtime.serialization.idatacontractsurrogate.aspx

    http://msdn.microsoft.com/en-us/library/system.servicemodel.description.datacontractserializeroperationbehavior_members.aspx

    【讨论】:

    • 是的,当然 - 你是绝对正确的。但我的问题是,这不取决于类,而是取决于服务。使用 GetAllCustomers 时不应序列化订单,但应在使用 GetCustomerById 时序列化。所以我正在寻找一种在服务上指定它的方法——或者告诉序列化程序不要尝试加载延迟加载的对象。如果不清楚,我很抱歉 - 我更新了问题以使其更清楚。
    • @stiank81:在这种情况下,也许您应该返回不同的 DTO?您可以将您的序列化规则应用于 DTO 并序列化那些而不是您的实体。如果您需要统一对待它们,您可以让它们实现一个通用接口。
    • 感谢您的努力。会调查你的建议。 +1。
    【解决方案2】:

    为不同的场景使用不同的 DTO,然后使用 Automapper 将您的 nhib 对象传输到 DTO - 这样您就可以为每种情况创建一个专用的对象图,然后您就无法进行迭代,从而为您的集合补充水分不想。

    【讨论】:

    • 这是有道理的,并且可能是最好的解决方案。会考虑是否有更简单的方法,或者我是否应该这样做。谢谢。
    【解决方案3】:

    我会为GetAllCustomers 服务返回不同的数据合同。例如,

    [DataContract]
    public class CustomerSummary
    {
       // Have properties that represent the summary of the customer
    }
    
    WebGet(UriTemplate = "/Customers/{id}")]    
    Customer GetCustomerById(string id);    
    
    [WebGet(UriTemplate = "/Customers")]    
    List<CustomerSummary> GetAllCustomers();
    

    或者,在将DataMember 属性应用于Orders 属性时使用EmitDefaultValue

    [DataContract]       
    public class Customer       
    {       
       [DataMember]       
       public virtual int Id { get; set; }       
    
       [DataMember]
       public virtual string Name { get; set; }       
    
       [DataMember(IsRequired = false, EmitDefaultValue = false)]
       public virtual IList<Order> Orders { get; set; }       
    }
    

    然后在 GetAllCustomers 返回它的数据时将 Orders 属性保留为空。

    【讨论】:

    • 感谢您提出宝贵的建议。第一个绝对是一个不错的选择,但我会考虑是否也可以使用第二个。
    【解决方案4】:

    延迟加载仅在休眠会话范围内有效。没有机会通过电线这样做。您需要急切地获取集合或使用 OOP 继承并从您的 Web 方法返回一个根本没有此集合的类,尤其是在消费者不需要它的情况下。

    【讨论】:

    • 是的,我知道延迟加载仅在会话范围内有效。您是说唯一的解决方案是拥有 2 个不同的课程 - 一个有订单,一个没有订单?当我使用 SOAP 进行传输时,它可以很好地传输带有延迟加载引用的对象,但显然我不能在客户端使用未初始化的对象。这也是我需要休息的地方。可以做到吗?我不明白造成这种差异的原因是什么。rest 是否默认使用不同的序列化程序?
    • wcf 默认使用不同的序列化程序 - 这是 rest 的事实并不难。
    • 您可以通过使用 [XmlSerializerFormat] 装饰 class 和 req 道具来恢复到旧的 skool 序列化程序
    猜你喜欢
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-27
    • 2017-02-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多