Mat*_*391 4 c# asp.net-core-mvc asp.net-core
我试图将控制器的ModelState注入我的服务层以添加与业务逻辑相关的验证错误(例如,条目已经存在).
为此,我创建了一个新的IValidationDictionary,它通过Initialize()函数注入我的服务.没有什么新东西可以通过我的谷歌搜索来判断一些人在MVC中所做的事情.
Controller构造函数如下所示:
public AccountController(IAccountService accountService)
{
_accountService = accountService;
_accountService.Initialize(new ValidationDictionary(ModelState));
}
Run Code Online (Sandbox Code Playgroud)
这一切都很好,我可以在我的服务中添加错误.再次离开服务时出现问题.此时,控制器ModelState中没有出现任何错误.经过一些调试后,我发现控制器构造函数中的ModelState与Action中的不同.在某些时候,它似乎创建了一个新的ModelState.
一种替代方案似乎是调用Initialize()在每个Action的开头注入ModelState.在我这样做之前,我想问一下是否有人有一种更优雅的方式(如在更少的类型中)解决这个问题.
编辑:IValidationDictionary:在商务层:
public interface IValidationDictionary
{
void AddError(string key, string message);
bool IsValid { get; }
}
Run Code Online (Sandbox Code Playgroud)
在控制器中:
public class ValidationDictionary : IValidationDictionary
{
private ModelStateDictionary _modelState;
public ValidationDictionary(ModelStateDictionary modelState)
{
_modelState = modelState;
}
public bool IsValid
{
get
{
return _modelState.IsValid;
}
}
public void AddError(string key, string message)
{
_modelState.AddModelError(key, message);
}
}
Run Code Online (Sandbox Code Playgroud)
首先,你不应该这样做,因为你将混合两件事并打破关注点的分离并紧密结合你的应用程序.
ModelStateproperty属于ModelStateDictioanryASP.NET Core特定类的类型.如果在业务层中使用它,则会创建对ASP.NET Core的依赖,从而无法在ASP.NET Core之外的任何位置重用逻辑,即后台工作进程,它是一个纯控制台应用程序,因为您既不引用ASP. NET Core也没有HttpContext或其他任何东西.
业务验证和输入验证是两个不同的事情,应该以不同的方式处理.ModelStateDictionary用于输入验证,以验证传递给控制器的输入.它并不意味着验证业务逻辑或类似的东西!
另一方面,业务验证不仅仅是对字段及其模式的粗略验证.它包含逻辑和验证可能很复杂,并且依赖于多个属性/值以及当前对象的状态.因此,例如,可能通过输入验证的值可能在业务验证中失败.
因此,通过同时使用两者,您将违反关注点的分离,并让一个班级做不止一件事.从长远来看,这对于维护代码是不利的.
IDicitionary<string, ModelStateEntry>为自定义验证模型/ValidationResultValidationResult 在System.Components.DataAnnotations`程序集中定义,它与ASP.NET Core无关但是.NET Core /完整.NET Framework的端口,因此您不会依赖ASP.NET Core并可以在控制台应用程序等,并使用工厂类在验证服务中传递它
public interface IAccoutServiceFactory
{
IAccountService Create(List<ValidationResult> validationResults);
}
// in controller
List<ValidationResult> validationResults = ConvertToValidationResults(ModelState);
IAccountService accountService = accountServiceFactory.Create(
Run Code Online (Sandbox Code Playgroud)
这解决了依赖关系的问题,但您仍然违反了关注点的分离,特别是如果您在业务层中使用与用作控制器参数相同的模型.
一开始它的工作量更多,但你的验证将是完全独立的,你可以改变其中一个而不影响另一个.
为此,您可以使用Fluent Validations之类的框架,这可以使验证更容易,更易于管理
您编写了一个自定义验证器,它将验证某个类/模型的逻辑.
自定义验证器可以像每个模型编写自己的验证器一样简单,可以实现这样的接口
public interface IValidator<T> where T : class
{
bool TryValidate(T model, out List<ValidationErrorModel> validationResults);
List<ValidationErrorModel> Validate(T model);
}
Run Code Online (Sandbox Code Playgroud)
并将它包装在验证器类周围
public class ModelValidator : IModelValidator
{
public List<ValidationErrorModel> Validate<T>(T model)
{
// provider is a IServiceProvider
var validator = provider.RequireService(typeof(IValidator<T>));
return validator.Validate(model);
}
}
Run Code Online (Sandbox Code Playgroud)
以上的替代,但使用验证属性作为基础和自定义逻辑.您可以使用Validator.TryValidateObjectfrom System.ComponentModel.DataAnnotations来验证模型ValidatorAttributes.但请注意,它只会验证传递的模型属性,而不验证子模型.
List<ValidationResult> results = new List<ValidationResult>();
var validationContext = new ValidationContext(model);
if(!Validator.TryValidateObject(model, validateContext, results))
{
// validation failed
}
Run Code Online (Sandbox Code Playgroud)
然后另外执行自定义逻辑.请参阅此博客文章,了解如何实现子模型验证.
Imho最干净的方法是自定义验证器,因为它分离和解耦,并且很容易让您更改模型的逻辑,而不影响其他模型的验证.
如果您只验证消息(即CQRS中的命令/查询),则可以使用第二种方法和验证属性.
为每个请求创建一个新的控制器。根据您将服务配置为瞬态服务还是单例服务,IAccountService每个请求都会创建一个新服务,或者重用单个实例。由于您管理每个请求的状态,因此我假设您有一个瞬态服务,即每个请求都有一个新实例。一旦请求消失,这些实例就不再被引用。
我不知道你离开服务是什么意思。我希望这提供了正确的输入来追踪您的问题。
| 归档时间: |
|
| 查看次数: |
2873 次 |
| 最近记录: |