我们有很多验证方法需要访问存储库/数据库才能完成工作。到目前为止,我们一直在使用服务定位器模式(尽管很少)在自定义ValidationAttributes中实现这一点:
public override bool IsValid(object value)
{
// use custom service locator in our app's infrastructure
var repos = DependencyInjector.Current.GetService<IXyzRepository>();
...
}
我知道这是:(作为反模式,我们想使用更正确的方法。我们使用统一,我读this post that says to use a build-up method。但是the link in the accepted answer说文档已过时(退休内容)。
解决方案不需要使用验证属性,我想它可以使用IValidatableObject,但问题仍然存在:如何将依赖项注入模型。我们需要一个自定义模型绑定器吗?
另一个解决方案是在控制器中执行验证,其中依赖注入很容易。对我来说,这感觉很混乱。我希望模型在到达action方法时得到验证。
此外,我们有时会使用[RemoteAttribute]在客户端上执行某些验证。目前,这些方法通过使用静态Validator.TryValidateObject方法构造视图模型并将验证委托给模型。
如何在不使用SL反模式的情况下完成需要注入依赖项才能完成工作的验证?
答案 0 :(得分:12)
您是如何完成需要注入的验证的? 依赖于完成其工作,而不使用SL反模式?
我使用FluentValidation.NET在我的应用程序中执行验证。它允许我inject dependencies进入我的验证器。它有一个非常好的integration with ASP.NET MVC。它还支持标准规则的自动客户端验证,就像使用jquery unobtrusive验证的数据注释一样:
我从未使用过数据注释来执行验证。当您需要处理更多复杂验证方案时,它们绝对无用,您需要验证依赖属性甚至使用某些服务。我在前一句中用斜体字表示复杂,因为我不认为validating that one of the 2 properties是一个非常复杂的验证方案,但是,只需检查一下你必须编写的基础设施废弃量,以便使用它来实现它数据注释。看看这段代码,你不再知道你在验证什么。
答案 1 :(得分:1)
将验证注入模型。
当您的验证故事变得更加复杂时,验证属性可能会变得很难处理。呸!
我喜欢将Codeity框架与Code First一起使用。那时我完全控制了我的模型。我也像@Darin Dimitrov一样使用FluentValidation,我非常喜欢它的易用性和简单的语法。
这是你如何把它放在一起。 我假设你有你的接口或合同的汇编。
这将是您的模型的基础界面......
using System.ComponentModel;
using FluentValidation.Results;
public interface IAbstractBase : IDataErrorInfo
{
bool IsValid { get; }
ValidationResult SelfValidate();
}
及其业务层中的对应部分如下所示......
using System;
using System.Linq;
using FluentValidation.Results;
using Contracts;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public abstract class AbstractBase : IAbstractBase
{
#region IDataErrorInfo
public abstract ValidationResult SelfValidate();
[NotMapped]
public bool IsValid
{
get
{
return SelfValidate().IsValid;
}
}
[NotMapped]
public string Error
{
get
{
var results = SelfValidate().Errors.Select(s => string.Format("● {0}{1}", s.ErrorMessage, Environment.NewLine)).ToArray();
return string.Join("", results);
}
}
[NotMapped]
public IList<ValidationFailure> Errors
{
get
{
var results = SelfValidate().Errors;
return results;
}
}
[NotMapped]
public string this[string columnName]
{
get
{
var validationResults = SelfValidate();
if (validationResults == null) return string.Empty;
var columnResults = validationResults.Errors.FirstOrDefault(x => string.Compare(x.PropertyName, columnName, true) == 0);
return columnResults != null ? columnResults.ErrorMessage : string.Empty;
}
}
#endregion
}
这是您的模型的基类。确保在模型中实现抽象方法。它看起来应该是这样的。
public class MyModel : AbstractBase, IMyModel
{
private AbstractValidator<IMyModelValidator> _myModelValidator;
public MyModel():this(new MyModelValidator()){};
public MyModel(AbstractValidator<IMyModelValidator> myModelValidator){
_myModelValidator = myModelValidator;
};
public int MyModelId { get; set; }
public string Name { get; set; }
public DateTime CreatedDate { get; set; }
public override ValidationResult SelfValidate()
{
return _myModelValidator.Validate(this);
}
}
你的验证器类看起来像这样。
public class MyModelValidator : AbstractValidator<IMyModelValidator>
{
private IMyModelProvider _myModelProvider;
public MyModelValidator(IMyModelProvider myModelProvider){ _myModelProvider = myModelProvider;};
private void SetRules()
{
RuleFor(x => x.Name).NotEmpty().WithMessage("Please specify a project name.");
RuleFor(x => x.Name.Length).LessThanOrEqualTo(100).WithMessage("The project name must be less than or equal to 100 characters.");
}
public override ValidationResult Validate(IMyModel instance)
{
SetRules();
return base.Validate(instance);
}
}
使用控制器中的以下调用将模型中的验证结果传递到Controller中的视图。
TryValidateModel(your model here);
在控制器中调用此函数后,请调用model.IsValid属性。
确保你注册了一切,你应该好好去。我假设你可以填写缺失的部分。
大局看起来像这样: