Как я могу избежать кастинга, но передать правильный тип - PullRequest
4 голосов
/ 25 октября 2010

Я использую 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 - это просто маркер. Я пробовал с маркером и без него, без разницы. Я также не хочу, чтобы мои модульные тесты были громоздкими, хотя я знаю, что у меня могут быть отдельные модульные тесты для проверки и для реальной сущности.

Далее я довольно ленив и не хочу писать мапперы и т. Д. Кроме того, я хотел бы иметь больше соглашений по конфигурации, и если есть валидатор, при условии, что он будет использоваться.

Я использовал дженерики во многих местах. Мне нравится функциональность, которую он дал, но я не эксперт, и в данный момент это кусает меня.

Есть ли что-нибудь вокруг этого? Способ избежать приведения объекта к объекту, чтобы среда выполнения могла выяснить, к чему его привести? Я использую инъекцию зависимостей Ninject, если это помогает

UPDATE: По запросу, конкретный валидатор

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 ]

1 голос
/ 26 октября 2010

Я предлагаю вам иметь сервисный локатор для ваших валидаторов и получать валидаторы на основе ваших типов, которые являются параметрами типов валидаторов, вы можете найти пример того, как это сделать здесь также я не понимаю, что на самом деле происходит с вашим кодом, так как работает следующий фрагмент. Поэтому, пожалуйста, опубликуйте весь отрывок со всеми используемыми классами

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 validator = validatorProvider.GetValidator (entity); var errors = validator.Validate (entity); if (errors.Count ()> 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);
}
0 голосов
/ 22 декабря 2010

Одна строка кода решит вашу проблему: динамическая сущность = entry.Entity;

0 голосов
/ 26 октября 2010

См. Ответы на этот вопрос для хорошего объяснения того, что здесь происходит.

Если вы измените ConcreteValidator на следующее, с вами все будет в порядке:

public class ConcreteValidator : IValidator<SomeObject>, IValidator<IValidatable>
0 голосов
/ 25 октября 2010

SomeObject наследуется от TObject? Тем не менее, в Java IValidator<SomeObject> - это , а не и IValidator<TObject>. Ваш код выглядит как C #, и я не знаю, как там работают дженерики.

Я думаю, вы должны добавить соответствующий языковой тег к вашему сообщению, потому что генерики определенно зависят от языка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...