We use Entity Framework, and we need some runtime build queries on our objects. Building expression trees from scratch seems like a lot of work, so we want to use "System.Linq.Dynamic"
Working through the samples I got this to work:
dbModel.As.Where("AStuff.Contains(#0) OR AStuff.Contains(#1)","ac","bc")
But if I try to build the expressions seperately like this:
Expression<Func<A, bool>> predicateA =
DynamicExpression.ParseLambda<A, bool>(
"AStuff.Contains(#0)",
"ac"
);
Expression<Func<A,bool>> predicateB =
DynamicExpression.ParseLambda<A, bool>(
"AStuff.Contains(#0)",
"bc"
);
dbModel.As.Where("#0(it) OR #1(it)", predicateA, predicateB);
it explodes with an exception:
NotSupportedException>>The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
It may be possible to build the entire query in the first form, but the later would be more useful in our scenario. Is there a way to make that work?
Just use Predicate Builder to join (Or/And) multiple predicates.
Related
I was wondering if there's a way to use Expression class for custom queries with LINQ queries such as this
Expression<Func<TEntity, bool>> expression = (x) => x.Id = 1
var items = from item in context.TEntity
where expression
select item
I know there's a way to do this using LINQ function as the Where function takes an expression as a parameter but I really need to do this using LINQ queries
note: the code above is just an example to make you get around what I'm trying to do it's not an actual working code
note 2: I need this to work with Entity Framework
Unfortunately, Entity Framework does not natively support this type of expression projection, which is why a common attempt such as where expression.Compile().Invoke(item); will throw an exception at runtime.
However, if you use the LinqKit library (which utilizes the Expression Visitor) and call .AsExpandable() on the entity set, you can then invoke your expression dynamically:
Expression<Func<TEntity, bool>> expression = x => x.Id == 1;
var items = from item in context.Set<TEntity>.AsExpandable()
where expression.Invoke(item)
select item;
This works, at least on EF Core (I have just tested it). Don't have any EF6 project handy where I could test.
Expression<Func<TEntity, bool>> expression = (x) => x.Id = 1
var items = from item in context.TEntity
where expression.Compile()(item);
select item
Update: I just made a simple test project on EF6 and it does not work there, since it doesn't recognize the expression invocation
I've got a class with a bool method, e.g.:
public bool IsInFuture()
{ return this.Date > DateTime.Now; }
And I store it in a database using EF Code First. If I'll try to use that predicate in Linq operations, I'll get an exception, as it can not be translated into SQL:
await context.Where(order => order.IsInFuture()).ToListAsync();
That predicate's logic can be rather complicated and I wouldn't like to duplicate it in my code. It there any way I can "inline" it's code into Linq operations? I'm pretty sure, this problem has a solution.
Thanks in advance!
You can't use the predicate directly. How should EF know how to convert it into an SQL WHERE statement?
You have a few alternatives:
Rephrase the query to use standard operations, e.g. smaller-than. This will duplicate the logic from the predicate, though.
Use a raw SQL query. This will also duplicate the logic, but in SQL.
Load the entire list of entities and then filter in-memory (i.e. Where() after ToList()). Like this, you can reuse the predicate. Note that this only a good idea for very small data sets.
We are in process of preparing dynamic filters which will be used for filtering records. We just should be able to select an entity (Lets say Person) then define filters according to that entity selected.
The design is something similar to this.
So how to design such a filtering system which can be used with any entity and any property?
We need to use
C#
MVC 5
I have created a dynamic filter feature using WPF, the way I did it was using LINQ To SQL ORM, using my DataContext I get all the tables and its corresponding fields (MetaDataMember) and used it build the UI
IEnumerable<MetaTable> mappedTables = DBContext.Mapping.GetTables();
IEnumerable<MetaDataMember> tablesColumns = mappedTables.SelectMany(t => t.RowType.DataMembers);
Then I build Expression Trees on the fly for each criteria the user created using the MetaDataMember and its property.
Type entityType = metaDataMember.DeclaringType.Type;
ParameterExpression parameterExpression = Expression.Parameter(entityType, "t"); // t => t; t is the parameterExpression representing the Table type
MemberExpression leftStringExpression = Expression.PropertyOrField(parameterExpression, metaDataMember.Name); // t => t.columnName
And then build the conditions i.e, Expression.LessThan (etc) and keep building the tree for each criteria using the conjunction expressions( Expression.AndAlso or Expression.OrElse) for each criteria and then finally create an lambda expression Expressions.Lambda and pass it to .Where() Linq Extension method to get my IQueryable<> filtered values back and use it to filter the UI.
IQueryable<SomeTable> results = DataContext.SomeTable.Where(whereLambdaExpression).Select(selectLambdaExpression);
The reason I went with this approach was because the query doesn't get executed until the enumeration (results.ToList()) occurs, and the query is executed in the DB server and not in the current application. LINQ converts the query expression to pure SQL statement and only the results are returned. It is pretty fast as everything happens in the DB Server.
I am not able to paste all my code as it pretty huge, but you get the gist of it.
I had come across this kind of problem and I solved it by compiling text as LINQ query and executing it on the data (collection) and collecting the result. It will not give you best performance as each time it needs to dynamically compile an assembly (I have used it with collection as big as 100K rows and works well). However, it is very flexible. You can perf tune it further. Code is pretty long so here is the github link.
Here is the main idea:
create a valid linq query expression as string
compile it as an assembly
load the assembly into memory and pass the input collection to the assembly
collect the output and use it.
The github link I shared has a simple working application with samples and projections.
I have a Service Repository pattern built on top of Entity Framework.
The service has methods such as Find(IQuery query) that return IEnumerable.
The IQuery object is our own query object type where we convert strings to an IQueryable expression that the repository, which exposes IQueryable, can use.
What I'd like to do is be able to write a queryable on the client side and pass that over the service so that we can take advantage of the static typing and linq style queries instead of building our own query object in formation.
In other words I want to be able to do something like:
var query = new List<Type>().Where(x => x.Property == "argument").AsQueryable();
service.find(query);
Then I would pass the queryable or the expression it creates to my repository and work like that.
Is this sort of thing possible, or would I have to build the expression from scratch? It seems like this should be possible, but I really don't know where to begin or see examples of how to share an expression like this.
If you call AsQueryable() on an IEnumerable that doesn't already implement IQueryable, then that whole enumerable is just stuffed into a ConstantExpression, and any previous LINQ operations are not expressed as expression trees.
If you call AsQueryable() right on the source, then the LINQ operations will return an IQueryable with the proper query expression tree:
var query = new List<Type>().AsQueryable().Where(x => x.Property == "argument");
I'm trying to do the following:
var query =
(from a in session.Query<A>()
where a.BasicSearch(searchString) == true
select a);
But it keeps giving me this exception "System.NotSupportedException"!
Any idea how to solve this?
It is not possible to use user-defined functions in a LINQ query. The NHibernate linq provider does not 'know' how to translate your function into SQL.
LINQ to NHibernate works by inspecting the LINQ expression that you provide at runtime, and translating what it finds in this expression tree into a regular SQL expression. Here's a good article to get some background on expression trees: http://blogs.msdn.com/b/charlie/archive/2008/01/31/expression-tree-basics.aspx
You CAN reuse predicates like this in another way however, using the techniques discussed here. (I'm not sure if this works with NHibernate however.) IF it works it would look something like this:
// this could be a static method on class A
public static Expression<Func<A, bool>> BasicSearch(string criteria)
{
// this is just an example, of course
// NHibernate Linq will translate this to something like
// 'WHERE a.MyProperty LIKE '%#criteria%'
return a => criteria.Contains(a.MyProperty);
}
Usage:
from a in Session.Query<A>().Where(A.BasicSearch(criteria))
UPDATE: apparently there will be issues with NHibernate. See this blog post for a version that ought to work.
It is possible to call your own and SQL functions, but you have to make a wrapper for them so that NHibernate knows how to translate the C# to SQL.
Here's an example where I write an extension method to get access to SQL Server's NEWID() function. You would use the same techniques to get access to any other function on your database server, built-in or user-defined.
Some examples to extend NHibernate LINQ:
http://fabiomaulo.blogspot.se/2010/07/nhibernate-linq-provider-extension.html
https://nhibernate.jira.com/browse/NH-3301
Declare a BasicSearch extension method. Supposing your udf is on dbo:
using NHibernate.Linq;
...
public static class CustomLinqExtensions
{
[LinqExtensionMethod("dbo.BasicSearch")]
public static bool BasicSearch(this string searchField, string pattern)
{
// No need to implement it in .Net, unless you wish to call it
// outside IQueryable context too.
throw new NotImplementedException("This call should be translated " +
"to SQL and run db side, but it has been run with .Net runtime");
}
}
Then use it on your entities:
session.Query<A>()
.Where(a => a.SomeStringProperty.BasicSearch("yourPattern") == true);
Beware, trying to use it without referencing an entity in its usage will cause it to get evaluated with .Net runtime instead of getting it translated to SQL.
Adapt this BasicSearch example to whatever input types it has to handle. Your question was calling it directly on the entity, which does not allow your readers to know on how many columns and with which types it need to run.