linq lambda expression for sql contains - c#

I am using a generic repository, like this:
itemsList = (from myrow in UoW.FileRepository.Get()
select new FileModel()
{record_id = myrow.type_id,
descr = myrow.descr}).ToList();});
And this is the Get method:
public virtual IEnumerable<TEntity> Get()
{
// _aQuery = _theDbContext.Set<TEntity>();
IEnumerable<TEntity> query = _aQuery;
return query;
}
How would I implement a generic linq lambda expression if I wanted to create a similar query to search for a particular string in a particular field? In my viewmodel I would like to call something like:
from myrow in UoW.FileRepository.Srch(nameofFieldToSearch, searchString).
The query would look something like this?
public IEnumerable<TEntity> Srch(Expression<Func<TEntity, bool>> expression)
{
IEnumerable<TEntity> srchList = _aQuery.ToList();
return srchList.Where(????);
}
Thank you for your suggestions.
EDIT-----------------------
I have all my queries like Get and Srch in a general repository class and for now just need to know how to declare the query in the repository class and how to call it with the search string from my viewmodel. I am not sure if there is a consensus as to where/when to materialize and compile? I saw another discussion http://www.fascinatedwithsoftware.com/blog/post/2012/01/10/More-on-Expression-vs-Func-with-Entity-Framework.aspx and I quote from it below to inquire whether that is the same approach being suggested here? Thank you again.
"The profiler told us that LoadMyEntities was being called many, many times and it was taking a large fraction of our CPU time. The simple change below solved the problem. Can you guess why?"
public IEnumerable<MyEntity> LoadMyEntities(Func<MyEntity, bool> predicate)
{return Context.MyEntities.Where(predicate);}
"The parameter is now a Func<> instead of an Expression>. The reason this makes a difference is that a predicate that's in the form of an Expression is passed to SQL server, but a predicate that's passed as a Func is not. Normally, you'd want SQL Server to do as much for you as possible, and an Expression would be the right choice, but in this case we'd like to pre-load the entire table in the context -- which is exactly what a Func will do.
The Where extension method has two flavors. One extends IQueryable and takes an Expression parameter. The other extends IEnumerable and takes a Func.
Because 'predicate' is now a Func, the Where that extends IEnumerable is used.
The Entity Framework's fluent interface for constructing SQL queries is based on IQueryables, not IEnumerables. Therefore, the fluency stops just before the Where. The part of the statement that gets passed to the Entity Framework is just Context.MyEntities.
Context.MyEntities therefore returns the entire table to the context.
The entire table is now filtered with the predicate, and the value we really want is returned.
The next time the method is called, the Entity Framework realizes that the record we want is already in the context. (In my case, we were querying by the primary key, and EF is apparently smart enough to know that if there's a record in the context with that ID, it won't find an additional such record in the database.) Since we don't go out to SQL Server, we save lots of time. Obviously there are occasions when you would not want this, but in our case it was exactly what we wanted. The table was relatively small, and the same context was queried hundreds of times.
In the original version, the predicate was an Expression, so the compiler used the Where that extends IQueryable. The predicate was thus passed to SQL Server, which dutifully returned just one row to the context. The next time we called LoadMyEntities, Entity Framework had to call SQL Server again."

Please take a look at msdn repository pattern
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
This will provide more complete Generic Get method.
For search you could Try
public IEnumerable<TEntity> GlobalSearch(Expression<Func<TEntity, bool>> expression)
{
return Get(expression);
}
In DataProvider you could try something like this
Note This is not may be exact implementation, but will give you basic idea, how to invoke.
public List<Users> Search(List<string> userList)
{
Expression<Func<User, bool>> expression = x=>UserList.Contains(x.UserName);
return GlobalSearch(expression);
}
Example of Repository pattern

This would be a simple implementation using the Expression trees. Following is the complete solution:
Method to fetch the Expression for Srch method:
Public Expression<Func<TEntity, bool>> SrchExpression(string nameofFieldToSearch, string searchString)
{
var parameterType = Expression.Parameter(typeof(TEntity), "obj");
var memberExpression = Expression.Property(typeof(string), nameofFieldToSearch)
// Calls Extension method created underneath
var filtersMethodInfo = typeof(StringExtensions).GetMethod("Contains", new[] { typeof(string), typeof(string) });
var filtersConstantExpression = Expression.Constant(searchString, typeof(string));
var finalExpression = Expression.Call(null, filtersMethodInfo, memberExpression, filtersConstantExpression)
return Expression.Lambda<Func<TEntity, bool>>(finalExpression, parameterType)
}
// Create a String extension method for Contains
public static class StringExtensions
{
public static bool Contains(this string source, string searchString)
{
return source?.IndexOf(subString, StringComparison.OrdinalIgnoreCase) >= 0;
}
}
Now your Srch method shall look like:
public IEnumerable<TEntity> Srch(Expression<Func<TEntity, bool>> expression)
{
Func<TEntity, bool>> func = expression.Compile();
IEnumerable<TEntity> srchList = _aQuery.Where(o => func(o));
return srchList;
}

