Net Core:以动态变量方式将DTO包装在响应模式中
Net Core: Wrap DTO in Response Pattern in Dynamic Variable Way
软件要求要求所有 DTO 包含它们自己的响应 Class。所以开发人员基本上将 Product DTO 包装在 Base Response class 中。我们有完整的 class 区域列表,需要数百个 class 产品、销售、客户等都做同样的事情,如下所示。客户不想换行为 BaseResponse<Product> or BaseResponse<IEnumerable<ProductDto>
因为它是 nested/unreadable.
是否有wrap/create变量classes且可读的方法,无需手动编写100s的class(可能是扩展方法,动态class,变量,不确定, 对任何方法开放)?
注意:响应 classes 可以不同,所以想给程序员选择创建自动化标准 class,或者创建自己的自定义手册 class,所以可以存在两个选项.
当前代码:
产品 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;}
}
基础响应:
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
这是一个好的方法,架构合理,还是应该应用其他方法?
这可能行不通,因为任何中间层 sending/receiving 响应都需要引用为 BaseResponse< IEnumerable< ProductDto > 等
顺便说一下,如果走这条路,这里会收到编译错误
'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);
}
}
您应该Return 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
}
}
除非您确实需要您的控制器提及这些特定类型,否则更灵活的解决方案是使用 result filters。结果过滤器 运行 在控制器操作之后,允许您转换和替换生成的结果。
我已经在 related answer 中介绍了这个想法的实现。
然后您可以应用该过滤器,例如使用 [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);
}
然后您将使用 result filter to wrap that product with your wrapper type, and use exception filters 来处理 return 自定义错误响应对象的异常。
软件要求要求所有 DTO 包含它们自己的响应 Class。所以开发人员基本上将 Product DTO 包装在 Base Response class 中。我们有完整的 class 区域列表,需要数百个 class 产品、销售、客户等都做同样的事情,如下所示。客户不想换行为 BaseResponse<Product> or BaseResponse<IEnumerable<ProductDto>
因为它是 nested/unreadable.
是否有wrap/create变量classes且可读的方法,无需手动编写100s的class(可能是扩展方法,动态class,变量,不确定, 对任何方法开放)?
注意:响应 classes 可以不同,所以想给程序员选择创建自动化标准 class,或者创建自己的自定义手册 class,所以可以存在两个选项.
当前代码:
产品 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;}
}
基础响应:
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
这是一个好的方法,架构合理,还是应该应用其他方法? 这可能行不通,因为任何中间层 sending/receiving 响应都需要引用为 BaseResponse< IEnumerable< ProductDto > 等
顺便说一下,如果走这条路,这里会收到编译错误
'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);
}
}
您应该Return 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
}
}
除非您确实需要您的控制器提及这些特定类型,否则更灵活的解决方案是使用 result filters。结果过滤器 运行 在控制器操作之后,允许您转换和替换生成的结果。
我已经在 related answer 中介绍了这个想法的实现。
然后您可以应用该过滤器,例如使用 [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);
}
然后您将使用 result filter to wrap that product with your wrapper type, and use exception filters 来处理 return 自定义错误响应对象的异常。