请检查此答案中间附加的EDIT,以获得更详细和通用的解决方案。
以下是我进行简单的基于属性的验证的解决方案。创建一个属性 -
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");
}
}
我创建了一个简单的模型来测试 -
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; }
}
然后我创建了以下视图 -
@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" />
}
现在,当我输入相同的电子邮件 - 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; }
}
假设我们的 Repository 和 UnitOfWork 以下列方式定义 -
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;
}
现在让我们创建自己的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" };
}
}
创建我们的Validator Factory,它将根据类型为我们提供特定命令。
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;
}
}
以及翻新的验证属性 -
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);
}
}
最后我们可以使用我们的属性如下 -
public class Person
{
[Required]
[MyValidate(typeof(IUniqueEmailCommand))]
public string Email { get; set; }
[Required]
public GenderType Gender { get; set; }
}
输出如下 -
编辑详细说明,使此解决方案更通用。
假设我有一个属性 Email,我需要在其中进行以下验证 -
- 格式
- 长度
- 独特
在这种情况下,我们可以创建继承自 IValidatorCommand 的 IEmailCommand。然后从IEmailCommand继承IEmailFormatCommand、IEmailLengthCommand和IEmailUniqueCommand。
我们的ValidatorFactory 将在Dictionary<Type, IValidatorCommand> Commands 中保存所有三个命令实现的池。
现在我们可以使用IEmailCommand 来装饰我们的Email 属性,而不是使用三个命令来装饰它。
在这种情况下,我们的ValidatorFactory.GetCommand() 方法需要更改。它应该返回特定类型的所有匹配命令,而不是每次都返回一个命令。所以基本上它的签名应该是List<IValidatorCommand> GetCommand(Type validatetype)。
现在我们可以获取与属性关联的所有命令,我们可以循环命令并在 ValidatorAttribute 中获取验证结果。