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

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')

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

Programmatically chain OrderBy/ThenBy using LINQ / Entity Framework

I have a reporting interface where the end user gets to select multiple fields for the sort order of the returned report. The problem I am having is that I can't really chain the OrderBy / ThenBy methods, since I'm iterating through a list of sort fields. I'm thinking something like this:
foreach (string sort in data.SortParams)
{
switch (sort)
{
case "state":
query = query.ThenBy(l => l.RegionCode);
break;
case "type":
query = query.ThenBy(l => l.Type);
break;
case "color":
query = query.ThenBy(l => l.Color);
break;
case "category":
query = query.OrderBy(l => l.Category);
break;
}
}
(Note: I've removed the switch determining if this is the first sort item for simplicity's sake.)
Any thoughts on how to iterate through a collection to determine the sort order?
You could do what you want if you use an initial "seed" OrderBy:
EDIT you need to call OrderBy to create an IOrderedEnumerable (or IOrderedQueryable) first before attaching ThenBy clauses:
var orderedQuery = query.OrderBy(l => 0);
foreach (string sort in data.SortParams)
{
switch (sort)
{
case "state":
orderedQuery = orderedQuery.ThenBy(l => l.RegionCode);
break;
case "type":
orderedQuery = orderedQuery.ThenBy(l => l.Type);
break;
case "color":
orderedQuery = orderedQuery.ThenBy(l => l.Color);
break;
case "category":
orderedQuery = orderedQuery.ThenBy(l => l.Category);
break;
}
}
query = orderedQuery; // cast back to original type.
If you want something more flexible check out this answer
I've created these extension methods to tackle an identical problem as stated in the question:
public static class QueryableExtensions
{
public static IOrderedQueryable<T> AppendOrderBy<T, TKey>(this IQueryable<T> query, Expression<Func<T, TKey>> keySelector)
=> query.Expression.Type == typeof(IOrderedQueryable<T>)
? ((IOrderedQueryable<T>) query).ThenBy(keySelector)
: query.OrderBy(keySelector);
public static IOrderedQueryable<T> AppendOrderByDescending<T, TKey>(this IQueryable<T> query, Expression<Func<T, TKey>> keySelector)
=> query.Expression.Type == typeof(IOrderedQueryable<T>)
? ((IOrderedQueryable<T>)query).ThenByDescending(keySelector)
: query.OrderByDescending(keySelector);
}
The code in the question could then be refactored to:
foreach (string sort in data.SortParams)
{
switch (sort)
{
case "state":
query = query.AppendOrderBy(l => l.RegionCode);
break;
case "type":
query = query.AppendOrderBy(l => l.Type);
break;
case "color":
query = query.AppendOrderBy(l => l.Color);
break;
case "category":
query = query.AppendOrderBy(l => l.Category);
break;
}
}
REMARK These extension methods only check the previous expression in the expression tree to determine wether to use OrderBy or ThenBy, no other expressions are allowed in-between. If you also want to tackle that, you'll have to walk through the complete tree which might just add that overhead you don't want :)
using System.Linq.Expressions;
abstract class BaseEntity
{
public int Id { get; set; }
}
class Product : BaseEntity
{
public string Name { get; set; } = String.Empty;
public override string ToString() => $"Id: {Id}, Name: {Name}";
}
class OrderSpecification<T> where T : BaseEntity
{
public bool IsDescending { get; private set; }
public Expression<Func<T, object>> Selector { get; private set; }
public OrderSpecification(Expression<Func<T, object>> selector, bool isDescending = false)
{
Selector = selector;
IsDescending = isDescending;
}
}
static class EntityExtensions
{
public static IQueryable<T> Sort<T>(this IQueryable<T> query, OrderSpecification<T>[] specs) where T : BaseEntity
{
if (specs.Length == 0)
return query;
int i = 0;
foreach (var spec in specs)
{
if (i++ == 0)
query = spec.IsDescending ? query.OrderByDescending(spec.Selector) : query.OrderBy(spec.Selector);
else
{
var temp = query as IOrderedQueryable<T>;
query = spec.IsDescending ? temp!.ThenByDescending(spec.Selector) : temp!.ThenBy(spec.Selector);
}
}
return query;
}
}
class MyDatabase
{
static void Main()
{
var products = new[]
{
new Product{ Id=4, Name="Cindy"},
new Product{ Id=2, Name="Andy"},
new Product{ Id=1, Name="Victor"},
new Product{ Id=1, Name="Bobby"},
new Product{ Id=3, Name="Austin"}
}
.AsQueryable();
var output = products.Sort(new OrderSpecification<Product>[]
{
new OrderSpecification<Product>(p=>p.Id), // Order by Id first,
new OrderSpecification<Product>(p=>p.Name, true), // then order by Name descendingly
});
foreach (var p in output) Console.WriteLine(p);
Console.ReadKey();
}
}
May be using all in one Linq query is not the best option from readability point of view. I would use IQueryable to construct your query in memory. Use similar sort of switch statement (but with IQueryable) and then in the end do .ToList (i.e. enumerate) to execute desired query at the server.

