我正在使用Entity Framework v4。我试图通过覆盖SaveChanges方法来实现一些逻辑来验证我的实体,然后再保存它们。我也是我实体的POCO。
通过执行以下操作,我获得了已修改和新实体的列表。
var entities = (ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified))
.Select(ose => ose.Entity).OfType<IValidatable>();
foreach(var entity in entities)
{
var validator = validatorProvider.GetValidator(entity);
var errors = validator.Validate(entity);
if (errors.Count() > 0)
{
throw new Exception("A validation error");
}
}
这是我的GetValidator方法的代码:
private static IValidator<TObject> GetValidator<TObject>(TObject obj) where TObject : IValidatable
{
var objType = obj.GetType();
var validatorType = Type.GetType("SmsPortal.Validation." + objType.Name + "Validator");
var varValidator = Activator.CreateInstance(validatorType);
return (IValidator<TObject>)varValidator;
}
问题是我失败了:
Unable to cast object of type 'Blah.Validation.ConcreteValidator' to type 'Blah.Validation.IValidator`1[Blah.Validation.IValidatable]'
我唯一可以做的就是摆脱这个错误,首先将对象转换为正确的类型,但我不想抛出所有内容。
这是我的IValidator界面
public interface IValidator<TEntity>
where TEntity : IValidatable
{
IEnumerable<ValidationError> Validate(TEntity entity);
}
好的,现在我的疯狂背后有一些推理。我试图坚持SRP,我不希望我的对象能够验证自己。所以我的IValidatable界面只是一个标记。我有没有标记尝试过,没有区别。我也不希望我的单元测试体积庞大,但我知道我可以为验证和实际实体进行单独的单元测试。
接下来我很懒,而且我不想写mappers等。另外我想对配置有更多的约定,如果有一个验证器提供它将被使用。
我在很多地方都使用过泛型。我喜欢它给出的功能,但我不是专家,而且现在正在咬我。
这周围有吗?一种避免必须转换对象的方法,以便运行时可以找出将其转换为什么?如果有帮助的话,我正在使用Ninject的依赖注入
更新 根据要求,具体验证器
public class ConcreteValidator : IValidator<SomeObject>
{
public IEnumerable<ValidationError> Validate(SomeObject entity)
{
if (string.IsNullOrEmpty(entity.Name))
yield return new ValidationError { Message = "Name is mandatory", Property = "Name" };
if (entity.Name != null && entity.Name.Length > 50)
yield return new ValidationError { Message = "Name must be less than 50 characters", Property = "Name" };
}
}
答案 0 :(得分:1)
我建议您为验证人员提供服务定位器,并根据您的验证员类型参数类型获取验证器,您可以找到如何执行验证的示例here 我也不明白你的代码究竟发生了什么,因为下面的代码段可行。所以请用你使用的所有课程发布整个剪辑
public interface IValidator<T> where T: IObj
{
}
public class PersonValidator : IValidator<Person>
{
}
public static class Validators
{
public static IValidator<TObject> GetValidator<TObject>(TObject obj)
where TObject : IObj
{
var t = obj.GetType();
var name = string.Format("{0}.{1}Validator", t.Namespace, t.Name);
return (IValidator<TObject>)
Activator.CreateInstance(Type.GetType(name));
}
}
[TestFixture]
public class ValidatorsTest
{
[Test]
public void TestPersonValidator()
{
var pValidator = Validators.GetValidator(new Person());
}
}
您遇到的问题在以下
中var entities = (ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified))
.Select(ose => ose.Entity).OfType<IValidatable>();
foreach(实体中的var实体) { var validator = validatorProvider.GetValidator(entity); var errors = validator.Validate(entity); if(errors.Count()&gt; 0) { 抛出新的异常(“验证错误”); } }
此处当您致电validatorProvider.GetValidator(entity)
时,该实体的类型为IValidator
,因此您正在呼叫validatorProvider.GetValidator<IValidator>(entity)
,但您真正想做的是validatorProvider.GetValidator<TEntity>(entity)
你需要用反射调用泛型方法,动态指定要调用的方法。
最后,要使用反射调用适当的方法,最好将方法声明更改为GetValidator<TObject>()
并执行以下操作:
foreach(var entity in entities)
{
var validator = validatorProvider.GetType().GetMethod("GetValidator").MakeGenericMethod(entity.GetType()).Invoke(validatorProvider, null );
var errors = validator.Validate(entity);
if (errors.Count() > 0)
{
throw new Exception("A validation error");
}
}
最后修订版:
....
foreach(var entity in entities)
{
GetType()
.GetMethod("ValidateObj")
.MakeGenericMethod(entity.GetType())
.Invoke(this, null );
}
....
....
public void ValidateObj<TEntity>(TEntity obj) where TEntity : IValidatable {
var errors = validatorProvider.GetValidator<TEntity>().Validate(obj);
if (errors.Count() > 0 ) throw new ValidationException(obj, errors);
}
答案 1 :(得分:0)
SomeObject
是否继承自TObject
?即便如此,在Java中,IValidator<SomeObject>
不和IValidator<TObject>
。你的代码看起来像C#,我不知道泛型如何在那里工作。
我认为您应该在帖子中添加适当的语言标记,因为泛型绝对是语言依赖的。
答案 2 :(得分:0)
请参阅this question的答案,以便更好地解释此处发生的事情。
如果您将ConcreteValidator
更改为以下内容,则应该没问题:
public class ConcreteValidator : IValidator<SomeObject>, IValidator<IValidatable>
答案 3 :(得分:0)
一行代码可以解决您的问题: dynamic entity = entry.Entity;