Net Core: Wrap DTO in Response Pattern in Dynamic Variable Way

感情迁移 提交于 2019-12-13 07:46:53

问题


Software requirements is asking all DTOs contain their own Response Class. So developers basically wrap Product DTO in Base Response class. We have whole list of class areas, requiring hundreds of classes for Product, Sales, Customer, etc all doing same thing, per below. Client does not want to wrap as BaseResponse<Product> or BaseResponse<IEnumerable<ProductDto> since it is nested/unreadable.

Is there method to wrap/create variable classes and readable, without manually writing 100s of class (maybe extension method, dynamic class, variable, not sure, open to any method)?

Note: Response classes can be different, so want to give programmer option to create automated standard class, or create own customized manual class, so two options can exist.

Current Code:

Product DTO Class:

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; }
}

Individual Response:

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

public class GetProductResponse : BaseResponse<ProductDto>
{
}

public class UpdateProductResponse : BaseResponse<ProductDto>
{
}

Proposed Code:

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>;
    }
}

So the code will now look as this,

ProductDTO.GetAllResponse
ProductDTO.GetResponse
ProductDTO.UpdateResponse

Is this a good method, architecturally sound, or should something else be applied? This probably will not work, since any middle tier layer sending/receiving response will need refer to as BaseResponse< IEnumerable< ProductDto >, etc.

By the way,if going this route, receiving compilation error here

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

Update: This is how we use DTO and 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);
    }
}

回答1:


Unless you actually need your controllers to mention those specific types, a more flexible solution would be to use result filters. Result filters run after the controller action and allow you to transform and replace the result that waas produced.

I’ve covered the implementation of this idea in this related answer.

You can then apply that filter, e.g. using a [TypeFilter] attribute:

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

This also works as an attribute on the controller, to apply it to all actions of the decorated controller.

Or apply it globally for all action results by configuring it in your Startup:

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

Regarding your error, the extensions you wrote just do return SomeType. But you will actually have to create your BaseResponse<T> type and place the result object into it, e.g.:

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

In regards to your “DTO and response” example you posted, that’s exactly what you would use result filters for. The idea is to make controller actions as concrete as possible. Any boilerplate stuff should be extracted into filters that can be reused. So the final action may look like this:

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

You would then use a result filter to wrap that product with your wrapper type, and use exception filters to handle exceptions to return a custom error response object instead.




回答2:


You should Return Object of BaseRequestResponse type.

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 
    }
}


来源:https://stackoverflow.com/questions/58055741/net-core-wrap-dto-in-response-pattern-in-dynamic-variable-way

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!