Related

How to call Expression<Func<T, bool>> parameterized function wtih Linq Skip and Take

I have a function shown below. It has Expression<Func<T, bool>> parameter where T is the entity called "Languages".
public async Task<List<T>> MyFilterAsync(Expression<Func<T, bool>> filter)
{
return await context.Set<T>().Where(filter).ToListAsync();
}
I want to call this function from my razor pages so that I can get records from 10 to 15 only (and not every record). So there is "filter" parameter of Expression<Func<T, bool>> type in this method. I want to make use of it.
So from C# code on my razor pages. I can call this like as shown below:
Expression<Func<Languages, bool>> filter = m => m.Name == "ABC";
The above code will give me lanaguages that have name "ABC". Now comes the modification part.
I want only 10 to 15 records so I need to modify it incude Skip(skip).Take(pageSize) for the where clause on Linq expression. The question is - is this can be done, so how?
The context.Set<T>() is a list of Languages so we can do skip and take, right?
I hope I am able to explain the question properly.
It's strange that you cannot modify your MyFilterAsync, are you sure? Because the requirement of ordering, skipping & taking need more arguments than just the filter. So it's best if you could write more overloads for your MyFilterAsync to accept more arguments and write similar code to what proposed by other users.
However here I'm trying to make it possible to keep your MyFilterAsync unchanged but still you can hook in the logic for ordering, skipping & taking. It's not magic at all but you still need to write other code: your own extension method to replace the default Where. It depends on how the extension method overloads are picked by the compiler. The default has the most generic type of TEntity for entitty type. You just need to make your extension method overload more specific on the type, e.g: the Languages type in your example. It can be your base entity type. When it's less general (more specific), your extension overloads will be used by the compiler instead of the default ones.
Here's how you can do to make it work:
//put this in the same namespace with the default
//extension methods defined in System.Linq.Queryable
public static class YaQueryableExtensions
{
static readonly AsyncLocal<int> _skip = new AsyncLocal<int>();
static readonly AsyncLocal<int> _take = new AsyncLocal<int>();
static class ExpressionBuffers<TEntity>
{
public static readonly AsyncLocal<Expression<Func<TEntity, object>>> OrderBy =
new AsyncLocal<Expression<Func<TEntity, object>>>();
}
//here is your own extension method for Where
//targeting the specific type of Languages
//which can be any base entity type (if you want it to apply on a broader scope)
public static IQueryable<Languages> Where(this IQueryable<Languages> source,
Expression<Func<Languages, bool>> filter)
{
return source.WhereWithExpressionBuffers(filter);
}
//the generic helper method which can be used on a specific closed type
//of T (this method can be made private)
public static IQueryable<T> WhereWithExpressionBuffers<T>(this IQueryable<T> source,
Expression<Func<T, bool>> filter)
{
source = Queryable.Where(source, filter);
//check for order-by (which should be chained first if any)
var orderBy = ExpressionBuffers<T>.OrderBy.Value;
if(orderBy != null)
{
source = source.OrderBy(orderBy);
ExpressionBuffers<T>.OrderBy.Value = null;
}
//check for skip
var skip = _skip.Value;
if (skip > 0)
{
source = source.Skip(_skip.Value);
_skip.Value = 0;
}
//check for take
var take = _take.Value;
if (take > 0)
{
source = source.Take(take);
_take.Value = 0;
}
return source;
}
public static Expression<Func<T, bool>> Skip<T>(this Expression<Func<T, bool>> filter, int skip)
{
_skip.Value = skip;
return filter;
}
public static Expression<Func<T, bool>> Take<T>(this Expression<Func<T, bool>> filter, int take)
{
_take.Value = take;
return filter;
}
public static Expression<Func<TEntity, bool>> OrderBy<TEntity>(this Expression<Func<TEntity, bool>> filter,
Expression<Func<TEntity,object>> orderBy)
{
ExpressionBuffers<TEntity>.OrderBy.Value = orderBy;
return filter;
}
}
Now is how you use it:
var result = await MyFilterAsync(filter.OrderBy(e => e.Name).Skip(skip).Take(pageSize));
The OrderBy, Skip and Take are chained on the filter instead (with our extension methods) so that they can be buffered for later using inside our own Where extension method where we can read the buffered expressions to build up the Where correctly the way we want).
NOTE: you should put your extension class in the same namespace with Queryable which is System.Linq so that your extension methods can become available automatically (and of course will be used instead of the default extension methods).
Yes just do it after sorting the items, and you may need to implement an interface for Name property to have the orderby property work with generics.
public interface IHasName
{
string Name { get; set; }
}
public async Task<List<T>> MyFilterAsync(Expression<Func<T, bool>> filter, int skip, int take)
where T : class, IHasName
{
return await context.Set<T>()
.Where(filter)
.OrderBy(x=> x.Name)
.Skip(skip)
.Take(take)
.ToListAsync();
}

