OData Containment - PullRequest
       18

OData Containment

1 голос
/ 14 марта 2019

Я только что узнал, что с атрибутом [Contained] я могу определить содержащуюся коллекцию. Это означает, что коллекция больше не доступна из моей корневой системы oData. Хорошо, хорошо, но вот моя модель:

У меня есть пользователь с адресами У пользователя есть счета Каждый счет может иметь один или два адреса от пользователя.

В какую коллекцию я должен добавить содержащийся атрибут?

1 Ответ

0 голосов
/ 14 апреля 2019

Ответ на этот вопрос полностью зависит от модели вашего домена. Совет, который я бы дал, заключается в том, чтобы экономно использовать OData. На самом деле имеет смысл использовать его только в том случае, если объект, который вы помечаете как отдельный объект, не может существовать вне контекста родительского объекта. Из-за этого ограничения я думаю, что варианты использования для хранения OData немногочисленны. Преимущество перед отдельным контроллером состоит в том, что он может иметь больше смысла с архитектурной точки зрения. Однако ваши контроллеры становятся более раздутыми, и это требует больше усилий, чтобы поддерживать ODataRouteAttributes в ваших методах. Что-то, что не нужно при использовании условной маршрутизации.

Пример руководства по настройке содержания OData объясняет это несколько. Это немного отстает от того, почему вы будете использовать его. Обратите внимание, что у объекта 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. Оба класса могут быть сохранены в одной записи в базе данных. И поскольку невозможно выполнить GET / POST / PUT / DELETE без указания идентификатора HttpRequest, к которому принадлежит HttpResponse, для класса HttpResponse нет смысла иметь собственный контроллер.

Теперь вернемся к вашему варианту использования. Я вижу две вероятные модели предметной области.

  1. Сущности User, UserAddress, Invoice и InvoiceAddress.

В этом первом варианте каждый отдельный объект имеет свой собственный назначенный адресный объект. В этом случае использование OData будет иметь смысл, если использовать такой дизайн, поскольку адресные объекты не существуют вне их соответствующих родительских объектов. UserAddress всегда связан с User, а InvoiceAddress всегда связан с Invoice. Получение единственного объекта UserAddress не имеет смысла, поскольку при использовании этой модели предметной области не нужно заботиться о том, где находится один адрес. Вместо этого основное внимание уделяется постоянным адресам для этого единственного User. Также невозможно создать UserAddress без указания существующего User. Сущность UserAddress опирается на сущность User полностью .

  1. Сущности User, Invoice, TypedAddress и Address.

В этом втором варианте объект 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);
...