Transform Expression m => m.Name to m => m[index].Name - asp.net-mvc-3

Given an Expression<Func<T, TValue>> (like m => m.Name) and an index, I'd like to be able to transform my expression to m => m[index].Name). And I must admit I'm stuck...
I give you the actual scenario if you want the "Why the hell" (and maybe find a better way).
Scenario : imagine a Server Side Editable Grid (without javascript).
I build my grid with an helper which look like that :
#(Html.Grid(Model)
.Columns(columns => {
columns.Edit(m => m.Name);
columns.Edit(m => m.Code);
})
.AsEditable());
Model is an IQueryable<T>
m => m.Name is an Expression<Func<T, TValue>> (TValue is string)
m => m.Code is an Expression<Func<T, TValue>> (TValue is int)
When rendering my view, I'd like to display an html form.
The IQueryable<T> is enumerated (order, pagination). => ok
So I'll have a List<T> of 5, 10 or 20 T items.
And Name and Code should be represented as TextBox, using a classic HtmlHelper.TextBoxFor(Expression<Func<T, TValue>>) (no problem to create the HtmlHelper<T>)
But as I've got a list, if I want correct Model binding, I can't use directly m => m.Name, but should use m => m[indexOfItem in List<T>].Name
Edit : more details :
So let's say we have an entity class
public class Test {
public int Id {get;set;}
public string Name {get;set;}
public string Code {get;set;}
}
Then, a method retrieving an IQueryable<Test>
Then a view
#model IQueryable<Test>
#(Html.Grid(Model)
.Columns(columns => {
columns.Edit(m => m.Name);
columns.Edit(m => m.Code);
})
.AsEditable());
as you see, Model given as parameter is IQueryable<Test>.
m => m.Name
and
m => m.Code
are just properties of the Model (which I wanna display as TextBox in my grid).
The model is an IQueryable<T> (not an IEnumerable<T>), because the Grid manages ordering and Pagination, so that my controller and service layer don't need to know about pagination and ordering.
Is it clearer ?

This could be easily achieved by writing a custom ExpressionVisitor:
public static class ExpressionExtensions
{
private class Visitor : ExpressionVisitor
{
private readonly int _index;
public Visitor(int index)
{
_index = index;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return Expression.ArrayIndex(GetArrayParameter(node), Expression.Constant(_index));
}
public ParameterExpression GetArrayParameter(ParameterExpression parameter)
{
var arrayType = parameter.Type.MakeArrayType();
return Expression.Parameter(arrayType, parameter.Name);
}
}
public static Expression<Func<T[], TValue>> BuildArrayFromExpression<T, TValue>(
this Expression<Func<T, TValue>> expression,
int index
)
{
var visitor = new Visitor(index);
var nexExpression = visitor.Visit(expression.Body);
var parameter = visitor.GetArrayParameter(expression.Parameters.Single());
return Expression.Lambda<Func<T[], TValue>>(nexExpression, parameter);
}
}
and then you could use this extension method like this:
Expression<Func<Test, string>> ex = m => m.Code;
Expression<Func<Test[], string>> newEx = ex.BuildArrayFromExpression(1);

Well, got something (ugly, just for testing purpose) working, but it makes things unclear, so I'll wait for a better solution or build my input another way :
public static Expression<Func<T[], TValue>> BuildArrayFromExpression<T, TValue>(this Expression<Func<T, TValue>> expression, int index)
{
var parameter = Expression.Parameter(typeof(T[]), "m");
Expression body = Expression.ArrayIndex(parameter, Expression.Constant(index));
var type = typeof(T);
var properties = expression.Body.ToString().Split('.');//ugly shortcut for test only
foreach (var property in properties.Skip(1))
{
var pi = type.GetProperty(property);
body = Expression.Property(body, type.GetProperty(property));
type = pi.PropertyType;
}
return Expression.Lambda<Func<T[], TValue>>(body, parameter);
}
used in a RenderMethod, called for each line of my List<T> (the paginated /ordered list returned from my IQueryable<T>)
public override ... RenderContent(T dataItem) {
var helper = new HtmlHelper<T[]>(GridModel.Context, new GridViewDataContainer<T[]>(GridModel.PaginatedItems.ToArray(), GridModel.Context.ViewData));
var modifiedExpression = Expression.BuildArrayFromExpression(GridModel.PaginatedItems.IndexOf(dataItem));//GridModel.PaginatedItems is the List<T> returned when "executing" the IQueryable<T>
var textBox = helper.TextBoxFor(modifiedExpression);
...
}