.Include expression inconsistency EntityFramework

I have the following custom method created for Finding entities in our EF context. It can accept include expressions, which seem to work quite inconsistently. For example, specifying an include, creating a query and then creating a list on that query will not include (sometimes) the entity in the list.
public static IQueryable<T> Find<T>(Expression<Func<T, bool>> expression, params Expression<Func<T, object>>[] includeExpressions) where T : class
{
var query = Current.Set<T>().Where(expression);
foreach (var include in includeExpressions)
{
query = query.Include(include);
}
return query;
}
Any suggestions on why could this happen?

Reusable LINQ query except for where clause

I've got a collection of movies which have various properties (title, release year, rating, etc) that I need to search for using a LINQ query as follows:
public BindingList<Movie> SearchByTitle(string title)
{
var matches = from movies in movieCollection
where movies.Title == title
select movies;
// do some other stuff with the matches
}
But I don't want a separate method to search for each property since the only thing that changes between searches is the where section. For example where movies.Rating == rating or where movies.ReleaseYear == releaseYear. How do I make the search method reusable for all different kinds of searches by passing in some sort of Expression or Func as the where section?
How do I make the search method reusable for all different kinds of searches by passing in some sort of Expression or Func as the where section?
Your query really isn't anything other than the where clause. But you can easily make the where part configurable... just not using query expressions.
public BindingList<Movie> SearchByTitle(Expression<Func<Movie, bool>> predicate)
{
var matches = movies.Where(predicate);
// Do common stuff with the matches.
}
EDIT: I was assuming that movies was an IQueryable<T>, given that you were talking about Expression. If it's just an IEnumerable<T>, you want:
public BindingList<Movie> SearchByTitle(Func<Movie, bool> predicate)
{
var matches = movies.Where(predicate);
// Do common stuff with the matches.
}
You can use an extension method (define this in a static class)
public static IQueryable<T> AddSearchParameter<T>(this IQueryable<T> query, bool condition, System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
if (condition)
{
query = query.Where(predicate);
}
return query;
}
So for example:
public BindingList<Movie> Search(string title, int? year, int? rating)
{
var matches = movieCollection.AddSearchParameter(!string.IsNullorEmpty(title), m=>m.Title == title);
matches = matches.AddSearchParameter(year.HasValue, m=>m.Year == year.Value);
matches = matches.AddSearchParameter(rating.HasValue, m=>m.rating >= rating.Value);
// do some other stuff with the matches
}
If you're using this against a database it will not actually execute the query until you enumerate so this will not make multiple calls to your database.
You could use a CompiledQuery.
Check this very interesting answer on SO.
Hope it helps.

Dynamically compiling LINQ queries to verify a dictionary value

