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

IQueryable Extensions working on expression for collection property

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

Problem

Followup to Repository searching code duplication

My goal currently is to make the current implementation of my repository's IQueryable filtering less duplicative. First, I looked into passing expressions around to target properties generically so that I can create reusable methods in a new extensions class I created:

```
internal static class IQueryableExtensions
{
internal static IQueryable FilterByAtLeast(this IQueryable query, TPropertyType value, Expression> property)
where TPropertyType : IComparable
{
return query.Where(x => value == null || property.Compile().Invoke(x).CompareTo(value) >= 0);
}

internal static IQueryable FilterByAtMost(this IQueryable query, TPropertyType value, Expression> property)
where TPropertyType : IComparable
{
return query.Where(x => value == null || property.Compile().Invoke(x).CompareTo(value) FilterByContains(this IQueryable query, string value, Expression> property)
{
return query.Where(x => value == null || property.Compile().Invoke(x).Contains(value));
}

internal static IQueryable FilterByExact(this IQueryable query, TPropertyType value, Expression> property)
where TPropertyType : IComparable
{
return query.Where(x => property.Compile().Invoke(x).Equals(value));
}

internal static IQueryable FilterByGreaterThan(this IQueryable query, TPropertyType value, Expression> property)
where TPropertyType : IComparable
{
return query.Where(x => value == null || property.Compile().Invoke(x).CompareTo(value) > 0);
}

internal static IQueryable FilterByGuids(this IQueryable query, Filter filter)
where TModel : PocoBase
{
return query.Where(x => !filter.ItemGuids.Any() || filter.ItemGuids.Contains(x.Id));
}

internal static IQueryable FilterByLessThan(this IQueryable query, TPropertyType value, Expression> property)
where TPropertyType : IComparable
{
return quer

Solution

I have worked out the typings and expressions required to extract the common logic that was being duplicated in numerous places. Lines like this:

return query.Where(x => x.Clients.Any(y => y.Account.Notes.Contains(filter.Notes);


...can be expressed generically (well, this particular case requires that hardcoded 'string' typing, not sure if there's a way around that) as this:

internal static IQueryable FilterByAnyContaining(this IQueryable query, string value, Expression>> collection, Expression> property)
{
    return query.Where(x => value == null || collection.Compile().Invoke(x).Any(y => property.Compile().Invoke(y).Contains(value)));
}


With that worked out finally, I think the only other thing I need is a somewhat similar method that works with a collection of items and a single value to find:

internal static IQueryable FilterByAnyContaining(this IQueryable query, ICollection collection, Expression> property)
{
    return query.Where(x => !collection.Any() || !collection.Contains(property.Compile().Invoke(x)));
}


Which allows me to convert lines like this:

query = query.Where(x => !filter.ItemGuids.Any() || filter.ItemGuids.Contains(x.Id));


To this:

query = query.FilterByAnyContaining(filter.ItemGuids, x => x.Id);


Everything else I see currently I think I can apply the Rule of Three to and just leave it be a single use case.

This is certainly a case where coming back to the problem a day later helped make wrapping my mind around the problem I faced easier. Hopefully this helps somebody else!

Code Snippets

return query.Where(x => x.Clients.Any(y => y.Account.Notes.Contains(filter.Notes);
internal static IQueryable<TContainerModel> FilterByAnyContaining<TContainerModel, TCollectionModel>(this IQueryable<TContainerModel> query, string value, Expression<Func<TContainerModel, ICollection<TCollectionModel>>> collection, Expression<Func<TCollectionModel, string>> property)
{
    return query.Where(x => value == null || collection.Compile().Invoke(x).Any(y => property.Compile().Invoke(y).Contains(value)));
}
internal static IQueryable<TContainerModel> FilterByAnyContaining<TContainerModel, TPropertyType>(this IQueryable<TContainerModel> query, ICollection<TPropertyType> collection, Expression<Func<TContainerModel, TPropertyType>> property)
{
    return query.Where(x => !collection.Any() || !collection.Contains(property.Compile().Invoke(x)));
}
query = query.Where(x => !filter.ItemGuids.Any() || filter.ItemGuids.Contains(x.Id));
query = query.FilterByAnyContaining(filter.ItemGuids, x => x.Id);

Context

StackExchange Code Review Q#146625, answer score: 2

Revisions (0)

No revisions yet.