How can I avoid casting but pass in the correct type

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

I get a list of changed and new objects by following these steps.

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 get rejected saying:

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

The only thing I can do to get rid of this error is to first direct the object to the desired type, but I do not want everything to be thrown.

Here is my interface for IValidator

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

Ok, now for some reason behind my insanity. I am trying to stick to SRP, and I don't want my objects to be able to validate themselves. Therefore, my IValidatable interface is just a marker. I tried it with a marker and without it, it does not matter. I also do not want my unit tests to be cumbersome, although I know that I can have separate unit tests for validation and for the actual object.

Next, I'm pretty lazy, and I don't want to write cartographers, etc. I would also like to have a more conditional configuration, and if there is a validator, provided that it will be used.

I have used generics in many places. I like the functionality that it gave, but I'm not an expert, and now I have bitten me.

Anyway, around? A way to avoid having to throw an object so that the runtime can figure out what this refers to? I use Ninject dependency injection if this helps

UPDATE: As requested, a specific 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" }; } } 
+4
source share
4 answers

I suggest that you have a service locator for your validators and get validators based on your types, which are validator type parameters, you can find an example of how to do this here , I donโ€™t understand what actually happens with your code, how the following works fragment. So please post all your files using all classes

 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 are facing is as follows

 var entities = (ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified)) .Select(ose => ose.Entity).OfType<IValidatable>(); 

foreach (var object in objects) {var validator = validatorProvider.GetValidator (entity); var errors = validator.Validate (entity); if (errors.Count ()> 0) {throw new Exception ("Validation Error"); }}

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

Finally, to call the appropriate method with reflection, it is better to change the 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"); } } 

latest version:

 .... 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
source

Does SomeObject from TObject ? However, in Java a IValidator<SomeObject> not an IValidator<TObject> . Your code looks like C #, although I don't know how generics work there.

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

0
source

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

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

 public class ConcreteValidator : IValidator<SomeObject>, IValidator<IValidatable> 
0
source

One line of code will solve your problem: dynamic entity = entry.Entity;

0
source

All Articles