views:

131

answers:

3

I am using Entity Framework v4. I am trying to implement some logic to Validate my entities before they are saved by overriding the SaveChanges method. I am also POCO for my entities.

I get a list of modified and new entities by doing the following.

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");
        }
}

And here is the code for my GetValidator method:

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;
}

The problem is that I am getting a failure saying:

Unable to cast object of type 'Blah.Validation.ConcreteValidator' to type 'Blah.Validation.IValidator`1[Blah.Validation.IValidatable]'

The only thing that I can do to get rid of this error is to cast the object to the right type first but I don't want to have to cast everything.

Here is my interface for IValidator

public interface IValidator<TEntity>
        where TEntity : IValidatable
{
    IEnumerable<ValidationError> Validate(TEntity entity);
}

Okay, now for some reasoning behind my insanity. I am trying to stick to SRP and I don't want my objects to be able to validate themselves. So my IValidatable interface is just a marker. I have tried it with and without the marker, it makes no difference. I also don't want my unit tests to be bulky although I know I could have separate unit tests for validation and for the actual entity.

Next I am quite lazy and I don't want to have to write mappers etc. Also I would like to have a more convention over configuration and if there is a validator provided it will be used.

I have used generics in a lot of places. I like the functionality it has given but I am not expert and it is biting me at the moment.

Is there anyway around this? A way to avoid having to cast the object so that the runtime can figure out what to cast it to? I am using Ninject's dependency injection if that helps

UPDATE: As requested, the concrete validator

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" };
    }
}
A: 

Does SomeObject inherit from TObject? Even so, in Java an IValidator<SomeObject> is not an IValidator<TObject>. Your code looks like C# though, and I don't know how generics work there.

I think you should add the appropriate language tag to your post, because generics are definitely language dependent.

Péter Török
@Peter. You are 100% correct this is C# not Java. I updated the tag as you suggested.
uriDium
+1  A: 

i suggest you to have service locator for your validators and get validators based on you types which are type parameters of validators, you can find example of how to do it here also I do not understand what's going on really with your code, as the following snippet works. So please post your whole snipped with all classes used

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());

    }
}

the problem you met is in the following

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"); } }

here when you call validatorProvider.GetValidator(entity) the entity is of type IValidator so you are calling validatorProvider.GetValidator<IValidator>(entity), however what you really wanted to do is validatorProvider.GetValidator<TEntity>(entity) you need to call generic method with reflection , dynamically specifing which method to call.

Finally to call appropriate method with reflection, you better change method declaration to GetValidator<TObject>() and do the following:

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");
        }
}

last revision:

....
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);
}
vittore
@vittore. I don't think you read my question. I have done pretty much exactly that. Except when you get something out of the entity framework it is an entity object. The entity object wraps the original object and exposes it. But I have to cast to the correct type in order for the runtime to correctly infer the correct type parameter for Validator. I don't know the type at runtime. I want it to be dynamic.
uriDium
i just realized what's going on. you call generic method in improper way.
vittore
@uriDium i updated my answer
vittore
@vittore, Hi. I am not sure if I understand your update. Also, is there meant to be more code in the code block. Maybe the upmarking isn't 100%.
uriDium
@uriDium, say you have Person class, but in foreach loop you will have it casted to IValidatable, so inside foreach loop GetValidator<IValidatable>(person is called) which returns IValidator<IValidatable> not IValidator<Person> is it clear ?
vittore
@uriDium: i updated answer one more time
vittore
A: 

See the answers to this question for a good explanation of what is happening here.

If you change ConcreteValidator to the following, you should be ok:

public class ConcreteValidator : IValidator<SomeObject>, IValidator<IValidatable>
adrift