03 March
Contains with the Entity Framework.
Sometimes you need to get a result set for a field that contains any value from an array.
The default approach in LINQ would be:
from var c in data where array.Contains(c.field) select c;
which creates a T-SQL that looks like that:
select * from data where data.field in (1,2,3,...)
Unfortunately, this would produce an error with EF, since Contains is currently not implemented with .NET 3.5SP1.
Therefore, we need a workarround that builds an expression that implements an or for each indiviudal value in the array to be compared against the field:
private void button2_Click(object sender, RoutedEventArgs e)
{
int[] types = new int[] { 1, 2, 3, 10 };
using (TomsPortal.TomsPortalEntities1 tp = new TomsPortalEntities1())
{
var r = from node in tp.Node where node.Taxonomy.Id != 3 where Contains<Node, int>(types) select node;
r = r.Where(Contains<Node, int>(node => node.Id, types));
foreach (var o in r)
{
Trace.WriteLine(o.Id);
}
}
}
static Expression<Func<TElement, bool>> Contains<TElement, TValue>(this Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
if (null == values) { throw new ArgumentNullException("values"); }
if (!values.Any()) return e => false;
var equals = from value in values
select
System.Linq.Expressions.Expression.Equal(
valueSelector.Body,
System.Linq.Expressions.Expression.Constant(value, typeof(TValue))
);
var body = equals.Aggregate((accumulate, equal) =>
System.Linq.Expressions.Expression.Or(accumulate, equal));
ParameterExpression p = valueSelector.Parameters.Single();
var ex = System.Linq.Expressions.Expression.Lambda<Func<TElement, bool>>(body, p);
return ex;
}