I have a class that works as a repository of some sort, and grants access to a database. I'm trying to customize it to allow queries with expressions.
So, I want to be able to do this:
IList<MyClass> myList = myRepository.GetBy(x => x.Name == "SomeName");
//and...
IList<MyClass> myList2 = myRepository.GetBy(x => x.Name == "SomeName" && x.ID = 5);
This is what I need to have on the repository function:
public IList<T> GetBy(Expression<Func<T, bool>> expression)
{
//Set up the query, etc
//I'm at the WHERE clause, and I need to get the property(ies) name(s) of the expression and their values to properly set the WHERE
}
How can I do this?
What you want to do is this:
IList <MyClass> myList2 = myRepository.GetBy (x => x.Name == "SomeName" && x.ID = 5);
It is true that you can represent x => x.Name == "SomeName" && x.ID = 5 with Expression <Func <T, bool >>
but also what you can do with the delegate Func <T, bool> only.
Regardless of which were to take the data will always be from an IEnumerable <T> so you always will have the Where method (whenever you use the namespace System.Linq), which accepts as a parameter a delegate Func <T, bool> . If the object IEnumerable <T> is a DbSet <T>, this will take care of transforming the delegate Func <T, bool> in a sql query. Remember that a Linq query, which is what is being used, is only executed when the query data is used or agrees with methods ToList () or ToArray () for example.
example:
IEnumerable <MyClass> list = ... from wherever you get the data even from DbSet of EntityFramework
var query = list.Where (x => x.Name == "SomeName" && x.ID = 5);
query is a shost a query, it contains no data until this is done
foreach (var x in list) is being consumed, so the query is executed
{
var c = x.Name;
}
or this
`var temp = query.ToList ();`
This force to stored in a List <MyClass>
With all this I want to say that if you use the DbSet of EntityFramework, what happens is that the delegate Func <T, bool> is transformed into a sql query, so that the data manager is responsible for filtering data (as it should be).
From this alone you would have to simply have your method
public IList <T> GetBy (Func <T, bool> expression)
{
origen.Where (expression).ToList();
}
if I understand your question correctly, you shoul inherit you repository interfaces from base generic repository interface.
public interface IRepositoryBase<TEntity>
{
IList<TEntity> GetBy(Expression<Func<TEntity, bool>> expression)
}
and repository realisation from base repository implementation
public abstract class RepositoryBase<TEntity>: IRepositoryBase<TEntity>
{
public MyEntities EntitiesContext { get; set; }
public IList<TEntity> GetBy(Expression<Func<TEntity, bool>> expression)
{
return EntitiesContext.Set<TEntity>().Where(filter).ToList()
}
}
Related
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();
}
I am using the following class to wrap some DocumentDB access which allows me to store multiple entities in the same collection:
public class TypedEntity<T> {
public string Type { get; set; }
public T Item { get; set; }
public TypedEntity(T item) {
Id = Guid.NewGuid().ToString();
Item = item;
Item.Id = Id;
Type = typeof (T).FullName;
}
}
The usage of this class is encapsulated inside a repository class. I'm trying to build the API of the repository class such that the consumer doesn't need to know about the usage of TypedEntity<T> and can instead treat it as a source for just <T>. For example, the repository has a method with this signature:
public async Task<IQueryable<T>> WhereAsync(Func<T, bool> predicate)
In order to actually retrieve this data, the predicate needs to be combined/converted with one that interacts with TypedEntity<T>. This is the pseudo code that I'm picturing in my head for what I'd ultimately like to achieve:
public async Task<IQueryable<T>> WhereAsync(Func<T, bool> predicate) {
// remembering that dataSource is talking to a backing store of TypedEntity<T>
var queryable = dataSource.Where(x => x.Type == typeof(T).FullName && predicate(x.Item));
// ... other business logic stuff
}
This actually builds but ultimately results in an Expression that uses .Invoke around the passed in predicate (which DocumentDb is unable to understand). Is there some way that I can combine the type part with the passed in Func to build up the Expression manually?
You'll want to take in an Expression<> rather than just a Func<>. Then, it should be fairly easy to apply it in a Where() clause after you've performed a Select() projection:
public async Task<IQueryable<T>> WhereAsync(Expression<Func<T, bool>> predicate) {
// remembering that dataSource is talking to a backing store of TypedEntity<T>
var typeName = typeof(T).FullName;
var queryable = dataSource.Where(x => x.Type == typeName)
.Select(x => x.Item)
.Where(predicate);
// ... other business logic stuff
}
I'm trying to dynamically construct an expression similar to the one below, where I can use the same comparison function, but where the values being compared can be passed in, since the value is passed from a property 'higher-up' in the query.
var people = People
.Where(p => p.Cars
.Any(c => c.Colour == p.FavouriteColour));
I believe I've constructed the query correctly, but the ExpressionExpander.VisitMethodCall(..) method throws the following exception when I try to use it:
"Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'"
In real-world code, using Entity Framework and actual IQueryable<T>, I often get:
"Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'" as well.
I've constructed a LinqPad-friendly example of my problem, as simple as I could make it.
void Main()
{
var tuples = new List<Tuple<String, int>>() {
new Tuple<String, int>("Hello", 4),
new Tuple<String, int>("World", 2),
new Tuple<String, int>("Cheese", 20)
};
var queryableTuples = tuples.AsQueryable();
// For this example, I want to check which of these strings are longer than their accompanying number.
// The expression I want to build needs to use one of the values of the item (the int) in order to construct the expression.
// Basically just want to construct this:
// .Where (x => x.Item1.Length > x.Item2)
var expressionToCheckTuple = BuildExpressionToCheckTuple();
var result = queryableTuples
.AsExpandable()
.Where (t => expressionToCheckTuple.Invoke(t))
.ToList();
}
public Expression<Func<string, bool>> BuildExpressionToCheckStringLength(int minLength) {
return str => str.Length > minLength;
}
public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// I'm passed something (eg. Tuple) that contains:
// * a value that I need to construct the expression (eg. the 'min length')
// * the value that I will need to invoke the expression (eg. the string)
return tuple => BuildExpressionToCheckStringLength(tuple.Item2 /* the length */).Invoke(tuple.Item1 /* string */);
}
If I'm doing something obviously wrong, I'd really appreciate a nudge in the right direction! Thanks.
Edit: I know that the following would work:
Expression<Func<Tuple<string, int>, bool>> expr = x => x.Item1.Length > x.Item2;
var result = queryableTuples
.AsExpandable()
.Where (t => expr.Invoke(t))
.ToList();
However, I'm trying to separate the comparison from the location of the parameters, since the comparison could be complex and I would like to re-use it for many different queries (each with different locations for the two parameters). It is also intended that one of the parameters (in the example, the 'min length') would actually be calculated via another expression.
Edit: Sorry, I've just realised that some answers will work when attempted against my example code since my example is merely masquerading as an IQueryable<T> but is still a List<T> underneath. The reason I'm using LinqKit in the first place is because an actual IQueryable<T> from an EntityFramework DbContext will invoke Linq-to-SQL and so must be able to be parsed by Linq-to-SQL itself. LinqKit enables this by expanding everything to expressions.
Solution! Thanks to Jean's answer below, I think I've realised where I'm going wrong.
If a value has come from somewhere in the query (i.e. not a value that is known before-hand.) then you must build the reference/expression/variable to it into the expression.
In my original example, I was trying to pass the 'minLength' value taken from within the expression and pass it to a method. That method call could not be done before-hand, since it used a value from the expression, and it could not be done within the expression, since you can't build an expression within an expression.
So, how to get around this? I chose to write my expressions so that they can be invoked with the additional parameters. Though this has the downside that the parameters are no longer 'named' and I could end up with an Expression<Func<int, int, int, int, bool>> or something down the line.
// New signature.
public Expression<Func<string, int, bool>> BuildExpressionToCheckStringLength() {
// Now takes two parameters.
return (str, minLength) => str.Length > minLength;
}
public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// Construct the expression before-hand.
var expression = BuildExpressionToCheckStringLength();
// Invoke the expression using both values.
return tuple => expression.Invoke(tuple.Item1 /* string */, tuple.Item2 /* the length */);
}
OK, so what you are trying to do (the transformation from a function that takes a single argument, that returns another function that takes a single argument f(x)(y) into a function that takes two arguments f(x, y)) is known as uncurrying. Look it up! :)
Now, the issue that you have in your code is that, in the expression returned by BuildExpressionToCheckTuple, there is a method call to BuildExpressionToCheckStringLength, which is not resolved. And you cannot resolve it because it takes an argument that is embedded in the tuple parameter.
The solution is, instead of using a method call, to use a lambda expression that will be equivalent to that method call.
That is:
public Expression<Func<int, Func<string, bool>>> ExpressionToCheckStringLengthBuilder() {
return minLength =>
str => str.Length > minLength;
}
public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// I'm passed something (eg. Tuple) that contains:
// * a value that I need to construct the expression (eg. the 'min length')
// * the value that I will need to invoke the expression (eg. the string)
// Putting builder into a variable so that the resulting expression will be
// visible to tools that analyze the expression.
var builder = ExpressionToCheckStringLengthBuilder();
return tuple => builder.Invoke(tuple.Item2 /* the length */).Invoke(tuple.Item1 /* string */);
}
So you are looking for something like this:
public static class Program
{
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public static IQueryable<T> WherePropertyEquals<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> property, TProperty value)
{
var result = src.Where(e => property.Invoke(e).Equals(value));
return result;
}
public static IQueryable<T> WhereGreater<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> property, TProperty value)
where TProperty : IComparable<TProperty>
{
var result = src.Where(e => property.Invoke(e).CompareTo(value) > 0);
return result;
}
public static IQueryable<T> WhereGreater<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> left, Expression<Func<T, TProperty>> right)
where TProperty : IComparable<TProperty>
{
var result = src.Where(e => left.Invoke(e).CompareTo(right.Invoke(e)) > 0);
return result;
}
public static void Main()
{
var persons = new List<Person>()
{
new Person
{
FirstName = "Jhon",
LastName = "Smith"
},
new Person
{
FirstName = "Chuck",
LastName = "Norris"
},
new Person
{
FirstName = "Ben",
LastName = "Jenkinson"
},
new Person
{
FirstName = "Barack",
LastName = "Obama"
}
}
.AsQueryable()
.AsExpandable();
var chuck = persons.WherePropertyEquals(p => p.FirstName, "Chuck").First();
var ben = persons.WhereGreater(p => p.LastName.Length, 6).First();
var barack = persons.WhereGreater(p => p.FirstName.Length, p => p.LastName.Length).First();
}
I may have function calls like this :
foo(**new {x.ID,x.Name}**);
and LINQ:
(IQueryable<SomeTableName>).where(x=>x.ID>1).select(x=>**new {x.ID,x.Name}**);
Is it possible to replace the "new {x.ID,x.Name}" part with a function, expression or variable, so I can change from time to time, in only 1 place?
Thank you.
I know I can make an Expression, which be used inside LINQ only
public static Func<SomeTableName, Object> Select_RS_Food = x => new { x.ID,x.Name };
but I also want to use for normal anonymous object creating. Like:
foo(CreateMyObject(x));
Update
Current:
return new { myTable.ID, myTable.Name};
//And
db.SomeTable.Select (x=> new { myTable.ID, myTable.Name));
Expect:
return SomeMagicCode;
//And
db.SomeTable.Select (x=> SomeMagicCode);
You could define a helper class like this to allow you to specify only some of the generic type parameters when creating a Func<T, R>:
public static class Funk<T>
{
public static Func<T, R> Make<R>(Func<T, R> func)
{
return func;
}
}
And use it like this:
var selector = Funk<SomeTableName>.Make(x => new {x.ID, x.Name});
var result = db.SomeTable.Where(x => x.ID>1).Select(selector);
This takes advantage of type inference to determine the return type, so you only have to define the input type (SomeTableName). The var keyword makes the selector variable implicitly typed, you won't have to specify Func<SomeTableName, _AnonymousType1_>. You can now reuse selector in multiple queries.
You will not be able to save this as property or field of a class, however, because implicit typing is not supported in type members. If you want to be able to reuse this selection function, you must use a named type or perhaps dynamic—but I wouldn't expect this to work with any current ORM.
For the other lambda expressions (x => x.ID == 1 and x => x.ID > 1), there are no anonymous types involved so you can simply do this:
public static Func<SomeTableName, bool> IDEquals1 = x => x.ID == 1;
public static Func<SomeTableName, bool> IDGreaterThan1 = x => x.ID == 1;
But by the way, if your using an ORM like entity framework, you probably need to use an Expression instead. Maybe something kind of like this:
public static Expression<Func<T, bool>> IDEquals1 = x => x.ID == 1;
Or even this:
public interface IHasID
{
int ID { get; }
}
public SomeTableName : IHasID { ... }
public static Expression<Func<T, bool>> IDEquals1<T>() where T : IHasID
{
return x => x.ID == 1;
}
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; }
}