【问题标题】:Populating association properties in entities from service call从服务调用填充实体中的关联属性
【发布时间】:2011-05-05 22:42:41
【问题描述】:

假设我有一个包含 Customer 对象和 SalesOrder 对象的通用模式。我有相应的 SalesOrderContract 和 CustomerContract 对象,它们是相似的、更扁平的对象,用于通过 Web 服务进行序列化

public class Customer 
{
     public int CustomerId { get; set; }
     public string Name { get; set; }
     public Address ShippingAddress { get; set; }
     //more fields...
}

public class Order
{
     public int OrderId { get; set; }
     public Customer Customer { get; set;
     // etc
}

我的销售订单合同是这样的

public class OrderContract
{
     public int OrderId { get; set; }
     public int CustomerId { get; set; }
}

public class OrderTranslator
{
     public static Order ToOrder(OrderContract contract)
     {
          return new Order { OrderId = contract.OrderId  };
          // just translate customer id or populate entire Customer object
     }
 }

我在服务层和业务对象层之间有一个层,在两者之间进行转换。我的问题是……我是否在另一端填充 Order.Customer 对象,因为 Order 表只需要客户 ID。我没有在 OrderContract 中携带整个客户对象,因为它没有必要而且太重。但是,作为保存它的一部分,我必须验证它确实是一个有效的客户。我可以做一些事情

  1. 当我在合同和实体之间进行转换时,完全基于 CustomerId 填充 Order.Customer 对象。这需要在实体和合同之间转换的帮助器类中调用 CustomerRepository。我觉得不对。翻译器应该只是数据映射。
  2. 为每组执行所需验证的操作创建一个域服务,而无需填充 Order.Customer。该服务将根据 Order.CustomerId 拉取 Customer 对象并检查它是否有效。对此不确定,因为销售订单应该能够自我验证,但它也没有明确处理订单,因为它也处理客户,所以可能是域服务?
  3. 创建一个单独的属性 Order.CustomerId 并在此基础上延迟加载客户对象。
  4. 从工厂类中填充 Order.Customer。现在我的工厂类只是为了从数据库加载。我并没有真正从数据合同中加载,但也许这有意义?

所以问题是两部分...如果您的实体中有关联属性,需要在保存之前判断某些内容是否完全有效,您是否只是填充它们?如果你这样做了,你实际上在哪里这样做,因为合同/实体翻译感觉不对?

底线是我需要能够做类似的事情

 if (order.Customer == null || !order.Customer.IsActive)
 {
      //do something
 }

问题是这样做有什么意义?实际上,我的 Order 对象有很多验证所需的子实体,我不希望事情变得臃肿。这就是为什么我正在考虑制作域服务来封装验证,因为在我的特定情况下它是一个如此巨大的操作(数百个奇怪的规则)。但我也不想删除所有使我的对象只是属性的逻辑。找到平衡很难。

希望这是有道理的。如果需要更多背景知识,请告诉我。

【问题讨论】:

  • 有人吗?如果你有观点,请分享一下?
  • 我一直在做更多的研究,我发现在创建新实体时,填充实体对象没有多大意义,因为它实际上并不在数据存储中。好的,我明白这一点,但是如果您需要进行复杂的验证(不仅仅是属性级别的东西),并且您没有完全填充的实体,那么您到底是如何做到的。

标签: wcf domain-driven-design datacontract


【解决方案1】:

这里发生了几件事。我认为部分问题主要在于您似乎如何安排您的翻译课程。请记住,对于一个实体,整个概念是基于实例身份的。所以一个实体的翻译器不应该返回一个新的对象,它应该返回对象的正确实例。这通常意味着您必须首先为其提供该实例。

从更新与创建新对象的角度考虑可能会很有用。

对于更新,我将构建此操作的方式如下:我将拥有应用程序调用以获取和返回合同对象的 Web 服务。该 Web 服务调用存储库和翻译器来完成它的工作。验证停留在域对象上。

在代码中,更新将如下所示。

网络服务:

[WebService]
public class OrderService
{
    [WebMethod]
    public void UpdateOrder(OrderContract orderContract)
    {
        OrderRepository orderRepository = new OrderRepository(_session);

        // The key point here is we get the actual order itself
        // and so Customer and all other objects are already either populated
        // or available for lazy loading.

        Order order = orderRepository.GetOrderByOrderContract(orderContract);

        // The translator uses the OrderContract to update attribute fields on
        // the actual Order instance we need.

        OrderTranslator.OrderContractToOrder(ref order, orderContract);

        // We now have the specific order instance with any properties updated
        // so we can validate and then persist.

        if (order.Validate())
        {
            orderRepository.Update(order);
        }
        else
        {
            // Whatever
        }
    }
}

译者:

public static class OrderTranslator
{
    public static void OrderContractToOrder(ref Order order, OrderContract orderContract)
    {
        // Here we update properties on the actual order instance passed in
        // instead of creating a new Order instance.

        order.SetSomeProperty(orderContract.SomeProperty);
        // ... etc.
    }

}

这里的关键概念是因为我们有一个实体,我们正在获取实际的订单,实体的实例,然后使用翻译器更新属性而不是创建一个新的订单实例。因为我们获取的是原始订单,而不是创建新实例,所以大概我们可以通过延迟加载填充或填充所有关联。我们不必从 OrderContract 重新创建任何关联,因此问题就消失了。

我认为问题的另一部分可能是您对工厂设计方式的理解。确实,对于实体而言,工厂可能不会设置所有可能的属性——如果这样做,该方法可能会变得非常复杂。

但是工厂应该做的是为新对象创建所有关联,以便返回的新对象处于有效状态,即完整且有效的聚合。然后调用者可以设置所有其他各种各样的“简单”属性。

只要你有一个工厂,你就必须决定要传入哪些参数。也许在这种情况下,Web 服务会获取实际的客户并将其作为参数传递给工厂。或者也许 Web 服务传入一个 Id,工厂负责获取实际的 Customer 实例。它会因具体情况而异,但无论如何,无论它获得所需的其他对象,工厂应该至少返回一个完全填充的对象,即所有关系都应该存在并且可以遍历。

在代码中,创建新订单的一个可能示例可能是:

[WebService]
public class OrderService
{

    [WebMethod]
    public void SaveNewOrder(OrderContract orderContract)
    {
        // Lets assume in this case our Factory has a list of all Customers
        // so given an Id it can create the association.

        Order order = OrderFactory.CreateNewOrder(orderContract.CustomerId);

        // Once again we get the actual order itself, albeit it is new,
        // and so Customer and all other objects are already either populated
        // by the factory create method and/or are available for lazy loading.

        // We can now use the same translator to update all simple attribute fields on
        // the new Order instance.

        OrderTranslator.OrderContractToOrder(ref order, orderContract);

        // We now have the new order instance with all properties populated
        // so we can validate and then persist.

        if (order.Validate())
        {
            //Maybe you use a Repository - I use a unit of work but the concept is the same.
            orderRepository.Save(order);
        }
        else
        {
            //Whatever
        }
    }
}

那么,希望对你有帮助吗?

【讨论】:

  • 这确实有点帮助。我了解更新场景。这是最有意义的,因为您在从数据库中填充它之前不会对其进行操作。这是困扰我的创建场景。我最终采用了类似的方法,在服务调用中建立关联。我的问题是在 OrderTranslator 中这样做感觉很奇怪。我不确定这样做是否“可以”,因为它现在需要某种对存储库的引用。因此我把它移到了一个我觉得更自然的工厂类。
猜你喜欢
  • 2010-12-15
  • 2020-10-03
  • 2012-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多