我正在使用 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 接口(interface)只是一个标记。我试过有无记号笔,没有区别。我也不希望我的单元测试很笨重,尽管我知道我可以为验证和实际实体进行单独的单元测试。
接下来我很懒惰,我不想编写映射器等。此外,我希望对配置有更多约定,如果有验证器,它将被使用。
我在很多地方都使用过泛型。我喜欢它提供的功能,但我不是专家,目前它正在咬我。
有没有办法解决?一种避免强制转换对象以便运行时可以确定将其转换为什么的方法?如果有帮助,我正在使用 Ninject 的依赖注入(inject)
更新:
根据要求,具体的验证器
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" };
}
}
最佳答案
我建议你为你的验证器设置服务定位器,并根据你的类型获取验证器,这些类型是验证器的类型参数,你可以找到如何做的例子 here
我也不明白您的代码到底发生了什么,因为以下代码段有效。因此,请发布您使用的所有类(class)的完整片段
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 错误 = validator.Validate(entity);
如果(错误。计数()> 0)
{
throw new Exception("验证错误");
}
}
在这里,当您调用
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);
}
关于c# - 如何避免强制转换但传入正确的类型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4014085/