我怎样才能避免施法而传递正确的类型

时间:2010-10-25 11:27:46

标签: c# oop generics entity-framework-4

我正在使用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" };
    }
}

4 个答案:

答案 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;