Question

This code correctly returns one row:

_loadedAssemblies.ForEach(x =>
{
    foundTypes.AddRange(from t in x.GetTypes()
                        where t.GetInterfaces().Contains(typeof(TInterface))
                     && t.BaseType.Name.LeftOf('`') == baseClass.Name.LeftOf('`')
                        select t);
}

However, when I use PredicateBuilder, I get zero rows:

var compiledPredicate = CompiledPredicate<TInterface>();

_loadedAssemblies.ForEach(x =>
{
    foundTypes.AddRange(from t in x.GetTypes()
                        where compiledPredicate.Invoke(typeof(TInterface))
                        select t);
}

    private static Func<Type, bool> CompiledPredicate<T>() where T : class
    {
        // True means all records will be returned if no other predicates are applied.
        var predicate = PredicateBuilder.True<Type>();

        // Get types that implement the interface (T).
        predicate = predicate.And(t => t.GetInterfaces().Contains(typeof(T)));

        // If the config file includes filtering by base class, then filter by it.
        if (!string.IsNullOrWhiteSpace(_baseClass))
        {
            Type baseClass = Type.GetType(_baseClass);
            predicate = predicate.And(t => t.BaseType.Name.LeftOf('`') == baseClass.Name.LeftOf('`'));
        }

        return predicate.Compile();
}

Someone suggested creating a copy of my loop variable, but I tried that and I still get zero rows. I'm not sure why using PredicateBuilder returns no rows. Any idea what I'm missing?

Était-ce utile?

La solution

The change that you mentioned in comments (foundTypes.AddRange(x.GetTypes().AsQueryable().Where(compiledPredicate));) had nothing at all to do with the fact that you were using AsQueryable. In the first case you're passing in a hard coded type to each call of your predicate, in the second you're passing the given item from the sequence. Had you removed the AsQueryable and used Enumerable.Where it would also work, or had you passed the current item, rather than a hard coded type, when invoking it that would also work.

So you can simply do:

foundTypes.AddRange(x.GetTypes().Where(compiledPredicate));

Also, when creating the predicate, there's no need to do as much work as you're doing. Expressions take a fair amount of extra work to deal with. With linq to objects you only need to deal with delegates, which are much less finicky.

private static Func<Type, bool> CompiledPredicate<T>() where T : class
{
    Func<Type, bool> predicate = t => t.GetInterfaces().Contains(typeof(T));

    if (!string.IsNullOrWhiteSpace(_baseClass))
    {
        Type baseClass = Type.GetType(_baseClass);
        return t => predicate(t) &&
            t.BaseType.Name.LeftOf('`') == baseClass.Name.LeftOf('`');
    }

    return predicate;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top