Как службы на основе сообщений должны обрабатывать операции извлечения?

Я рассматривал возможность перехода к службе на основе сообщений (стиль ServiceStack) и отказу от служб стиля WCF (почти RPC). После использования сервисов в стиле WCF я вижу некоторые недостатки и хочу попробовать другой подход. Меня убивает то, что методы WCF не могут иметь перегрузок, и я понимаю, почему, и я знаю, что есть способы обойти это, но они кажутся мне хакерскими. Где службы, основанные на сообщениях, признают, что все происходит по сети.

Вопрос, который у меня есть, заключается в том, как обрабатывать извлечение данных. ServiceStack (и другие API), похоже, имеют один объект запроса для получения либо одного объекта, либо набора объектов. Этот объект запроса имеет ряд необязательных параметров. Например

public GetInvoiceRequest
{
    public int? InvoiceId {get; set;}

    public int? AccountId {get; set;}

    public DateTime? From {get; set;}
}

public GetInvoiceResponse
{
    public List<InvoiceDto> {get;set;}

    public ResponseStatus Status {get;set;}
}

Какова стандартная практика для такого рода вещей? Включаете ли вы счета для каждого необязательного параметра? Таким образом, вы получаете счет для идентификатора, затем, если AccountId также установлен, добавьте к нему все счета для этой учетной записи, а если также установлен From, добавьте все счета с определенной даты? Эффективно работающие союзы. Или вы выполняете пересечение этих множеств? Или вы просто соблюдаете первый установленный параметр. Итак, если все параметры установлены, вы возвращаете только Invoice, соответствующий InvoiceId, потому что это первый встречающийся параметр?


soa
person uriDium    schedule 13.05.2014    source источник


Ответы (2)


У вас правильная идея, и ваш подход верен, но люди склонны отделять намерение запроса конкретного счета от запроса списка счетов.

Я склонен создавать сервис таким образом, чтобы мои DTO следовали формату операций C-R-U-D-L, т.е. Создать список для чтения, обновления и удаления типа DTO:

Поэтому, когда у меня есть ReadInvoiceRequest а не GetInvoiceRequest, я знаю, что всегда будет возвращаться один объект InvoiceDto и требуется параметр InvoiceId:

[Route("/invoices/{InvoiceId}", "GET")]
[Route("/account/{AccountId}/invoices/{InvoiceId}","GET")]
public ReadInvoiceRequest : IReturn<InvoiceDto>
{
    public int InvoiceId { get; set; } // InvoiceId is always expected
    public int? AccountId { get; set; }
    public DateTime? From { get; set; }
}

А маршруты специально для листинга всегда будут возвращать список InvoiceDto:

[Route("/invoices", "GET")]
[Route("/account/{AccountId}/invoices","GET")]
public ListInvoicesRequest : IReturn<List<InvoiceDto>>
{
    public int? AccountId { get; set; }
    public DateTime? From { get; set; }
}

Это предотвращает двусмысленность в отношении того, сколько результатов было бы возвращено, если бы было указано InvoiceId, а типом возвращаемого значения был список результатов.

Это также дает понять потребителю, что необязательные параметры в DTO запроса будут рассматриваться как фильтры.

Тогда ваши методы действий в вашем сервисе будут похожи на это:

public class MyInvoiceService : Service
{
    // CREATE
    public int Post(CreateInvoiceRequest request)
    {
        // Return the InvoiceId of the created record
    }

    // READ
    public InvoiceDto Get(ReadInvoiceRequest request)
    {
        var invoice = ... // request.InvoiceId; (filter by AccountId & From if set)
        return invoice;
    }

    // UPDATE
    public void Post(UpdateInvoiceRequest request)
    {
        // Handle update
        // I don't return anything, only throw exceptions if update fails
        // Success is indicated by 200 status
    }

    // DELETE
    public void Delete(DeleteInvoiceRequest request)
    {
        // Handle delete
        // I don't return anything, only throw exceptions if delete fails
        // Success is indicated by 200 status
    }

    // LIST
    public List<InvoiceDto> Get(ListInvoicesRequest request)
    {
        var invoices = ... // (filter by AccountId & From if set)
        return invoices;
    }
}

С точки зрения потребителей услуг, я считаю, что меньше неопределенности в отношении ответа, который он получит от разделенных DTO.

person Scott    schedule 13.05.2014
comment
Большое спасибо. Думаю, я предпочитаю этот подход. - person uriDium; 13.05.2014
comment
@uriDium Пожалуйста. Я рад объяснить что-нибудь еще. Удачи с вашим проектом :) - person Scott; 13.05.2014
comment
Привет @Scott по этому поводу, разве ваши ответы больше не должны реализовывать IHasResponseStatus? Насколько я понимаю, это помогает с ошибками/проверками и т. д.? - person CallumVass; 14.05.2014
comment
@BiffBaffBoff Я решил не реализовывать IHasResponseStatus в своих DTO, потому что мне не нужно возвращать объект DTO вместе с исключением. Я просто хочу исключения. Таким образом, при возникновении исключения объект, содержащий только ResponseStatus, автоматически создается ServiceStack и предоставляет необходимый ответ об ошибке без дополнительных затрат. - person Scott; 14.05.2014
comment
О, ладно, насколько я понял, вам нужно было это реализовать, кажется, я неправильно понял или это старый код? Спасибо за ответ! - person CallumVass; 14.05.2014
comment
@BiffBaffBoff Я считаю, что в какой-то момент это было необходимо реализовать, но это стало необязательным даже в версии 3. Это упоминается в разделе обработки ошибок здесь< /а>. :) - person Scott; 14.05.2014

ServiceStack API отошел от одного шаблона DTO «запрос» и «сопоставление ответа». ServiceStack теперь позволяет использовать множество различных DTO запросов и возможность явно назначать объект ответа.

Вы должны увидеть этот ответ: Дизайн DTO запроса ServiceStack

Но обычно, когда вы выполняете поиск, я использую необязательные (обнуляемые) свойства и генерирую поисковый запрос на лету.

person kampsj    schedule 13.05.2014