Let us suppose we need to query on a list of entities and we do not know the criteria which is pretty much dynamic and the entity has both dictionaries and simple fields inside Let this be the next entity - Address(I left only one property for simplicity).
public class Address
{
#region Public members
/// <summary>
/// The extra datafield values
/// </summary>
public IDictionary<string, string> DataFieldValues { get; set; }
public string City { get; set; }
#endregion
}
Now if we query on a fixed field called City when I got the implementation:
private static Expression<Func<Address, bool>> BuildLambdaForAQueryItem(string caption, string value)
{
ParameterExpression param = Expression.Parameter(typeof(Address), caption);
BinaryExpression body = Expression.Equal(Expression.PropertyOrField(param, caption),
Expression.Constant(value,
typeof(Address).GetProperty(
caption).PropertyType));
return Expression.Lambda<Func<Address, bool>>(body, param);
}
Now if I want to query on a DataFieldValue coolection I need to write a lambda similar too:
x=>x.DataFieldValues.ContatinsKey(key) && DataFieldValues[key]==value
What I get with the method below is almost similar but still it does not apply the filter correctly:
private static Expression<Func<Address, bool>> BuildLambdaForAnExtraField(PostedQueryItem queryItem)
{
ParameterExpression dataFields = Expression.Parameter(typeof(Address), "x");
var dictionaryExpression = Expression.PropertyOrField(dataFields, "DataFieldValues");
var keyExists = Expression.Call(dictionaryExpression, "ContainsKey", null, Expression.Constant(queryItem.Caption));
Expression dictionaryAccessExpr = Expression.Property(dictionaryExpression, "Item",
Expression.Constant(queryItem.Caption));
var valueCorresponds = Expression.Equal(dictionaryAccessExpr, Expression.Constant(queryItem.Value));
return Expression.Lambda<Func<Address, bool>>(keyExists, dataFields).And(
Expression.Lambda<Func<Address, bool>>(valueCorresponds, dataFields));
}
I think you want to use Expression.AndAlso (a short-circuiting AND) on the two predicate expressions in question to construct the body of the expression-tree.
var body = Expression.AndAlso(keyExists, valueCorresponds);
return Expression.Lambda<Func<Address, bool>>(body, dataFields);
EDIT: (If you want to stick with your existing technique)
My guess is that your And method is an extension-method from the LINQKit library. If so, note that this extension involves 'invoking' the right-hand side expression with the parameters of the first expression as part of producing the result. If this isn't acceptable to you (LINQ provider limitations, perhaps?), you can use the useful Expand extension that also comes with this library to 'inline' the invoked expression.
return Expression.Lambda<Func<Address, bool>>(keyExists, dataFields)
.And(Expression.Lambda<Func<Address, bool>>(valueCorresponds, dataFields))
.Expand();
But this is massive overkill in this case; my advice is to go with my first sample.

How do I make an anonymous method run in LINQ to Entities?

I'm trying to build a generic method that EF4.1 to look in both the Database and the Local memory for a particular row in a table that matches a particular criteria.
So far, this is what I have this.
This is the caller.
dbEntities.MyTables.LocalAndDb(delegate(MyTable s)
{ return s.Description.Contains("test"); });
This is LocalAndDb
public static object LocalAndDb<T>(this DbSet<T> myTable, Func<T, bool> function) where T : class
{
// look in local
var item = myTable.Local.Where(o => function((T)o)).FirstOrDefault()
// if not exist, look in the database
if (item == null)
{
Expression<Func<T, bool>> predicate = (u) => function(u);
item = myTable.Where(predicate).FirstOrDefault();
}
return item;
}
The problem is with this line.
item = myTable.Where(predicate).FirstOrDefault();
When it calls the database, it throws this error.
"The LINQ expression node type 'Invoke' is not supported in LINQ to Entities."
I imagine it's because I'm passing in an anonymous method and it doesn't know how to turn this into SQL. I thought converting it to an Expression object would do the trick but it's still not working for me.
What do I need to do to make a anonymous method become something that LINQ can turn into SQL?
To make this work, you need to pass the lambda expression to LocalAndDb as an expression tree (so that LINQ to Entities can analyze the code and translate it to SQL):
public static object LocalAndDb<T>(this DbSet<T> myTable,
Expression<Func<T, bool>> expr) where T : class {
// ...
if (item == null) {
item = myTable.Where(expr).FirstOrDefault();
}
return item;
}
Then, of course, the problem is that you cannot execute the expression tree when checking the in-memory data. One way to solve this is to use the Compile method of Expression<T>, but that will be a bit inefficient (depending on your scenario).
Another option is to just pass the condition as both function and expression tree:
public static object LocalAndDb<T>(this DbSet<T> myTable,
Func<T, boo> function, Expression<Func<T, bool>> expr) where T : class {
var item = myTable.Local.Where(o => function((T)o)).FirstOrDefault();
if (item == null) {
item = myTable.Where(expr).FirstOrDefault();
}
return item;
}
table.LocalAndDb(t => t.Foo > 10, t => t.Foo > 10);
This is a bit ugly, but it doesn't require inefficient compilation at runtime. If you want a slightly more sophisticated solution, then you can define your own type to keep pre-compiled functions:
class Precompiled<T1, T2> {
public Precompiled(Expression<Func<T1, T2>> expr) {
this.Expression = expr;
this.Function = expr.Compile();
}
public Expression<Func<T1,T2>> Expression { get; private set; }
public Func<T1,T2> Function { get; private set; }
}

Categories

Resources