sample code
public class program
{
public void sample()
{
var qry = repo.All(); // returns IQueryable //came from EF
var keyword = "al";
var result = qry.DynamicFilter(keyword, filterconditions);
// it should return all persons with 'al' in their name and nickname
}
public List<Expression<Func<Person, string, bool>>> filterconditions
{
get
{
var ret = new List<Expression<Func<Person, string, bool>>>();
ret.Add((m,n) => m.Name.Contains(n));
ret.Add((m,n) => m.Nickname.Contains(n));
return ret;
}
}
}
this is my extension
public static class linqExt
{
public static IQueryable<T> DynamicFilter<T>(this IQueryable<T> qry, string keyword, IEnumerable<Expression<Func<T, string, bool>>> conditions) where: T
{
var ret = qry;
foreach(var item in conditions)
{
ret = ret.Where(m => item.Compile()(m, keyword));
}
return ret;
}
}
is there any way to implement this without using the Compile/Invoke??
ret = ret.Where(m=> func.Compile()(m, keyword));??
the problem is that I can't use the Compile because the collection is an IQueryable and i can't convert it to IEnumarable because if I do that it will pull all the data from database.
I am thinking if I can use the METHODS and PROPERTIES of the EXPRESSION class
any guest? thanks
We have done something similar; but we use the PredicateBuilder class presented here: https://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/
var filter = PredicateBuilder.True<Person>()
.And(m => m.Name.Contains(n))
.And(m => m.Nickname.Contains(n));
Then you can just do qry.Where(filter) and it will apply your "dynamic" where clause. If this is LinqToEF this will cause the Where() clause to actually be generated and executed in SQL, which is probably want you want.
The PredicateBuilder is a very powerful class, and you can build some very elaborate clauses. When you need to do "ORs", be sure and "start" with a PredicateBuilder.False<Person>() statement.
You can also do conditional logic, and "append" statements to your filter, for example:
if (lookingForReallyLongNames == true)
{
filter = filter.And(m => m.Name.Length > 15);
}
Related
How may I avoid to duplicate the code I use for mapping a database entity to a poco object?
Given this code:
private IQueryable<DummyExtended> Find()
{
return (from dt in Entities.dummy_table
select new DummyExtended
{
Description = dt.table_1.table_2.description,
Dummy = new Dummy
{
Name = d.name,
Notes = d.notes,
HelpText = d.help_text
}
}).AsQueryable();
}
Can I create a common linq expression to be re-used for both methods?
private IQueryable<DummyExtended> Find()
{
return (from dt in Entities.dummy_table
select new DummyExtended
{
Description = dt.table_1.table_2.description,
Dummy = ...???
}).AsQueryable();
}
private IQueryable<DummyAlsoExtended> FindAnother()
{
return (from dt in Entities.dummy_table
select new DummyAlsoExtended
{
InnerHtml = dt.table_html.description,
Dummy = ....??
}).AsQueryable();
}
Example:
public static Expression<Func<dummy_table, Dummy>> EntityToPoco()
{
return d => new Dummy
{
Name = d.name,
Notes = d.notes,
HelpText = d.help_text
};
}
I can't quite get it right
....
Dummy = ExtensionClass.EntityToPoco()
So you have a dummy_table which is a Enumerable or Queryable sequence of objects. Let's assume that the sequence contains objects of class DummyTableElement.
You showed, that if you have a DummyTableElement you know how to convert it into a Dummy object. You want to reuse this function to create other objects like DummyExtended and DummyAlsoExtended. If you want to do this LINQ-alike, it is best to create extension functions for it:
static class DummyTableElementExtensions
{
public static Dummy ToDummy(this TableElement tableElement)
{
return new Dummy()
{
Name = tableElement.name,
Notes = tableElement.notes,
HelpText = tableElement.help_text
};
}
}
Once you have this, you can create similar functions to convert TableElements into DummyExtended and DummyAlsoExtended. They will be one-liners.
In the same extension class:
public static DummyExtended ToDummyExtended(this TableElement tableElement)
{
return new DummyExtended()
{
Description = tableElement.table_1.table_2.description,
Dummy = tableElement.ToDummy(),
};
}
public static DummyAlsoExtended ToDummyAlsoExtended(this TableElement tableElement)
{
return new DummyAlsoExtended
{
InnerHtml = tableElement.table_html.description,
Dummy = tableElement.ToDummy(),
};
}
And once you've got these, you can create extension functions to convert any IQueryable of TableElements:
public static IQueryable<DummyExtended> ToDummyExtended(
this IQueryable<TableElement> tableElements)
{
return tableElements
.Select(tableElement => tableelement.ToDummyExtended();
}
And a similar one-line function for DummyAlsoExtended.
Your Find function and FindAnother function will also be one-liners:
private IQueryable<DummyExtended> Find()
{
return dummy_table.ToDummyExtended();
}
private IQueryable<DummyAlsoExtended> FindAnother()
{
return dummy_table.ToDummyAlsoExtended();
}
I'm not sure why you wanted to use an expression in this. It doesn't seem that DummyExtended and DummyAlsoExtended are really similar, except that they both have a property Dummy.
One reason to parameterize the destination of your find function could be because you want to create anonymous classes in your Find function.
Again, once you've created ToDummy this will be a one-liner:
public static IQueryable<TResult> Find<TSource, TResult>(
this IQueryable<TSource> source,
Expression<Func<TSource, TResult>> resultSelector)
{
return source.Select(sourceElement => resultSelector(sourceElement);
}
Usage would be:
var X = dummy_Table.find(tableElement => new
{
foo = tableElement.CalculateFoo(),
bar = tableElement.CalculateBar(),
Dummy = tableElement.ToDummy(),
});
I get the following exception message when trying to run the ToList method for the Sitecore ContentSearch LINQ query in the following code block:
Unsupported expression node type: Parameter. This could be due to ordering of
expression statements. For example filtering after a select expression like this :
queryable.Select(i => new { i.Id, i.Title }).Where(i => d.Title > "A"
public virtual List<SearchResultItem> RunQuery(SearchParam param, bool showAllVersions, bool firstLoad)
{
Assert.ArgumentNotNull(Index, "Sitecore.SharedSource.Search");
var resultCollection = new List<SearchResultItem>();
try
{
using (var context = this.Index.CreateSearchContext())
{
var result = context.GetQueryable<SearchResultItem>()
.Where(x => HasFullText(x, param.FullTextQuery) &&
HasLanguage(x, param.Language) &&
HasRelation(x, param.RelatedIds) &&
HasTemplate(x, param.TemplateIds) &&
HasLocation(x, param.LocationIds)
);
resultCollection = result.ToList();
}
}
catch (Exception exception)
{
Log.Error(exception.StackTrace, this);
throw;
}
return resultCollection;
}
I can't figure out what causes this issue and I can't seem to reproduce the issue with standard .NET LINQ queries, in a standard Console Application (source code at the end).
Here is the source code for the HasLanguage, HasRelation, HasTemplate and HasLocation functions. I expect it has something to do with those because when I remove them and replace them with their implementation (where possible) I get no errors. However, when left inside the query they are not even accessed (tried debugging):
protected bool HasRefinements(SearchResultItem pseudoResult, SafeDictionary<string> refinements)
{
if (refinements.Count <= 0) return false;
foreach (var refinement in refinements)
{
var fieldName = refinement.Key.ToLowerInvariant();
var fieldValue = refinement.Value;
if (pseudoResult.GetField(fieldName).Value.Equals(IdHelper.ProcessGUIDs(fieldValue)))
{
return true;
}
}
return false;
}
protected bool HasLanguage(SearchResultItem pseudoResult, string language)
{
if (String.IsNullOrEmpty(language)) return false;
return pseudoResult.GetField(BuiltinFields.Language).Equals(language.ToLowerInvariant());
}
protected bool HasFullText(SearchResultItem pseudoResult, string searchText)
{
if (String.IsNullOrEmpty(searchText)) return false;
return pseudoResult.Content.Contains(searchText);
}
protected bool HasId(SearchResultItem pseudoResult, string fieldName, string filter)
{
if (String.IsNullOrEmpty(fieldName) || String.IsNullOrEmpty(filter)) return false;
var values = IdHelper.ParseId(filter);
foreach (var value in values.Where(ID.IsID))
{
if (pseudoResult.GetField(fieldName).Value.Equals(IdHelper.ProcessGUIDs(value)))
{
return true;
}
}
return false;
}
protected bool HasTemplate(SearchResultItem pseudoResult, string templateIds)
{
if (String.IsNullOrEmpty(templateIds)) return false;
templateIds = IdHelper.NormalizeGuid(templateIds);
return pseudoResult.TemplateId.ToString().Equals(templateIds);
}
protected bool HasLocation(SearchResultItem pseudoResult, string locationIds)
{
return HasId(pseudoResult, BuiltinFields.Path, locationIds);
}
protected bool HasRelation(SearchResultItem pseudoResult, string ids)
{
return HasId(pseudoResult, BuiltinFields.Links, ids);
}
And here is the source code for my test application using regular LINQ queries:
static void Main(string[] args)
{
Program p = new Program();
p.Process();
}
public void Process()
{
List<Boolean> flags = new List<Boolean>();
flags.Add(true);
flags.Add(false);
flags.Add(false);
flags.Add(true);
flags.Add(false);
bool b = true;
try
{
List<Boolean> trueFlags = flags
.Where<Boolean>(x => IsTrue(x, b))
.ToList();
Console.WriteLine(trueFlags.ToString());
Console.ReadKey();
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
}
}
public bool IsTrue(bool x, bool b)
{
return x ^ b;
}
I can't seem to find anything on this exception message on the internet.
Sitecore LINQ isn't really like normal LINQ to objects in .NET. Like LINQ to SQL isnt like normal LINQ.
What actually happens is that Sitecore parses the expression tree and "converts/translates" your conditions to a Search Query. By default this is to a Lucene query expression - but using a SearchProvider it could also translate to a SOLR expression or Coveo expression.
In the same way LINQ to SQL translates to a SQL expression.
You can see that the LINQ is actually an IQueryable and not IEnumerable.
When working on the IQuerable Sitecore must know how to translate it to the search expression. Sitecore doesn't know how to translate your properties and methods in the LINQ expression and that is why you get the error.
You should change your expression to only hold something that can be translated or create a predicate. You should look into the PredicateBuilder
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.
I have an array of input strings that contains either email addresses or account names in the form of domain\account. I would like to build a List of string that contains only email addresses. If an element in the input array is of the form domain\account, I will perform a lookup in the dictionary. If the key is found in the dictionary, that value is the email address. If not found, that won't get added to the result list. The code below will makes the above description clear:
private bool where(string input, Dictionary<string, string> dict)
{
if (input.Contains("#"))
{
return true;
}
else
{
try
{
string value = dict[input];
return true;
}
catch (KeyNotFoundException)
{
return false;
}
}
}
private string select(string input, Dictionary<string, string> dict)
{
if (input.Contains("#"))
{
return input;
}
else
{
try
{
string value = dict[input];
return value;
}
catch (KeyNotFoundException)
{
return null;
}
}
}
public void run()
{
Dictionary<string, string> dict = new Dictionary<string, string>()
{
{ "gmail\\nameless", "nameless#gmail.com"}
};
string[] s = { "anonymous#gmail.com", "gmail\\nameless", "gmail\\unknown" };
var q = s.Where(p => where(p, dict)).Select(p => select(p, dict));
List<string> resultList = q.ToList<string>();
}
While the above code works (hope I don't have any typo here), there are 2 problems that I do not like with the above:
The code in where() and select() seems to be redundant/repeating.
It takes 2 passes. The second pass converts from the query expression to List.
So I would like to add to the List resultList directly in the where() method. It seems like I should be able to do so. Here's the code:
private bool where(string input, Dictionary<string, string> dict, List<string> resultList)
{
if (input.Contains("#"))
{
resultList.Add(input); //note the difference from above
return true;
}
else
{
try
{
string value = dict[input];
resultList.Add(value); //note the difference from above
return true;
}
catch (KeyNotFoundException)
{
return false;
}
}
}
The my LINQ expression can be nicely in 1 single statement:
List<string> resultList = new List<string>();
s.Where(p => where(p, dict, resultList));
Or
var q = s.Where(p => where(p, dict, resultList)); //do nothing with q afterward
Which seems like perfect and legal C# LINQ. The result: sometime it works and sometime it doesn't. So why doesn't my code work reliably and how can I make it do so?
If you reverse the where and the select you can convert unknown domain accounts to null first, then just filter them out.
private string select(string input, Dictionary<string, string> dict)
{
if (input.Contains("#"))
{
return input;
}
else
{
if (dict.ContainsKey(input))
return dict[input];
}
return null;
}
var resultList = s
.Select(p => select(p, dict))
.Where(p => p != null)
.ToList()
This takes care of your duplicate code.
It takes 2 passes. The second pass converts from the query expression to List.
Actually this is only one pass as LINQ is lazy evaluated. This is why your last statements only work sometimes. The filter is only applied and your list generated if the LINQ query is evaluated. Otherwise the Where statement is never run.
It sounds like what you want is an iterator. By making your own iterator you can filter the list and produce output at the same time.
public static IEnumerable EmailAddresses(IEnumerable<string> inputList,
Dictionary<string, string> dict)
{
foreach (string input in inputList)
{
string dictValue;
if (input.Contains("#"))
yield return input;
else if (TryGetValue(input, out dictValue)
yield return dictValue;
// else do nothing
}
}
List<string> resultList = EmailAddresses(s, dict).ToList();
You don't generally want to have side-effects on an unrelated object like your list. It makes it difficult to understand, debug, and refactor. I wouldn't worry about optimizing the query until you know it's not performing well.
So, what's wrong with your original expression? You don't need both the select and the where. You only need the Where() call. This will return a list of email addresses, which you can stick into a HashSet. The HashSet will provide the uniqueness you seem to desire. This will add execution time, so if you don't need it, don't use it.
You should only really need something like:
var s = new[] {"me#me.com", "me_not_at_me.com", "not_me"};
var emailAddrs = s.Where( a => a.Contains("#")); // This is a bad email address validator; find a better one.
var uniqueAddrs = new HashSet<string>(emailAddrs);
(Note, I've not dealt with HashSet, so the constructor might not take an Enumerable. This would be an exercise for the reader.)
Here is one way you could approach it with LINQ. It groups the values by whether or not they are email addresses, resulting in 2 groups of strings. If a group is the email address group, we select directly from it, otherwise we look up the emails and select from those:
public static IEnumerable<string> SelectEmails(
this IEnumerable<string> values,
IDictionary<string, string> accountEmails)
{
return
from value in values
group value by value.Contains("#") into valueGroup
from email in (valueGroup.Key ? valueGroup : GetEmails(valueGroup, accountEmails))
select email;
}
private static IEnumerable<string> GetEmails(
IEnumerable<string> accounts,
IDictionary<string, string> accountEmails)
{
return
from account in accounts
where accountEmails.ContainsKey(account)
select accountEmails[account];
}
You would use it like this:
var values = new string[] { ... };
var accountEmails = new Dictionary<string, string> { ... };
var emails = values.SelectEmails(accountEmails).ToList();
Of course, the most straightforward way to implement this extension method would be #gabe's approach.
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')