Related

How to select multiple class properties in LINQ Expression?

If I have a class like this
`
class Person
{
public string First;
public string Last;
public bool IsMarried;
public int Age;
}`
Then how can I write a LINQ Expression where I could select properties of a Person. I want to do something like this (user can enter 1..n properties)
SelectData<Person>(x=>x.First, x.Last,x.Age);
What would be the input expression of my SelectData function ?
SelectData(Expression<Func<TEntity, List<string>>> selector); ?
EDIT
In my SelectData function I want to extract property names and then generate SELECT clause of my SQL Query dynamically.
SOLUTION
Ok, so what I have done is to have my SelectData as
public IEnumerable<TEntity> SelectData(Expression<Func<TEntity, object>> expression)
{
NewExpression body = (NewExpression)expression.Body;
List<string> columns = new List<string>();
foreach(var arg in body.Arguments)
{
var exp = (MemberExpression)arg;
columns.Add(exp.Member.Name);
}
//build query
And to use it I call it like this
ccc<Person>().SelectData(x => new { x.First, x.Last, x.Age });
Hopefully it would help someone who is looking :)
Thanks,
IY
I think it would be better to use delegates instead of Reflection. Apart from the fact that delegates will be faster, the compiler will complain if you try to fetch property values that do not exist. With reflection you won't find errors until run time.
Luckily there is already something like that. it is implemented as an extension function of IEnumerable, and it is called Select (irony intended)
I think you want something like this:
I have a sequence of Persons, and I want you to create a Linq
statement that returns per Person a new object that contains the
properties First and Last.
Or:
I have a sequence of Persns and I want you to create a Linq statement
that returns per Person a new object that contains Age, IsMarried,
whether it is an adult and to make it difficult: one Property called
Name which is a combination of First and Last
The function SelectData would be something like this:
IEnumerable<TResult> SelectData<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
{
return source.Select(selector);
}
Usage:
problem 1: return per Person a new object that contains the
properties First and Last.
var result = Persons.SelectData(person => new
{
First = person.First,
Last = person.Last,
});
problem 2: return per Person a new object that contains Age, IsMarried, whether he is an adult and one Property called Name which is a combination
of First and Last
var result = Persons.SelectData(person => new
{
Age = person.Name,
IsMarried = person.IsMarried,
IsAdult = person.Age > 21,
Name = new
{
First = person.First,
Last = person.Last,
},
});
Well let's face it, your SelectData is nothing more than Enumerable.Select
You could of course create a function where you'd let the caller provide a list of properties he wants, but (1) that would limit his possibilities to design the end result and (2) it would be way more typing for him to call the function.
Instead of:
.Select(p => new
{
P1 = p.Property1,
P2 = p.Property2,
}
he would have to type something like
.SelectData(new List<Func<TSource, TResult>()
{
p => p.Property1, // first element of the property list
p -> p.Property2, // second element of the property list
}
You won't be able to name the returned properties, you won't be able to combine several properties into one:
.Select(p => p.First + p.Last)
And what would you gain by it?
Highly discouraged requirement!
You could achive similar result using Reflection and Extension Method
Model:
namespace ConsoleApplication2
{
class Person
{
public string First { get; set; }
public string Last { get; set; }
public bool IsMarried { get; set; }
public int Age { get; set; }
}
}
Service:
using System.Collections.Generic;
using System.Linq;
namespace Test
{
public static class Service
{
public static IQueryable<IQueryable<KeyValuePair<string, object>>> SelectData<T>(this IQueryable<T> queryable, string[] properties)
{
var queryResult = new List<IQueryable<KeyValuePair<string, object>>>();
foreach (T entity in queryable)
{
var entityProperties = new List<KeyValuePair<string, object>>();
foreach (string property in properties)
{
var value = typeof(T).GetProperty(property).GetValue(entity);
var entityProperty = new KeyValuePair<string, object>(property, value);
entityProperties.Add(entityProperty);
}
queryResult.Add(entityProperties.AsQueryable());
}
return queryResult.AsQueryable();
}
}
}
Usage:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
class Program
{
static void Main(string[] args)
{
var list = new List<Person>()
{
new Person()
{
Age = 18,
First = "test1",
IsMarried = false,
Last = "test2"
},
new Person()
{
Age = 40,
First = "test3",
IsMarried = true,
Last = "test4"
}
};
var queryableList = list.AsQueryable();
string[] properties = { "Age", "Last" };
var result = queryableList.SelectData(properties);
foreach (var element in result)
{
foreach (var property in element)
{
Console.WriteLine($"{property.Key}: {property.Value}");
}
}
Console.ReadKey();
}
}
}
Result:
Age: 18
Last: test2
Age: 40
Last: test4

How to map an int to its enum description using AutoMapper during a queryable projection?

Here is the enum extension method to get its description attribute.
public static string GetDescription(this Enum enumeration)
{
if (enumeration == null)
throw new ArgumentNullException();
var value = enumeration.ToString();
var type = enumeration.GetType();
var descriptionAttribute =
(DescriptionAttribute[]) type.GetField(value).GetCustomAttributes(typeof (DescriptionAttribute), false);
return descriptionAttribute.Length > 0 ? descriptionAttribute[0].Description : value;
}
Here is the source object:
public class Account {
public int AccountId {get;set;}
public int AccountStatusId {get;set;}
}
Here is the enum:
public enum AccountStatus {
[Description("N/A")]
None,
[Description("OPEN")]
Open,
[Description("CLOSED")]
Closed,
[Description("BAD CREDIT")
Problem
}
Here is the destination object:
public class GetAccountResponse {
public int AccountId {get;set;}
public string Status {get;set;}
}
Here is my attempt to map (using the latest non-static automapper version). Remember this is during an EF queryable projection.
_config = new MapperConfiguration(cfg => cfg.CreateMap<Account, GetAccountsResponse>()
.ForMember(dest => dest.Status,
opts => opts.MapFrom(src => ((AccountStatus) src.AccountStatusId).GetDescription())));
Here is the projection where query is an IQueryable<Account>:
query.ProjectToList<GetAccountResponse>(_config);
This is the exception I get:
Can't resolve this to Queryable Expression
If you check out the signature of the MapFrom method, you'll notice that one of the overloads takes a parameter of type Expression<Func<TSource, TMember>>.
This suggests that you could write a method which builds an expression tree from ternary expressions that can convert any possible value of your enum to its appropriate string. AutoMapper would then convert this into the appropriate SQL expression via LINQ.
Here's an example which just uses the Enum names themselves: you should be able to adapt it straightforwardly to use your Descriptions:
public static class EnumerableExpressionHelper
{
public static Expression<Func<TSource, String>> CreateEnumToStringExpression<TSource, TMember>(
Expression<Func<TSource, TMember>> memberAccess, string defaultValue = "")
{
var type = typeof(TMember);
if (!type.IsEnum)
{
throw new InvalidOperationException("TMember must be an Enum type");
}
var enumNames = Enum.GetNames(type);
var enumValues = (TMember[])Enum.GetValues(type);
var inner = (Expression)Expression.Constant(defaultValue);
var parameter = memberAccess.Parameters[0];
for (int i = 0; i < enumValues.Length; i++)
{
inner = Expression.Condition(
Expression.Equal(memberAccess.Body, Expression.Constant(enumValues[i])),
Expression.Constant(enumNames[i]),
inner);
}
var expression = Expression.Lambda<Func<TSource,String>>(inner, parameter);
return expression;
}
}
You would use it as follows:
CreateMap<Entry, EntryListItem>()
.ForMember(e => e.ReviewStatus,
c => c.MapFrom(EnumerableExpressionHelper.CreateEnumToStringExpression((Entry e) => e.ReviewStatus)))

How can I transform this linq expression?

Say I have an entity that I want to query with ranking applied:
public class Person: Entity
{
public int Id { get; protected set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
In my query I have the following:
Expression<Func<Person, object>> orderBy = x => x.Name;
var dbContext = new MyDbContext();
var keyword = "term";
var startsWithResults = dbContext.People
.Where(x => x.Name.StartsWith(keyword))
.Select(x => new {
Rank = 1,
Entity = x,
});
var containsResults = dbContext.People
.Where(x => !startsWithResults.Select(y => y.Entity.Id).Contains(x.Id))
.Where(x => x.Name.Contains(keyword))
.Select(x => new {
Rank = 2,
Entity = x,
});
var rankedResults = startsWithResults.Concat(containsResults)
.OrderBy(x => x.Rank);
// TODO: apply thenby ordering here based on the orderBy expression above
dbContext.Dispose();
I have tried ordering the results before selecting the anonymous object with the Rank property, but the ordering ends up getting lost. It seems that linq to entities discards the ordering of the separate sets and converts back to natural ordering during both Concat and Union.
What I think I may be able to do is dynamically transform the expression defined in the orderBy variable from x => x.Name to x => x.Entity.Name, but I'm not sure how:
if (orderBy != null)
{
var transformedExpression = ???
rankedResults = rankedResults.ThenBy(transformedExpression);
}
How might I be able to use Expression.Lambda to wrap x => x.Name into x => x.Entity.Name? When I hard code x => x.Entity.Name into the ThenBy I get the ordering that I want, but the orderBy is provided by the calling class of the query, so I don't want to hard-code it in. I have it hardcoded in the example above for simplicity of explanation only.
This should help. However you are going to have to concrete up the Anonymous type for this to work. My LinqPropertyChain will not work with it, since its going to be difficult to create the Expression<Func<Anonymous, Person>> whilst its still Anonymous.
Expression<Func<Person, object>> orderBy = x => x.Name;
using(var dbContext = new MyDbContext())
{
var keyword = "term";
var startsWithResults = dbContext.People
.Where(x => x.Name.StartsWith(keyword))
.Select(x => new {
Rank = 1,
Entity = x,
});
var containsResults = dbContext.People
.Where(x => !startsWithResults.Select(y => y.Entity.Id).Contains(x.Id))
.Where(x => x.Name.Contains(keyword))
.Select(x => new {
Rank = 2,
Entity = x,
});
var rankedResults = startsWithResults.Concat(containsResults)
.OrderBy(x => x.Rank)
.ThenBy(LinqPropertyChain.Chain(x => x.Entity, orderBy));
// TODO: apply thenby ordering here based on the orderBy expression above
}
public static class LinqPropertyChain
{
public static Expression<Func<TInput, TOutput>> Chain<TInput, TOutput, TIntermediate>(
Expression<Func<TInput, TIntermediate>> outter,
Expression<Func<TIntermediate, TOutput>> inner
)
{
Console.WriteLine(inner);
Console.WriteLine(outter);
var visitor = new Visitor(new Dictionary<ParameterExpression, Expression>
{
{inner.Parameters[0], outter.Body}
});
var newBody = visitor.Visit(inner.Body);
Console.WriteLine(newBody);
return Expression.Lambda<Func<TInput, TOutput>>(newBody, outter.Parameters);
}
private class Visitor : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> _replacement;
public Visitor(Dictionary<ParameterExpression, Expression> replacement)
{
_replacement = replacement;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (_replacement.ContainsKey(node))
return _replacement[node];
else
{
return node;
}
}
}
}
Figured out a way to do this with less Explicite Generics.
Expression<Func<Person, object>> orderBy = x => x.Name;
Expression<Func<Foo, Person>> personExpression = x => x.Person;
var helper = new ExpressionChain(personExpression);
var chained = helper.Chain(orderBy).Expression;
// Define other methods and classes here
public class ExpressionChain<TInput, TOutput>
{
private readonly Expression<Func<TInput, TOutput>> _expression;
public ExpressionChain(Expression<Func<TInput, TOutput>> expression)
{
_expression = expression;
}
public Expression<Func<TInput, TOutput>> Expression { get { return _expression; } }
public ExpressionChain<TInput, TChained> Chain<TChained>
(Expression<Func<TOutput, TChained>> chainedExpression)
{
var visitor = new Visitor(new Dictionary<ParameterExpression, Expression>
{
{_expression.Parameters[0], chainedExpression.Body}
});
var lambda = Expression.Lambda<Func<TInput, TOutput>>(newBody, outter.Parameters);
return new ExpressionChain(lambda);
}
private class Visitor : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> _replacement;
public Visitor(Dictionary<ParameterExpression, Expression> replacement)
{
_replacement = replacement;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (_replacement.ContainsKey(node))
return _replacement[node];
else
{
return node;
}
}
}
}
Since you're ordering by Rank first, and the Rank values are identical within each sequence, you should be able to just sort independently and then concatenate. It sounds like the hiccup here would be that, according to your post, Entity Framework isn't maintaining sorting across Concat or Union operations. You should be able to get around this by forcing the concatenation to happen client-side:
var rankedResults = startsWithResults.OrderBy(orderBy)
.AsEnumerable()
.Concat(containsResults.OrderBy(orderBy));
This should render the Rank property unnecessary and probably simplify the SQL queries being executed against your database, and it doesn't require mucking about with expression trees.
The downside is that, once you call AsEnumerable(), you no longer have the option of appending additional database-side operations (i.e., if you chain additional LINQ operators after Concat, they will use the LINQ-to-collections implementations). Looking at your code, I don't think this would be a problem for you, but it's worth mentioning.

Change a LINQ expression predicate from one type to another

I have two unrelated classes. One is exposed as API, and the other is used internally by 3rd party API.
Entity is exposed from our API, while EntityProvider is from the 3rd party assembly.
class Entity
{
public A { get; set; }
}
class EntityProvider
{
public A { get; set; }
}
Consumers of our API will provide predicates of the form Expression <Func<Entity, bool>> and I need to modify it to Expression <Func<EntityProvider, bool>> so that I can pass the same to internal 3rd party assembly.
Please help with this conversion.
Since Expressions in .NET are immutable, the only way to do this is to rebuild the whole expression. To do this usually involves inheriting from the ExpressionVisitor class. Depending on the complexity of the expressions you have to convert this could be quite complicated.
This is a simple example of a visitor that will work with simple expressions( like x=>x.Someproperty == somevalue ). It's just an example to get you started and it's in no way finished or tested(it won't handle method calls in the expression for example)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
//Type from which to convert
public class A
{
public int Property1 { get; set; }
public int Property2 { get; set; }
}
//Type to which we want the Expression converted
public class B
{
public int Property1 { get; set; }
public int Property2 { get; set; }
}
class Program
{
static void Main(string[] args)
{
//the expression we want to convert expresion
Expression<Func<A, bool>> expA = x => x.Property1 == 6 && x.Property2 == 3;
var visitor = new ParameterTypeVisitor<A,B>(expA);
var expB = visitor.Convert();
var b = new B() { Property1 = 6, Property2 = 3 };
//try the converted expression
var result = expB.Compile().Invoke(b);
}
}
public class ParameterTypeVisitor<TFrom,TTo> : ExpressionVisitor
{
private Dictionary<string, ParameterExpression> convertedParameters;
private Expression<Func<TFrom, bool>> expression;
public ParameterTypeVisitor(Expression<Func<TFrom,bool>> expresionToConvert )
{
//for each parameter in the original expression creates a new parameter with the same name but with changed type
convertedParameters = expresionToConvert.Parameters
.ToDictionary(
x => x.Name,
x => Expression.Parameter(typeof (TTo), x.Name)
);
expression = expresionToConvert;
}
public Expression<Func<TTo,bool>> Convert()
{
return (Expression<Func<TTo, bool>>)Visit(expression);
}
//handles Properties and Fields accessors
protected override Expression VisitMember(MemberExpression node)
{
//we want to replace only the nodes of type TFrom
//so we can handle expressions of the form x=> x.Property.SubProperty
//in the expression x=> x.Property1 == 6 && x.Property2 == 3
//this replaces ^^^^^^^^^^^ ^^^^^^^^^^^
if (node.Member.DeclaringType == typeof(TFrom))
{
//gets the memberinfo from type TTo that matches the member of type TFrom
var memeberInfo = typeof (TTo).GetMember(node.Member.Name).First();
//this will actually call the VisitParameter method in this class
var newExp = Visit(node.Expression);
return Expression.MakeMemberAccess(newExp, memeberInfo);
}
else
{
return base.VisitMember(node);
}
}
// this will be called where ever we have a reference to a parameter in the expression
// for ex. in the expression x=> x.Property1 == 6 && x.Property2 == 3
// this will be called twice ^ ^
protected override Expression VisitParameter(ParameterExpression node)
{
var newParameter = convertedParameters[node.Name];
return newParameter;
}
//this will be the first Visit method to be called
//since we're converting LamdaExpressions
protected override Expression VisitLambda<T>(Expression<T> node)
{
//visit the body of the lambda, this will Traverse the ExpressionTree
//and recursively replace parts of the expression we for which we have matching Visit methods
var newExp = Visit(node.Body);
//this will create the new expression
return Expression.Lambda(newExp,convertedParameters.Select(x=>x.Value));
}
}

How to do a "where in values" in LINQ-to-Entities 3.5

Does anybody know how to apply a "where in values" type condition using LINQ-to-Entities? I've tried the following but it doesn't work:
var values = new[] { "String1", "String2" }; // some string values
var foo = model.entitySet.Where(e => values.Contains(e.Name));
I believe this works in LINQ-to-SQL though? Any thoughts?
Update: found out how to do this. And EF will generate the appropriate SQL on the database. I'm not sure if this is for EF4 only but I got the tip from Entity Framework 4.0 Recipes
var listOfIds=GetAListOfIds();
var context=CreateEntityFrameworkObjectContext();
var results = from item in context.Items
where listOfIds.Contains(item.Category.Id)
select item;
//results contains the items with matching category Ids
This query generates the correct in clause on the server side. I haven't tested it with EF 3.5 but it does work with EF4.
NB: The values passed into the in clause are NOT parameters so make sure you validate your inputs.
It is somewhat of a shame that Contains is not supported in Linq to Entities.
IN and JOIN are not the same operator (Filtering by IN never changes the cardinality of the query).
Contains is not supported in EF at this time.
FYI:
If you are using ESql you are able to use in operation.
I don't have VS 2008 With me but code should be something like following:
var ids = "12, 34, 35";
using (context = new Entites())
{
var selectedProducts = context.CreateQuery<Products>(
String.Format("select value p from [Entities].Products as p
where p.productId in {{{0}}}", ids)).ToList();
...
}
For the cases when you want to use expressions when querying your data, you can use the following extension method (adapted after http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/095745fe-dcf0-4142-b684-b7e4a1ab59f0/):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Data.Objects;
namespace Sample {
public static class Extensions {
public static IQueryable<T> ExtWhereIn<T, TValue>(this ObjectQuery<T> query,
Expression<Func<T, TValue>> valueSelector,
IEnumerable<TValue> values) {
return query.Where(BuildContainsExpression<T, TValue>(valueSelector, values));
}
public static IQueryable<T> ExtWhereIn<T, TValue>(this IQueryable<T> query,
Expression<Func<T, TValue>> valueSelector,
IEnumerable<TValue> values) {
return query.Where(BuildContainsExpression<T, TValue>(valueSelector, values));
}
private static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values) {
if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
if (null == values) { throw new ArgumentNullException("values"); }
ParameterExpression p = valueSelector.Parameters.Single();
// p => valueSelector(p) == values[0] || valueSelector(p) == ...
if (!values.Any()) {
return e => false;
}
var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
}
class Program {
static void Main(string[] args) {
List<int> fullList = new List<int>();
for (int i = 0; i < 20; i++) {
fullList.Add(i);
}
List<int> filter = new List<int>();
filter.Add(2);
filter.Add(5);
filter.Add(10);
List<int> results = fullList.AsQueryable().ExtWhereIn<int, int>(item => item, filter).ToList();
foreach (int result in results) {
Console.WriteLine(result);
}
}
}
}
Using the extensions is really easy (as you can see in the sample). To use it on a database object, assuming you are filtering a table called "Product" by more than one id, you could do something like that:
class Product {
public int Id { get; set; }
/// ... other properties
}
List<Product> GetProducts(List<int> productIds) {
using (MyEntities context = new MyEntities()) {
return context.Products.ExtWhereIn<Product, int>(product => product.Id, productIds).ToList();
}
}
Using the where method doesn't alway work
var results = from p in db.Products
where p.Name == nameTextBox.Text
select p;
Yes it does translate to SQL, it generates a standard IN statement like this:
SELECT [t0].[col1]
FROM [table] [t0]
WHERE [col1] IN ( 'Value 1', 'Value 2')

Resources