patterncsharpMinor
IQueryable Extensions working on expression for collection property
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
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:
...can be expressed generically (well, this particular case requires that hardcoded 'string' typing, not sure if there's a way around that) as this:
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:
Which allows me to convert lines like this:
To this:
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!
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.