Net Core: обернуть DTO в шаблон отклика динамическим способом - PullRequest
0 голосов
/ 23 сентября 2019

Требования к программному обеспечению требуют, чтобы все DTO содержали свой собственный класс ответа.Таким образом, разработчики в основном включают Product DTO в класс Base Response.У нас есть полный список областей классов, требующих сотен классов для Продукта, Продажи, Клиента и т. Д., Которые делают одно и то же, как описано ниже.Клиент не хочет переносить как BaseResponse<Product> or BaseResponse<IEnumerable<ProductDto>, так как он является вложенным / нечитаемым.

Есть ли метод для переноса / создания переменных классов и чтения, без ручной записи сотен классов (возможно, метод расширения, динамический класс, переменная(не уверен, открыт ли какой-либо метод)?

Примечание. Классы ответов могут быть разными, поэтому необходимо предоставить программисту возможность создавать автоматический стандартный класс или создать собственный настраиваемый ручной класс, чтобы существовали две опции.

Текущий код:

Класс DTO продукта:

public class ProductDto
{
    public int ProductId { get; set;},
    public string ProductName { get; set;},
    public string ProductDescription { get; set;},
    public float SalesAmount { get; set;}
}

BaseResponse:

public class BaseResponse<T>
{
    [Required, ValidateObject]
    public T Body { get; set; }
    public bool HasError { get; set; }
    public string Error { get; set; }
}

Индивидуальный ответ:

public class GetAllProductResponse : BaseResponse<IEnumerable<ProductDto>>
{
}

public class GetProductResponse : BaseResponse<ProductDto>
{
}

public class UpdateProductResponse : BaseResponse<ProductDto>
{
}

Предлагаемый код:

public static class ResponseExtensions
{
    public static BaseRequestResponse<T> GetAllResponse<T> (this T obj) where T:class
    {
        return BaseRequestResponse<IEnumerable<T>>;
    }

    public static BaseRequestResponse<T> GetResponse<T>(this T obj) where T : class
    {
        return BaseRequestResponse<T>;
    }

    public static BaseRequestResponse<T> UpdateResponse<T>(this T obj) where T : class
    {
        return BaseRequestResponse<T>;
    }
}

Таким образом, код теперь будет выглядеть какthis,

ProductDTO.GetAllResponse
ProductDTO.GetResponse
ProductDTO.UpdateResponse

Это хороший метод, архитектурно обоснованный или следует применить что-то еще?Это, вероятно, не будет работать, так как любой ответ отправки / получения уровня среднего уровня должен будет называться BaseResponse и т. Д.

Кстати, если идти по этому маршруту, здесь появляется ошибка компиляции

'BaseRequestResponse<T>' is a type, which is not valid in the given context 

Обновление: Так мы используем DTO и Response

public async Task<ActionResult<GetProductResponse>> GetByProduct(int id)
{
    try
    {
        var productdto = await productAppService.GetProductById(id);
        var response = new GetProductResponse { Body = productdto };
        return Ok(response);
    }
    catch (Exception ex)
    {
        logger.LogError(ex, ex.Message);
        var response = new GetDocumentResponse { HasError = true, Error = ex.Message };
        return StatusCode(StatusCodes.Status500InternalServerError, response);
    }
}

Ответы [ 2 ]

2 голосов
/ 23 сентября 2019

Если вам не нужны ваши контроллеры для упоминания этих конкретных типов, более гибким решением будет использование результирующих фильтров .Фильтры результатов запускаются после действия контроллера и позволяют преобразовывать и заменять результат, полученный waas.

Я рассмотрел реализацию этой идеи в этом связанном ответе .

Затем вы можете применить этот фильтр, например, используя атрибут [TypeFilter]:

[TypeFilter(typeof(ApiResultFilter))]
public ActionResult<ProductDto> GetProduct(int id)
{
    //
    return product;
}

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

Или примените его глобально для всех результатов действий, настроив его в Startup:

services.AddMvc(options =>
{
    options.Filters.Add(new ApiResultFilter());
});

Что касается вашей ошибки, то написанные вами расширения просто делают return SomeType.Но на самом деле вам нужно будет создать свой тип BaseResponse<T> и поместить в него объект результата, например:

public static BaseRequestResponse<T> GetResponse<T>(this T obj) where T : class
{
    return new BaseRequestResponse<T>()
    {
        Body = obj,
    };
}

Что касается опубликованного вами примера «DTO и ответ», это именно то, чтовы бы использовали фильтры результатов для.Идея состоит в том, чтобы сделать действия контроллера как можно более конкретными.Любой материал в шаблоне должен быть извлечен в фильтры, которые можно использовать повторно.Таким образом, окончательное действие может выглядеть следующим образом:

public async Task<ActionResult<Product>> GetByProduct(int id)
{
    return await productAppService.GetProductById(id);
}

Затем вы должны использовать фильтр результатов , чтобы обернуть этот продукт с вашим типом оболочки, и использовать фильтры исключений обрабатывать исключения, чтобы вместо них возвращать пользовательский объект ответа об ошибке.

1 голос
/ 23 сентября 2019

Вы должны вернуть объект типа BaseRequestResponse.

public static class ResponseExtensions
{
    public static BaseRequestResponse<T> GetAllResponse<T> (this T obj) where T:class
    {
        return //should return a object instade of type 
    }

    public static BaseRequestResponse<T> GetResponse<T>(this T obj) where T : class
    {
        return //should return a object instade of type 
    }

    public static BaseRequestResponse<T> UpdateResponse<T>(this T obj) where T : class
    {
        return //should return a object instade of type 
    }
}
...