tags:

views:

349

answers:

3

Here's the code:

public interface IValidator<T>
{
   bool IsValid(T obj);
}

public class OrderValidator: IValidator<Order>
{
  // ...
}

public class BaseEntity
{
}

public class Order: BaseEntity
{
}

The problem is that I can't do:

var validator = new OrderValidator();
// this line throws because type can't be converted
var baseValidator = (IValidator<BaseEntity>)validator;
// all this is because I need a list with IValidator<Order>, IValidator<BaseOrder>, etc.
IList<IValidator<BaseEntity>> allValidators = ...

How do I get and store a list of all implementations of IValidator<T> for base T - say, BaseEntity? Currently I do non-generic IValidator that accepts "object obj" but it is not good and not type-safe.

The funny stuff is that C# allows to compile:

var test = (IValidator<BaseEntity>)new OrderValidator();

but fails at runtime with

   Unable to cast object of type 'OrderValidator' to type 'IValidator`1[Orders.Core.BaseEntity]'

This is the same exception that Windsor gives me (I tried both Windsor and manual types lookup, this issue is really not related to this, only to the interfaces casting).

Thanks to Heinzi, I see now why I can't cast - because IValidator for Order expects Order as generic type. But how do I return a list of IValidator for different types? The reason is that the BaseEntity takes its real type and gathers all validators - for all types from GetType() to the object. I'd really like to have a generic GetValidators() and then operate on it.

+2  A: 

While this won't answer you directly, I'd recommend taking a look at the source code for StructureMap, they do alot of work with open generic types. Actually might even want to use StructureMap to handle caching of your validators, this is exactly what i do.

ForRequestedType(typeof (ValidationBase<>)).CacheBy(InstanceScope.Singleton);
Scan(assemblies =>
    {
        assemblies.TheCallingAssembly();
        assemblies.AddAllTypesOf(typeof(IValidation<>));
    });

Then I have a factory class to do the actual validation

public static class ValidationFactory
{
    public static Result Validate<T>(T obj)
    {
        try
        {
            var validator = ObjectFactory.GetInstance<IValidator<T>>();
            return validator.Validate(obj);
        }
        catch (Exception ex)
        {
            ...
        }
    }
}

Edit: I wrote a big blog post on generic validation using IoC, if you take a look at it since you said you already use Spring, I bet you could adapt my work to solve your problem: Creating a generic validation framework

Chris Marisic
I actually used Windsor, and manual types lookup, both fails because of this issue (because interfaces are unrelated).
queen3
I added a link to a blog post I wrote about the subject of generic validation frameworks, I think it will help you alot to solve your issue and still be able to just call Validate(myObject) and have everything resolve right.
Chris Marisic
I use Windsor, not Spring. But I'm going to take a look at the article. And frankly I suck at IoC configuration.
queen3
You article is too simplistic. What I'm implementing, I need to create IValidator<Order> for generic validation, and IValidator<SpecificOrder> for derived validation - or even _several_ ones, e.g. OrderDateValidator, OrderProductsValidator... Now, BaseEntity.Validate() will fetch _ALL_ validators for concrete type - for SpecificOrder, for Order, for BaseEntity... and execute all of them, merging results. This is to separate unrelated validation classes. Thus, simple GetInstance<IValidator<T>> won't do.
queen3
In that case my solution would still be basically the same, what you need to do is exactly what you wanted. Create each of your validators OrderDateValidator, OrderProductsValidator by just derviving each class from `IValidator<Order>` except instead of GetInstance you would use `ObjectFactory.GetAllInstances<IValidator<T>>()` which would give you all of them validators in an IList and then you could process them however you want.
Chris Marisic
Or the equivalent on Spring or whichever container you use, as far as I know all modern IOC containers should offer the GetAll construct.
Chris Marisic
After re-reading your comment what I think you're trying to do is incorrect, this goes the last line in the comment from @Heinzi explaining why you can't cast the validator the way you expect to. If you really wish to dervive all your validators from different classes of objects you might be better off just making physical class files that group all of the validators in the way you want. However IMO you would be much better off designing your framework that it can be handled by GetAllInstances.
Chris Marisic
+4  A: 

Maybe it helps you if I explain why this cast is forbidden: Assume that you have the following function

void myFunc(IValidator<BaseEntity> myValidator) {
    myValidator.IsValid(new BaseEntity());
}

This code would compile correctly. Nevertheless, if you passed an OrderValidator to this function, you would get a run-time exception, because OrderValidator.IsValid expects an Order, not a BaseEntity. Type safety would no longer be maintained if your cast were allowed.

EDIT: C# 4 allows for generic co- and contravariance, but this would not help in your case, since you use T as an input parameter. Thus, only casting to an IValidator<SomeSubtypeOfOrder> could be done in a type-safe way.

So, to be clear, you cannot cast OrderValidator to IValidator<BaseEntity> because your OrderValidator can only validate orders, not all kinds of BaseEntities. This, however, is what would be expected of an IValidator<BaseEntity>.

Heinzi
Very clear explanation, this definitely sums up why it's best to leave all this work to a IoC/DI framework.
Chris Marisic
See update for additional info.
queen3
+3  A: 

The cast doesn't work because IValidator<Order> and IValidator<BaseEntity> are totally unrelated types. IValidator<Order> is not a subtype of IValidator<BaseEntity>, so they can't be cast.

C# does support multiple interface inheritance, so the simplest way to handle this is to make your order validator inherit from an interface for both validator types, that way it you will be able to cast it to either interface as required. Obviously this means you will have to implement both interfaces and specify how to handle the base when a BaseEntity provided doesn't match the type the validator is for.

Something like this:

public class OrderValidator : IValidator<Order>, IValidator<BaseEntity>
{
    public bool IsValid(Order obj)
    {
        // do validation
        // ...
        return true;
    }

    public bool IsValid(BaseEntity obj)
    {
        Order orderToValidate = obj as Order;
        if (orderToValidate != null)
        {
            return IsValid(orderToValidate);
        }
        else
        {
            // Eiter do this:
            throw new InvalidOperationException("This is an order validator so you can't validate objects that aren't orders");
            // Or this:
            return false;
            // Depending on what it is you are trying to achive.
        }
    }
}

This relates to what Heinzi says about not being able to cast because an IValidator<BaseEntity> needs to be able to validate BaseEntities, which your current OrderValidator can't do. By adding this multiple interface you explicitly define the behaviour for validating BaseEntities (by either explicitly ignoring it or causing an exception) so the cast becomes possible.

Simon P Stevens
This seems related to my answer but my method avoids alot of the semantic restrictions and allows a generic call to ValidationFactory.Validate(myOrder)
Chris Marisic