OData包含 [英] OData Containment

查看:49
本文介绍了OData包含的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I just learn that with the [Contained] attribute I can define a contained collection. This means the collection is no more accessible from my root oData system. Ok fine, but here is my model:

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

I have a user that have addresses The user has invoices Each invoice can have one or two addresses from the user.

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

On which collection should I add the contained attribute?

推荐答案

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

The answer to this completely depends on your domain model. The advice I would give is to use OData containment sparingly. It really only makes sense to use it if the entity you are marking as being a contained entity cannot exist outside of the context of the parent entity. Because of this constraint I think the use cases for OData containment are few and far in between. The advantage over a separate controller is that it can make more sense from an architectural standpoint. However your controllers become more bloated and it is more work to maintain the ODataRouteAttributes on your methods. Something which is not necessary when using convention based routing.

The example on the guide to set up OData containment explains it somewhat. It falls a bit short on why you would use it. Note that the PaymentInstrument entity has no foreign key to Account. This means that there is no separate table where the PaymentInstrument information is stored. Instead it is stored directly on the Account record. Yet is still defined as a Collection<T> so it is probably stored as JSON or across multiple columns. This might not necessarily be the case, but from a code standpoint the database could look like that.

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

To further explain OData containment let's say we have the domain model below.

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 .

As you can see the HttpResponse class has no navigation property to HttpRequest. Therefore it makes no sense to want to call GET odata/HttpResponses as we would be getting all HttpResponses, but not the HttpRequest they are linked to. In other words the HttpResponse class is useless without the context i.e. the HttpRequest for which it was produced.

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

The HttpResponse class not having any meaning outside of the HttpRequest context makes it a perfect candidate for OData containment. Both classes could even be saved on the same record in the database. And because it's not possible to perform a GET/POST/PUT/DELETE without specifying the id of the HttpRequest to which the HttpResponse belongs, it makes no sense for the HttpResponse class to have its own controller.

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

Now, back to your use case. I can see two likely domain models.

  1. 实体 User UserAddress 发票 InvoiceAddress .

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

In this first option every single entity has their own designated address entity. OData containment would make sense here using such a design as the address entities do not exist outside of their respective parent entity. A UserAddress is always linked to a User and an InvoiceAddress is always linked to an Invoice. Getting a single UserAddress entity makes less sense because using this domain model one shouldn't care where the single address is. Instead the focus lays more on what the persisted addresses for this single User are. It's also not possible to create a UserAddress without specifying an existing User. The UserAddress entity relies on the User entity entirely.

  1. 实体 User 发票 TypedAddress Address .

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

In this second option the Address entity is stand-alone. It exists separately from the other entities. Since an address boils down to a unique location on this planet it is only saved once. Other entities then link to the Address entity via the TypedAddress entity where they specify what kind of address it is in relation to the entity linking to it. Getting a single Address makes perfect sense using this domain model. An addressbook of the entire company could easily be retrieved by requesting GET odata/Addresses. This is where OData containment does not make sense.

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

Do note that it is possible to use the ODataConventionModelBuilder to configure containment. Because you do not need to add the ContainedAttribute to your class, this has the advantage of not polluting your data layer with a reference to the OData library. I would recommend this approach. In your situation I would expect to have the configuration below.

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

这篇关于OData包含的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