-1

软件要求要求所有 DTO 包含自己的响应类。因此开发人员基本上将 Product DTO 包装在 Base Response 类中。我们有完整的类领域列表,需要数百个产品、销售、客户等类都在做同样的事情,如下所示。客户端不想包装,BaseResponse<Product> or BaseResponse<IEnumerable<ProductDto>因为它是嵌套/不可读的。

是否有包装/创建变量类和可读的方法,而无需手动编写 100 个类(可能是扩展方法、动态类、变量,不确定,对任何方法开放)?

注意:响应类可以不同,所以要给程序员选择创建自动化标准类,或者创建自己定制的手动类,所以两个选项可以存在。

当前代码:

产品 DTO 类:

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

基本响应:

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

所以代码现在看起来像这样,

ProductDTO.GetAllResponse
ProductDTO.GetResponse
ProductDTO.UpdateResponse

这是一个好方法,架构合理,还是应该应用其他方法?这可能不起作用,因为任何中间层发送/接收响应都需要引用为 BaseResponse< IEnumerable< ProductDto > 等。

顺便说一句,如果走这条路,这里会收到编译错误

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

更新: 这就是我们使用 DTO 和响应的方式

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

2 回答 2

1

除非您确实需要您的控制器来提及这些特定类型,否则更灵活的解决方案是使用结果过滤器。结果过滤器在控制器操作之后运行,并允许您转换和替换产生的结果。

我已经在这个相关的答案中介绍了这个想法的实现。

然后,您可以应用该过滤器,例如使用[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);
}

然后,您将使用结果过滤器将该产品与您的包装器类型一起包装,并使用异常过滤器来处理异常以返回自定义错误响应对象。

于 2019-09-23T06:21:45.447 回答
0

您应该返回 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 
    }
}
于 2019-09-23T06:13:13.587 回答