OData包含

时间:2019-03-14 13:14:22

标签: asp.net-core-webapi odata-v4

我刚刚了解到,使用[Contained]属性可以定义一个包含的集合。这意味着无法从我的根oData系统访问该集合。好的,但这是我的模型:

我有一个拥有地址的用户 用户有发票 每个发票可以有一个或两个来自用户的地址。

我应该在哪个集合上添加包含的属性?

1 个答案:

答案 0 :(得分:0)

答案完全取决于您的域模型。我将给出的建议是谨慎使用OData包含。只有当您标记为包含实体的实体不能存在于父实体的上下文之外时,才真正使用它。由于这种限制,我认为OData包含的用例很少而且介于两者之间。与单独的控制器相比,其优势在于,从体系结构的角度来看,它更有意义。但是,您的控制器变得更加肿,维护方法上的ODataRouteAttributes更加艰巨。使用基于约定的路由时不必要的事情。

the guide to set up OData containment上的示例对此有所解释。它对您为什么要使用它有些欠缺。请注意,PaymentInstrument实体没有Account的外键。这意味着没有单独的表存储PaymentInstrument信息。而是直接将其存储在Account记录中。但是仍然定义为Collection<T>,因此它很可能存储为JSON或跨多列存储。可能不一定是这种情况,但是从代码角度来看,数据库看起来像那样。

要进一步说明OData包含,我们假设下面有域模型。

public class HttpRequest
{
    public int Id { get; set; }

    public DateTime CreatedOn { get; set; }

    public string CreatedBy { get; set; }

    public virtual HttpResponse HttpResponse { get; set; }
}

public class HttpResponse
{
    public string Content { get; set; }

    public DateTime CreatedOn { get; set; }
}

如您所见,HttpResponse类没有到HttpRequest的导航属性。因此,要调用GET odata/HttpResponses是没有任何意义的,因为我们将获得所有的HttpResponses,而不是它们链接的HttpRequest。换句话说,HttpResponse类在没有上下文(即为其生成的HttpRequest)的情况下是无用的。

HttpResponse类在HttpRequest上下文之外没有任何意义,使其成为OData包含的理想选择。这两个类甚至可以保存在数据库的同一记录中。并且由于无法在不指定HttpRequest所属的HttpResponse的ID的情况下执行GET / POST / PUT / DELETE,因此HttpResponse类没有意义它自己的控制器。

现在,回到您的用例。我可以看到两种可能的领域模型。

  1. 实体UserUserAddressInvoiceInvoiceAddress

在第一个选项中,每个单个实体都有其自己的指定地址实体。由于地址实体在其各自的父实体之外不存在,因此在这里使用这样的设计可以使OData包含。 UserAddress始终链接到User,而InvoiceAddress始终链接到Invoice。获得单个UserAddress实体的意义不大,因为使用此域模型无需理会单个地址在哪里。相反,重点更多地放在了这个User的持久地址上。如果不指定现有的UserAddress,也无法创建UserUserAddress实体完全依赖于User实体。

  1. 实体UserInvoiceTypedAddressAddress

在第二个选项中,Address实体是独立的。它与其他实体分开存在。由于地址归结为该星球上的唯一位置,因此仅保存一次。然后,其他实体通过Address实体链接到TypedAddress实体,并在其中指定与链接到该实体的实体有关的地址类型。使用此域模型,获得单个Address非常有意义。通过请求GET odata/Addresses可以轻松检索整个公司的地址簿。这就是OData约束没有意义的地方。

请注意,可以使用ODataConventionModelBuilder来配置容纳。因为您不需要向类中添加ContainedAttribute,所以它的优点是不会引用OData库污染数据层。我会推荐这种方法。根据您的情况,我希望使用以下配置。

var modelBuilder = new ODataConventionModelBuilder();
modelBuilder
    .EntityType<User>()
    .ContainsMany(user => user.UserAddresses);
modelBuilder
    .EntityType<Invoice>()
    .ContainsMany(invoice => invoice.InvoiceAddresses);