来自数据库的MVC模型验证

PaR*_*RaJ 23 .net c# validation asp.net-mvc asp.net-mvc-4

我有一个非常简单的模型,需要从数据库中进行验证

public class UserAddress
{
    public string CityCode {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

CityCode 可以具有仅在我的数据库表中可用的值.

我知道我可以做点什么.

[HttpPost]
public ActionResult Address(UserAddress model)
{
    var connection = ; // create connection
    var cityRepository = new CityRepository(connection);

    if (!cityRepository.IsValidCityCode(model.CityCode))
    {
        // Added Model error
    }
}
Run Code Online (Sandbox Code Playgroud)

这似乎WET就像我必须在很多位置使用这个模型并添加相同的逻辑,每个地方似乎我没有正确使用MVC架构.

那么,从数据库验证模型的最佳模式是什么?

注意: 大多数验证是从数据库中进行单字段查找,其他验证可能包括字段组合.但是现在我对单字段查找验证感到满意,只要它是DRY并且没有使用过多的反射它是可以接受的.

没有客户端验证: 对于在客户端验证方面回答的任何人,我不需要任何此类验证,我的大多数验证都是服务器端的,我需要相同的,请不要回答客户端验证方法.


PS如果有人能给我提示如何从数据库进行基于属性的验证,将会非常有用.

ram*_*ilu 30


请查看本答复中间附带的编辑,以获得更详细和通用的解决方案.


以下是我做一个简单的基于属性的验证的解决方案.创建一个属性 -

public class Unique : ValidationAttribute
{
    public Type ObjectType { get; private set; }
    public Unique(Type type)
    {
        ObjectType = type;
    }
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (ObjectType == typeof(Email))
        {
            // Here goes the code for creating DbContext, For testing I created List<string>
            // DbContext db = new DbContext();
            var emails = new List<string>();
            emails.Add("ra@ra.com");
            emails.Add("ve@ve.com");

            var email = emails.FirstOrDefault(u => u.Contains(((Email)value).EmailId));

            if (String.IsNullOrEmpty(email))
                return ValidationResult.Success;
            else
                return new ValidationResult("Mail already exists");
        }

        return new ValidationResult("Generic Validation Fail");
    }
}
Run Code Online (Sandbox Code Playgroud)

我创建了一个简单的模型来测试 -

public class Person
{
    [Required]
    [Unique(typeof(Email))]
    public Email PersonEmail { get; set; }
    [Required]
    public GenderType Gender { get; set; }
}

public class Email
{
    public string EmailId { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后我创建了以下视图 -

@model WebApplication1.Controllers.Person
@using WebApplication1.Controllers;

<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

@using (Html.BeginForm("CreatePersonPost", "Sale"))
{
    @Html.EditorFor(m => m.PersonEmail)

    @Html.RadioButtonFor(m => m.Gender, GenderType.Male) @GenderType.Male.ToString()
    @Html.RadioButtonFor(m => m.Gender, GenderType.Female) @GenderType.Female.ToString()
    @Html.ValidationMessageFor(m => m.Gender)

    <input type="submit" value="click" />
}
Run Code Online (Sandbox Code Playgroud)

现在,当我输入相同的电子邮件 - ra@ra.com并单击"提交"按钮时,我的POST操作可能会出现错误,如下所示.

在此输入图像描述


编辑这里更通用和详细的答案.


创建IValidatorCommand-

public interface IValidatorCommand
{
    object Input { get; set; }
    CustomValidationResult Execute();
}

public class CustomValidationResult
{
    public bool IsValid { get; set; }
    public string ErrorMessage { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

让我们假设我们有我们RepositoryUnitOfWork中这样定义的-

public interface IRepository<TEntity> where TEntity : class
{
    List<TEntity> GetAll();
    TEntity FindById(object id);
    TEntity FindByName(object name);
}

public interface IUnitOfWork
{
    void Dispose();
    void Save();
    IRepository<TEntity> Repository<TEntity>() where TEntity : class;
} 
Run Code Online (Sandbox Code Playgroud)

现在让我们创建自己的Validator Commands-

public interface IUniqueEmailCommand : IValidatorCommand { }

public interface IEmailFormatCommand : IValidatorCommand { }

public class UniqueEmail : IUniqueEmailCommand
{
    private readonly IUnitOfWork _unitOfWork;
    public UniqueEmail(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }
    public object Input { get; set; }

    public CustomValidationResult Execute()
    {
        // Access Repository from Unit Of work here and perform your validation based on Input
        return new CustomValidationResult { IsValid = false, ErrorMessage = "Email not unique" };
    }
}

public class EmailFormat : IEmailFormatCommand
{
    private readonly IUnitOfWork _unitOfWork;
    public EmailFormat(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }
    public object Input { get; set; }

    public CustomValidationResult Execute()
    {
        // Access Repository from Unit Of work here and perform your validation based on Input
        return new CustomValidationResult { IsValid = false, ErrorMessage = "Email format not matched" };
    }
}
Run Code Online (Sandbox Code Playgroud)

创建我们的Validator Factory将根据Type为我们提供特定命令.

public interface IValidatorFactory
{
    Dictionary<Type,IValidatorCommand> Commands { get; }
}

public class ValidatorFactory : IValidatorFactory
{
    private static Dictionary<Type,IValidatorCommand> _commands = new Dictionary<Type, IValidatorCommand>();

    public ValidatorFactory() { }        

    public Dictionary<Type, IValidatorCommand> Commands
    {
        get
        {
            return _commands;
        }
    }

    private static void LoadCommand()
    {
        // Here we need to use little Dependency Injection principles and
        // populate our implementations from a XML File dynamically
        // at runtime. For demo, I am passing null in place of UnitOfWork
        _commands.Add(typeof(IUniqueEmailCommand), new UniqueEmail(null));
        _commands.Add(typeof(IEmailFormatCommand), new EmailFormat(null));
    }

    public static IValidatorCommand GetCommand(Type validatetype)
    {
        if (_commands.Count == 0)
            LoadCommand();            

        var command = _commands.FirstOrDefault(p => p.Key == validatetype);
        return command.Value ?? null;
    }
}
Run Code Online (Sandbox Code Playgroud)

并且经过翻新的验证属性 -

public class MyValidateAttribute : ValidationAttribute
{
    public Type ValidateType { get; private set; }
    private IValidatorCommand _command;
    public MyValidateAttribute(Type type)
    {
        ValidateType = type;            
    }
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        _command = ValidatorFactory.GetCommand(ValidateType);
        _command.Input = value;
        var result = _command.Execute();

        if (result.IsValid)
            return ValidationResult.Success;
        else
            return new ValidationResult(result.ErrorMessage);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后我们可以使用我们的属性如下 -

public class Person
{
    [Required]
    [MyValidate(typeof(IUniqueEmailCommand))]
    public string Email { get; set; }
    [Required]
    public GenderType Gender { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

输出如下 -

在此输入图像描述


编辑详细解释,使这个解决方案更通用.


假设我有一个属性Email,我需要做以下验证 -

  1. 格式
  2. 长度
  3. 独特

在那种情况下,我们可以创建IEmailCommand继承自IValidatorCommand.然后继承IEmailFormatCommand,IEmailLengthCommandIEmailUniqueCommandIEmailCommand.

我们ValidatorFactory将保留所有三个命令实现的池Dictionary<Type, IValidatorCommand> Commands.

现在Email我们可以用三个命令来装饰它们,而不是用三个命令来装饰它们IEmailCommand.

在这种情况下,我们的ValidatorFactory.GetCommand()方法需要改变.它不应每次返回一个命令,而应返回特定类型的所有匹配命令.所以基本上它的签名应该是List<IValidatorCommand> GetCommand(Type validatetype).

现在我们可以获取与属性关联的所有命令,我们可以循环命令并获取验证结果ValidatorAttribute.


Yog*_*ogi 5

我会用RemoteValidation。我发现这对于数据库验证等场景来说是最简单的。

用远程属性装饰您的财产 -

[Remote("IsCityCodeValid","controller")]
public string CityCode { get; set; }
Run Code Online (Sandbox Code Playgroud)

现在,“IsCityCodeValid”将是一个操作方法,它将返回 JsonResult 并采用您要验证的属性名称作为参数,“controller”是将您的方法放置在其中的控制器的名称。确保参数名称与属性名称相同。

在方法中进行验证,如果有效则返回 json true ,否则返回 false 。简单又快捷!

    public JsonResult IsCityCodeValid(string CityCode)
    {
        //Do you DB validations here
        if (!cityRepository.IsValidCityCode(cityCode))
        {
            //Invalid
           return Json(false, JsonRequestBehavior.AllowGet);
        }
        else
        {            
            //Valid
            return Json(true, JsonRequestBehavior.AllowGet);
        }
    }
Run Code Online (Sandbox Code Playgroud)

你就完成了!MVC 框架将处理剩下的事情。

当然,根据您的要求,您可以使用远程属性的不同重载。您还可以包含其他依赖属性,定义客户错误消息等。您甚至可以将模型类作为参数传递给 Json 结果操作方法 MSDN Ref。


liv*_*luc 2

我认为你应该使用自定义验证

public class UserAddress
{
    [CustomValidation(typeof(UserAddress), "ValidateCityCode")]
    public string CityCode {get;set;}
}
public static ValidationResult ValidateCityCode(string pNewName, ValidationContext pValidationContext)
{
    bool IsNotValid = true // should implement here the database validation logic
    if (IsNotValid)
        return new ValidationResult("CityCode not recognized", new List<string> { "CityCode" });
    return ValidationResult.Success;
}
Run Code Online (Sandbox Code Playgroud)