HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Validating an entity using a dynamic list of predicates

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
predicatesvalidatingusingdynamiclistentity

Problem

I have a generic extension method for validating my entities. The main idea is to be able to specify at runtime (context related) the criteria for validating a specific entity (with the end goal of saving it in the data base).

public static Boolean IsValidEntity(this T entity, List> predicates)
{
    Boolean res = true;
    predicates.ForEach(p => res &= (Boolean)p.DynamicInvoke(entity));
    return res;
}


Inside my repository I can then use (simplified as this is not the scope but is somewhat necessary for the question context):

public void SaveIntProperty(int myProperty, List> predicates)
{
    if (myProperty.IsValidEntity(predicates))
    {
        //save to data base or other actions
    }
}


A simple example on a list of ints would be to search for elements which (in a particular context) need to be both even and less than 8.

List numbers = new List() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

//dynamically create the validation criteria
List> predicates = new List>();// List();
Predicate lessThan = x => x  even = x => x % 2 == 0;
predicates.Add(even); 

numbers.ForEach(n => SaveIntProperty(n, predicates));


The IsValidEntity extension method works and looks simple enough. However, I'm having doubts about the performance and the extensibility. Is the use of the ForEach ok? Should I pass the predicates differently? Should I use something else instead of predicates?

Solution

You can simplify your extension method a bit as so:

public static Boolean IsValidEntity(this T entity, IEnumerable> predicates)
{
    return predicates.All(p => (bool)p.DynamicInvoke(entity));
}


This is a bit better for readability, and shouldn't have any cost in efficiency as LINQ short-circuits with All. I also switched your List for IEnumerable, just to save an unnecessary ToList call if you end up with some other kind of collection.

You should also check if you really need that DynamicInvoke. If not, you can further simplify to

public static Boolean IsValidEntity(this T entity, IEnumerable> predicates)
{
    return predicates.All(p => p(entity));
}


Depending on your wider design, this might be enough. However, since you asked about extensibility, you can see that having to build predicates isn't the nicest business. For example, you had your 'less than 8' predicate. What if elsewhere in the code you wanted 'less than 7'? You'd have to repeat the (albeit very simple in this example) logic, just with a different number.

If you tried to refactor this to adhere to the Don't Repeat Yourself principle, you might then end up with something like a PredicateFactory class which exists purely to build common predicates based on parameters. But this isn't fantastic design either. It would likely end up as a monolith of public methods without much cohesion, and violate the open/closed and single responsibility principle.

A better option might be to create an interface like:

interface IEntityValidator
{
    bool IsValid(T Entity);
}


Instead of predicates, you can now create and use classes which implement this interface. This gives better adherence to the design principles I mentioned before, and allows you take full advantage of polymorphism (for more complicated validation you might find that inheritance is useful, for example). At this point your original method would be:

public static Boolean IsValidEntity(this T entity, IEnumerable> validators)
{
    return validators.All(v => v.IsValid(entity));
}


To my mind, that line is simple and readable enough that it may no longer warrant its own method, especially its own extension method which will apply to every single type. Validation doesn't seem like something that would be done in too many places, so even if you didn't like that line, you might just move it to a private method somewhere. That's more a matter of taste, though.

Code Snippets

public static Boolean IsValidEntity<T>(this T entity, IEnumerable<Predicate<T>> predicates)
{
    return predicates.All(p => (bool)p.DynamicInvoke(entity));
}
public static Boolean IsValidEntity<T>(this T entity, IEnumerable<Predicate<T>> predicates)
{
    return predicates.All(p => p(entity));
}
interface IEntityValidator<T>
{
    bool IsValid(T Entity);
}
public static Boolean IsValidEntity<T>(this T entity, IEnumerable<IEntityValidator<T>> validators)
{
    return validators.All(v => v.IsValid(entity));
}

Context

StackExchange Code Review Q#44238, answer score: 5

Revisions (0)

No revisions yet.