Let's say I have a class like:
public class Foo
{
public string Title {get;set;}
}
Now, let's assume I have a public List<Foo> myList which I want to filter by Linq as so:
var x = myList.Where(f => f.Title == myValue);
Everything is nice and clear until now.
But how can access the property by variable? Something like:
string myProperty = "Title";
var x = myList.Where(f => f.myProperty == myValue);
You can write an extension method
public static class MyExtensions
{
public static object GetProperty<T>(this T obj, string name) where T : class
{
Type t = typeof(T);
return t.GetProperty(name).GetValue(obj, null);
}
}
and use it like this
var x = myList.Where(f => f.GetProperty("Title") == myValue);
This is not the type of situation that LINQ is used for. LINQ is a fluent interface for manipulating collections. Accessing members via a textual representation is done with reflection.
object GetProperty(Foo f, string propertyName) {
var type = typeof(Foo);
var propInfo = type.GetProperty(propertyName);
return propInfo.GetValue(f, null);
}
If you need to compose your queries dynamically on the fly, you can use the LINQ Dynamic Query library, a sample from Microsoft:
This sample shows a technique for composing LINQ statements on the
fly, dynamically, at run time.
Reference the library in your code:
using System.Linq.Dynamic;
Your query would look like this:
// You can use a string as the argument for the Where method
// meaning you can compose this string dynamically
string myProperty = "Title";
var x = myList.Where(myProperty + " = " + myValue);
It's also possible to use placeholder in the query string, which improves readability (somewhat):
var x = myList.Where("#0 = #1", myProperty, myValue);
See also this post from Scott Guthrie: Dynamic LINQ Part 1: Using the LINQ Dynamic Query Library (I don't think there ever was a part 2...)
Note: you have to compile the sample code from Microsoft and reference the built assembly, or you could include the code in your own project.
I know this is an old thread but here is another way to do it. This has the advantage of being significantly faster if you need to do it in a loop. I have converted the result out of "func" to be object to make it a bit more general purpose.
var p = Expression.Parameter(typeof(string));
var prop = Expression.Property(p, "Length");
var con = Expression.Convert(prop, typeof(object));
var exp = Expression.Lambda(con, p);
var func = (Func<string, object>)exp.Compile();
var obj = "ABC";
int len = (int)func(obj);
In the original question the code was being used inside linq so speed could be good. It would be possible to use "func" direct in the where clause also if it was built correctly, eg
class ABC
{
public string Name { get; set; }
}
var p = Expression.Parameter(typeof(ABC));
var prop = Expression.Property(p, "Name");
var body = Expression.Equal(prop, Expression.Constant("Bob"));
var exp = Expression.Lambda(body, p);
var func = (Func<ABC, bool>)exp.Compile();
ABC[] items = "Fred,Bob,Mary,Jane,Bob".Split(',').Select(s => new ABC() { Name = s }).ToArray();
ABC[] bobs = items.Where(func).ToArray();
you cant use linq dynamic query from microsoft
here is sample code
var query = db.Customers.Where("City == #0 and Orders.Count >= #1", "London", 10).
OrderBy("CompanyName").
Select("New(CompanyName as Name, Phone)");
Related
I have an IQueryable<T> from my DbSet in Entity Framework. I am provided a "Fuzzy Search String", named searchText, like so:
public List<T> Search<T>(string searchText)
{
using (var context = ...)
{
var baseQuery = context.Set<T>().AsQueryable();
baseQuery = baseQuery.Where(x =>
DbFunctions.Like(x.PropertyName, searchText)
|| DbFunctions.Like(x.PropertyTwo, searchText)
|| DbFunctions.Like(x.PropertyThree, searchText)
|| DbFunctio..... etc
);
return baseQuery.ToList();
}
}
But given the generic nature, I don't know what properties there are on the type. I can provide an abstract method to somebody implementing this which allows them to give me a List of Properties (or even PropertyInfo or whatever else, I can figure that out). But I don't know how to dynamically create the expression. This is what I have so far:
var baseQuery = context.Set<T>().AsQueryable();
var expression = baseQuery.Expression;
var colName = "colName"; // Or names, I can iterate.
var parameter = Expression.Parameter(typeof(T), "x");
var selector = Expression.PropertyOrField(parameter, colName);
expression = Expression.Call(typeof(DbFunctions), nameof(DbFunctions.Like),
new Type[] { baseQuery.ElementType, selector.Type },
expression, Expression.Quote(Expression.Lambda(selector, parameter)));
The problem here is... well, it doesn't work to begin with. But mainly that I'm not using the searchText anywhere in it, and don't know how to plug it in. I THINK I'm close... but have spent an inordinate amount of time on it.
Hopefully I'm getting your query logic right: if you want to build a set of LIKE conditions based on known type and list of column names, you could try something like this:
static private MethodInfo dbLikeMethod = typeof(DbFunctions).GetMethod(nameof(DbFunctions.Like), BindingFlags.Public | BindingFlags.Static, null, new Type[] {typeof(string), typeof(string)}, null); // I am targeting DbFunctions.Like(string, string). You might want another overload (or even mix them up depending on your inputs)
public List<T> Search<T>(string searchText) where T: class
{
using (var context = new ...)
{
var baseQuery = context.Set<T>().AsQueryable().Where(CreateExpression<T>(searchText));// you could probably find a more elegant way of plugging it into your query
return baseQuery.ToList();
}
}
Expression<Func<T, bool>> CreateExpression<T>(string searchText) where T : class
{
var cols = new List<string> {
"PropertyName",
"PropertyTwo" // i understand you've got a way to figure out which strings you need here
};
var parameter = Expression.Parameter(typeof(T), "x");
var dbLikeCalls = cols.Select(colName => Expression.Call(dbLikeMethod, Expression.PropertyOrField(parameter, colName), Expression.Constant(searchText))); // for convenience, generate list of DbFunctions.Like(x.<Property>, searchText) expressions here
var aggregatedCalls = dbLikeCalls.Skip(1).Aggregate((Expression)dbLikeCalls.First(), (accumulate, call) => Expression.OrElse(accumulate, call)); // aggregate the list using || operators: use first item as a seed and keep adding onto it
return Expression.Lambda<Func<T, bool>>(aggregatedCalls, parameter);
}
It's possible to declare a lambda function and immediately call it:
Func<int, int> lambda = (input) => { return 1; };
int output = lambda(0);
I'm wondering if it's possible to do so in one line, e.g. something like
int output = (input) => { return 1; }(0);
which gives a compiler error "Method name expected". Casting to Func<int, int> doesn't work either:
int output = (Func<int, int>)((input) => { return 1; })(0);
gives the same error, and for reasons mentioned below I'd like to avoid having to explicitly specify the input argument type (the first int).
You're probably wondering why I want to do this, instead of just embedding the code directly, e.g. int output = 1;. The reason is as follows: I've generated a reference for a SOAP webservice with svcutil, which because of the nested elements generates extremely long class names, which I'd like to avoid having to type out. So instead of
var o = await client.GetOrderAsync(request);
return new Order {
OrderDate = o.OrderDate,
...
Shipments = o.Shipment_Order == null ? new Shipment[0]
o.Shipment_Order.Select(sh => new Shipment {
ShipmentID = sh.ShipmentID,
...
Address = CreateAddress(sh.ReceiverAddress_Shipment);
}).ToArray()
};
and a separate CreateAddress(GetOrderResultOrderShipment_OrderShipmentShipment_Address address) method (real names are even longer, and I have very limited control about the form), I'd like to write
var o = await client.GetOrderAsync(request);
return new Order {
OrderDate = o.OrderDate,
...
Shipments = o.Shipment_Order == null ? new Shipment[0]
o.Shipment_Order.Select(sh => new Shipment {
ShipmentID = sh.ShipmentID,
...
Address = sh.ReceiverAddress_Shipment == null ? null : () => {
var a = sh.ReceiverAddress_Shipment.Address;
return new Address {
Street = a.Street
...
};
}()
}).ToArray()
};
I know I could write
Address = sh.ReceiverAddress_Shipment == null ? null : new Address {
Street = sh.ReceiverAddress_Shipment.Address.Street,
...
}
but even that (the sh.ReceiverAddress_Shipment.Address part) becomes very repetitive if there are many fields. Declaring a lambda and immediately calling it would be more elegant less characters to write.
Instead of trying to cast the lambda, I propose you use a small helper function:
public static TOut Exec<TIn, TOut>(Func<TIn, TOut> func, TIn input) => func(input);
which you could then use like this: int x = Exec(myVar => myVar + 2, 0);. This reads a lot nicer to me than the alternatives suggested here.
It's ugly, but it's possible:
int output = ((Func<int, int>)(input => { return 1; }))(0);
Anonymous functions, including lambda expressions, are implicitly convertible to a delegate that matches their signature, but this syntax requires the lambda to be enclosed in parentheses.
The above can be simplified as well:
int output = ((Func<int, int>)(input => 1))(0);
Lambda literals in C# have a curious distinction in that their meaning is dependent on their type. They are essentially overloaded on their return type which is something does not exist anywhere else in C#. (Numeric literals are somewhat similar.)
The exact same lambda literal can either evaluate to an anonymous function that you can execute (i.e. a Func/Action) or an abstract representation of the operations inside of the Body, kind of like an Abstract Syntax Tree (i.e. a LINQ Expression Tree).
The latter is, for example, how LINQ to SQL, LINQ to XML, etc. work: the lambdas do not evaluate to executable code, they evaluate to LINQ Expression Trees, and the LINQ provider can then use those Expression Trees to understand what the body of the lambda is doing and generate e.g. a SQL Query from that.
In your case, there is no way for the compiler to know wheter the lambda literal is supposed to be evaluated to a Func or a LINQ Expression. That is why Johnathan Barclay's answer works: it gives a type to the lambda expression and therefore, the compiler knows that you want a Func with compiled code that executes the body of your lambda instead of an un-evaluated LINQ Expression Tree that represents the code inside the body of the lambda.
You could inline the declaration of the Func by doing
int output = (new Func<int, int>(() => { return 1; }))(0);
and immediately invoking it.
You can also create the alias in the Select method
var o = await client.GetOrderAsync(request);
return new Order {
OrderDate = o.OrderDate,
...
Shipments = o.Shipment_Order == null ? new Shipment[0]
o.Shipment_Order.Select(sh => {
var s = sh.ReceiverAddress_Shipment;
var a = s.Address;
return new Shipment {
ShipmentID = sh.ShipmentID,
...
Address = s == null ?
null :
new Address {
Street = a.Street
...
}
};
}).ToArray()
};
or with the ?? operator
var o = await client.GetOrderAsync(request);
return new Order {
OrderDate = o.OrderDate,
...
Shipments = o.Shipment_Order?.Select(sh => {
var s = sh.ReceiverAddress_Shipment;
var a = s.Address;
return new Shipment {
ShipmentID = sh.ShipmentID,
...
Address = s == null ?
null :
new Address {
Street = a.Street
...
}
};
}).ToArray() ?? new Shipment[0]
};
If you don't mind violating a few of the extension methods design guidelines, extension methods combined with null-conditional operator ?. can take you reasonably far:
public static class Extensions
{
public static TOut Map<TIn, TOut>(this TIn value, Func<TIn, TOut> map)
where TIn : class
=> value == null ? default(TOut) : map(value);
public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> items)
=> items ?? Enumerable.Empty<T>();
}
will give you this:
return new Order
{
OrderDate = o.OrderDate,
Shipments = o.Shipment_Order.OrEmpty().Select(sh => new Shipment
{
ShipmentID = sh.ShipmentID,
Address = sh.ReceiverAddress_Shipment?.Address.Map(a => new Address
{
Street = a.Street
})
}).ToArray()
};
and if you mostly need arrays, then override ToArray extension method to encapsulate a few more method calls:
public static TOut[] ToArray<TIn, TOut>(this IEnumerable<TIn> items, Func<TIn, TOut> map)
=> items == null ? new TOut[0] : items.Select(map).ToArray();
resulting in:
return new Order
{
OrderDate = o.OrderDate,
Shipments = o.Shipment_Order.ToArray(sh => new Shipment
{
ShipmentID = sh.ShipmentID,
Address = sh.ReceiverAddress_Shipment?.Address.Map(a => new Address
{
Street = a.Street
})
})
};
I have a given linq-sql like this:
var erg = from p in m_session.Query<LovTestData>()
select new
{
SomeString = p.SomeString,
SomeOtherString = p.SomeOtherString
};
This should be the "base"-query for a Lov-Dialog. So this is the query which defines the content of the Lov.
But there are fields in the LOV to search. So this is the query I have to use to fill the Lov at runtime:
var erg = from p in m_session.Query<LovTestData>()
where ((string.IsNullOrEmpty(someStringValueFilter) || p.SomeString.ToLower().Contains(someStringValueFilter.ToLower())) &&
(string.IsNullOrEmpty(someOtherStringFilter) || p.SomeOtherString.ToLower().Contains(someOtherStringFilter.ToLower())))
select new
{
SomeString = p.SomeString,
SomeOtherString = p.SomeOtherString
};
So I wonder how its possible to "inject" the where clause afterwards into the given query? This is how I think it should look like:
var erg = from p in m_session.Query<LovTestData>()
select new
{
SomeString = p.SomeString,
SomeOtherString = p.SomeOtherString
};
var additionalWhere = ... //Some way to define this part: ((string.IsNullOrEmpty(someStringValueFilter) || p.SomeString.ToLower().Contains(someStringValueFilter.ToLower())) && (string.IsNullOrEmpty(someOtherStringFilter) || p.SomeOtherString.ToLower().Contains(someOtherStringFilter.ToLower())))
erg = InjectWhere(erg, additionalWhere); //In this function the where is inserted into the linq so the result is the second query.
Updated:
The additionalWhere should be constructed out of the original query. So its not possible for me to write "p.SomeString" because the construction of the additionalWhere is universal. This is the way I get the fields
Type elementType = erg.ElementType;
foreach (PropertyInfo pi in elementType.GetProperties())
{
//pi.name...
}
If Query returns IQueryable then there is no problem at all.
List<LovTestData> GetTestData(Expression<Func<T, bool>> where)
{
var erg = from p in m_session.Query<LovTestData>()
select new
{
...
}
IQueryable result = erg.Where(where);
return result.ToList();
}
Now. IQueryable will NOT EXECUTE unitl you really use it. So you can do select, where, union and so on, but until you use the IQueryable it won't do anything. Here the real SQL will run in: result.ToList(). That's why you can build all there conditions earlier. Of course assuming that m_session.Query returns IQueryable but as far as I remember - it does.
So you can even do this without result variable that I have created. Just operate on erg.
Waayd's comment will also work.
OK, now about creating filter dynamically.
Let's take a simple class:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Now, let's create a list of records - database.
List<Person> list = new List<Person>
{
new Person {Name = "Adam Abc", Age = 15},
new Person {Name = "John Abc", Age = 23},
new Person {Name = "Steven Abc", Age = 26},
new Person {Name = "Adam Bca", Age = 21},
new Person {Name = "Adam Xyz", Age = 26},
};
Now, let's prepare a filter. You have to get filter data from a view, now let's just simulate this:
string nameIs = "Adam";
bool createAgeFilter = true;
int ageFilterMin = 20;
int ageFilterMax = 25;
So we need all Adam's that are in age between 20 and 25. Let's create this condition:
First condition on name:
Func<Person, bool> whereName = new Func<Person, bool>((p) =>
{
if (!string.IsNullOrWhiteSpace(nameIs))
return p.Name.Contains(nameIs);
else
return true;
}
);
Next condition on age:
Func<Person, bool> whereAge = new Func<Person, bool>((p) =>
{
if (createAgeFilter)
return p.Age >= ageFilterMin && p.Age <= ageFilterMax;
else
return true;
}
);
Next, let's have our IQueryable:
IQueryable<Person> q = list.AsQueryable();
And finally let's add where clauses:
List<Person> filteredList = q.Where(whereName)
.Where(whereAge)
.ToList();
That's it. The idea behind this is that you have to create several partial where clauses. Each for one thing you want to filter. But what I've done at the end will make "AND" between the filters. If you would like to "OR" them, you should do it in one other filter type - like in age filter.
I've just came up with this idea. So there may be a better solution. Maybe even some one liner.
Edit
If you can't use linq like that, there is another way. But not so simple.
There MUST be a point somewhere in your application that you build filter in LINQ style. For example in your view. So take this expression and call ToString(). You will get string representation of the linq query.
The next thing you have to do is to install Roslyn package.
Finally you can change string representation of LINQ expression to LINQ expression using some Roslyn magic:
public async static Task<Expression<Func<T, bool>>> ExpressionFromStr<T>(string expressionStr)
{
var options = ScriptOptions.Default.AddReferences(typeof(T).Assembly);
return await CSharpScript.EvaluateAsync<Expression<Func<T, bool>>>(expressionStr, options);
}
Usings:
using Microsoft.CodeAnalysis.CSharp.Scripting; //roslyn
using Microsoft.CodeAnalysis.Scripting; //roslyn
using System;
using System.Linq.Expressions;
using System.Threading.Tasks; //for async and Task.
I am using Linq-to-Entities and one query requires a property to be loaded dynamically. The query looks like this:
var found = Context.IntegrateViews.GroupBy(x => x.TypeOfBed)
.Select(type => new TypeCounts
{ Name = type.Key, Count = type.Count() }).ToList();
The property on which Group By clause is being run is TypeOfBed. Now I want to run this query on different properties e.g., next time I want to run it on, let's say x.TypeOfChair and so on. For this, I need to have this property in there dynamically.
I am trying to write a Expression builder which is like this.
public Expression<Func<LookupFilterItem>> PropertyLambda(string propertyName)
{
var param = Expression.Parameter(typeof(LookupFilterItem), "lookupItem");
//type of x in above example is LookupFilterItem
var propertyExpression = Expression.Property(param, propertyName);
var returnExpr = Expression.Lambda<Func<LookupFilterItem>>(propertyExpression);
return returnExpr;
//If I Pass string "TypeOfBed" to this method, I am expecting it to return something like
// lookupItem => lookupItem.TypeOfBed
}
And I intend to use it like this:
var found = Context.IntegrateViews.GroupBy(PropertyLambda("TypeOfBed"))
.Select(type => new TypeCounts
{ Name = type.Key, Count = type.Count() }).ToList();
But this is not working, I am getting a compile time error which says:
error CS0411: The type arguments for method
'System.Linq.Queryable.GroupBy(System.Linq.IQueryable,
System.Linq.Expressions.Expression>)' cannot
be inferred from the usage. Try specifying the type arguments
explicitly.
What am I doing wrong here?
This seems to be a syntax issue. Try this:
public Expression<Func<LookupFilterItem, string>> PropertyLambda(string propertyName)
{
var param = Expression.Parameter(typeof(LookupFilterItem), "lookupItem");
var propertyExpression = Expression.Property(param, propertyName);
var returnExpr = Expression.Lambda<Func<LookupFilterItem, string>>(propertyExpression, param);
return returnExpr;
}
And the usage:
var found = Context.IntegrateViews.GroupBy(PropertyLambda("TypeOfBed").Compile())
.Select(type => new TypeCounts
{ Name = type.Key, Count = type.Count() }).ToList();
Another option is to use LINQ Dynamic Query Library :
using System.Linq.Dynamic;
var found = Context.IntegrateViews.AsQueryable().GroupBy("new(TypeOfBed)", "it")
.Select(new (Key.TypeOfBed as Name, Count() as Count)).ToList();
If I have a LINQ statement like
x = Table.SingleOrDefault(o => o.id == 1).o.name;
how can I replace "id" and "name" with passed in variables using reflection? I keep getting object reference not set to instance of an object errors when I try. My attempts are things like
x = (string)Table.SingleOrDefault(o => (int?)o.GetType().GetProperty(idString)
.GetValue(o, null) == 1).GetType().GetField(nameString).GetValue(x);
Any help would be great. Thanks.
You should use Expression Trees instead of reflection. It will perform better, and you'll be able to use it with both LINQ to Objects and LINQ to SQL/Entities.
var source = new List<Test> { new Test { Id = 1, Name = "FirsT" }, new Test { Id = 2, Name = "Second" } };
var idName = "Id";
var idValue = 1;
var param = Expression.Parameter(typeof(Test));
var condition =
Expression.Lambda<Func<Test, bool>>(
Expression.Equal(
Expression.Property(param, idName),
Expression.Constant(idValue, typeof(int))
),
param
).Compile(); // for LINQ to SQl/Entities skip Compile() call
var item = source.SingleOrDefault(condition);
then, you can get Name property using reflection (you'll do it just once, so it's fine to do it using reflection.
var nameName = "Name";
var name = item == null ? null : (string) typeof(Test).GetProperty(nameName).GetValue(item);
Test class is defined as
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
}
You can't use reflection, but you can use dynamic Linq as described here. If you are using Entity Framework for this, you should also be able to use Entity SQL, which is basically a hard-coded SQL string.