Transform Expression m => m.Name to m => m[index].Name

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);
...
}

Help with linq2sql generic lambda expression

In my Database almost every table has its own translations table. I.e. Sports has SportsTranslations table with columns: SportId, LanguageId, Name. At the moment I'm taking translations like:
int[] defaultLanguages = { 1, 3 };
var query = from s in dc.Sports
select new
{
sportName = s.SportsTranslations.Where(st => defaultLanguages.Contains(st.LanguageID)).First()
};
I wonder is it possible to implement some kind of generic method, so I could refactor code like here:
var query = from s in dc.Sports
select new
{
sportName = s.SportsTranslations.Translate()
};
Solved. Here is the static method I written:
public static class Extras
{
public static T Translate<T>(this IEnumerable<T> table) where T : class
{
try
{
return table.Where(
t => defaultLanguages.Contains(
(int)t.GetType().GetProperty("LanguageID").GetValue(t, null)
)
).First();
}
catch (Exception)
{
throw new ApplicationException(string.Format("No translation found in table {0}", typeof(T).Name));
}
}
}

Dynamic Order (SQL ORDERBY) in LINQ CompiledQuery

how can I create a dynamic ORDERBY in my LINQ CompiledQuery (e.g. supply Order Field and Direction as parameters for the compiled query)?
I would do it this way, first all you really need is a way to access the property value by string on an object. You could use reflection, but its slow. So use this helper class approach which is based on the tests of http://stefan.rusek.org/Posts/LINQ-Expressions-as-Fast-Reflection-Invoke/3/
public static class LINQHelper
{
public static IComparable OrderByProperty<TClass>(TClass item,
string propertyName)
{
var t = Expression.Parameter(typeof(TClass), "t");
var prop = Expression.Property(t, propertyName);
var exp = Expression.Lambda(prop, t).Compile();
return (IComparable)exp.DynamicInvoke(item);
}
}
The in your code where you want your order by string of property name, in this example col1, you just do the following.
var myQuery = from i in Items
select i;
myQuery.OrderBy(i=>LINQHelper.OrderByProperty(i,"col1"));
Hope this helps.
I think I found it:
Check out this link. It will point you to the VS2008 code samples which contains a Dynamic Linq Query Library which contains the extension method below. This will allow you to go:
Object.OrderBy("ColumnName");
Here is the extension methods, but you may want the whole library.
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) {
return (IQueryable<T>)OrderBy((IQueryable)source, ordering, values);
}
public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) {
if (source == null) throw new ArgumentNullException("source");
if (ordering == null) throw new ArgumentNullException("ordering");
ParameterExpression[] parameters = new ParameterExpression[] {
Expression.Parameter(source.ElementType, "") };
ExpressionParser parser = new ExpressionParser(parameters, ordering, values);
IEnumerable<DynamicOrdering> orderings = parser.ParseOrdering();
Expression queryExpr = source.Expression;
string methodAsc = "OrderBy";
string methodDesc = "OrderByDescending";
foreach (DynamicOrdering o in orderings) {
queryExpr = Expression.Call(
typeof(Queryable), o.Ascending ? methodAsc : methodDesc,
new Type[] { source.ElementType, o.Selector.Type },
queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters)));
methodAsc = "ThenBy";
methodDesc = "ThenByDescending";
}
return source.Provider.CreateQuery(queryExpr);
}

Resources