how to turn a string into a linq expression? - c#

Similar: Convert a string to Linq.Expressions or use a string as Selector?
A similar one of that one: Passing a Linq expression as a string?
Another question with the same answer: How to create dynamic lambda based Linq expression from a string in C#?
Reason for asking something which has so many similar questions:
The accepted answer in those similar questions is unacceptable in that they all reference a library from 4 years ago (granted that it was written by code master Scott Gu) written for an old framework (.net 3.5) , and does not provide anything but a link as an answer.
There is a way to do this in code without including a whole library.
Here is some sample code for this situation:
public static void getDynamic<T>(int startingId) where T : class
{
string classType = typeof(T).ToString();
string classTypeId = classType + "Id";
using (var repo = new Repository<T>())
{
Build<T>(
repo.getList(),
b => b.classTypeId //doesn't compile, this is the heart of the issue
//How can a string be used in this fashion to access a property in b?
)
}
}
public void Build<T>(
List<T> items,
Func<T, int> value) where T : class
{
var Values = new List<Item>();
Values = items.Select(f => new Item()
{
Id = value(f)
}).ToList();
}
public class Item
{
public int Id { get; set; }
}
Note that this is not looking to turn an entire string into an expression such as
query = "x => x.id == somevalue";
But instead is trying to only use the string as the access
query = x => x.STRING;

Here's an expression tree attempt. I still don't know if this would work with Entity framework, but I figure it is worth a try.
Func<T, int> MakeGetter<T>(string propertyName)
{
ParameterExpression input = Expression.Parameter(typeof(T));
var expr = Expression.Property(input, typeof(T).GetProperty(propertyName));
return Expression.Lambda<Func<T, int>>(expr, input).Compile();
}
Call it like this:
Build<T>(repo.getList(), MakeGetter<T>(classTypeId))
If you can use an Expression<Func<T,int>> in place of a just a Func, then just remove the call to Compile (and change the signature of MakeGetter).
Edit:
In the comments, TravisJ asked how he could use it like this: w => "text" + w.classTypeId
There's several ways to do this, but for readability I would recommend introducing a local variable first, like this:
var getId = MakeGetter<T>(classTypeId);
return w => "text" + getId(w);
The main point is that the getter is just a function, and you can use it exactly like you normally would. Read Func<T,int> like this: int DoSomething(T instance)

Here is an extension method for you with my testing code (linqPad):
class test
{
public string sam { get; set; }
public string notsam {get; set; }
}
void Main()
{
var z = new test { sam = "sam", notsam = "alex" };
z.Dump();
z.GetPropertyByString("notsam").Dump();
z.SetPropertyByString("sam","john");
z.Dump();
}
static class Nice
{
public static void SetPropertyByString(this object x, string p,object value)
{
x.GetType().GetProperty(p).SetValue(x,value,null);
}
public static object GetPropertyByString(this object x,string p)
{
return x.GetType().GetProperty(p).GetValue(x,null);
}
}
results:

I haven't tried this, and not sure if it would work, but could you use something like:
b => b.GetType().GetProperty(classTypeId).GetValue(b, null);

Related

Get string path properties from object path clearly in C#

i need to have this result :
ProgrammeEtude.Description
So, i have done something like that and it work
modelMember = $"{nameof(Gabarit.ProgrammeEtude)}.{nameof(Gabarit.ProgrammeEtude.Description)}";
But it's ugly and if we have more than one class to reach, it will not be clean. So, i would like to know if it's possible to create a function to get the fullName property without the first class clearly. Only by calling a function
// Like that
modelMember = typeof(ProgrammeEtude).GetPropertyFullName(nameof(ProgrammeEtude.Description));
// Or like that
modelMember = GetPropertyFullName(ProgrammeEtude.Description);
Thank you!
Final solution help By Ecoron :
public void Test1()
{
var result = NameOf<Gabarit>(x => x.ProgrammeEtude.Description);
}
public static string NameOf<T>(Expression<Func<T, object>> selector)
{
return string.Join(".", selector.ToString().Split('.').Skip(1));
}
You can do it in runtime:
public class SomeClass
{
public SomeClass Other;
}
public class Tests
{
[Test]
public void Test1()
{
var result = NameOf<SomeClass>(x => x.Other.Other.Other);
}
public static string NameOf<T>(Expression<Func<T,object>> selector)
{
const string joinWith = ".";
return nameof(T) + joinWith + string.Join(joinWith, selector.ToString().Split('.').Skip(1));
}
}
Result: SomeClass.Other.Other.Other
You can play with this function to get desired result - with/out namespaces/indexes/separation select just start or end or skip something, etc.
Be aware that this working great only if you don't use some funky variables/enums inside which accessed by dot. For more correct version you should traverse expression yourself, but in this example Im just kinda lazy to write this all, and better to use simple approach.

LINQ to Entities and polymorphism

Let's say I have a simple model:
public class Movie
{
public int ID { get; set; }
public string Name { get; set; }
}
And a DbContext:
public class MoviesContext : DbContext
{
...
public DbSet<Movie> Movies { get; set; }
}
Also I have a method in MoviesContext class that filters Movies by substring like this:
return Movies.Where(m => m.Name.Contains(filterString)).Select(m => m);
Now suppose I'd like to add a new model, say:
public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string FullName { get { return FirstName + (MiddleName?.Length > 0 ? $" {MiddleName}" : "") + $" {LastName}"; } }
}
I also want to filter persons (DbSet Persons) by name (i.e. FullName). I'd like DRY, so it is preferrable to generalize a filter method of MoviesContext. And, what is important, I'd like to do filtering on the database level. So I have to deal with LINQ for Entities.
If not for this, the task is pretty simple. I could use an abstract class and add a virtual method that do the "contains substring" logic. Alternatively, I could use an interface. Unfortunately, because of LINQ for Entities, I can't use a FullName property (which is not convenient but bearable) and I can't write something like this:
return dbset.Where(ent => ent.NameContains(filterString)).Select(ent => ent);
So, how to solve this problem? I've found some solution (almost have my head broken), but I am not very happy with it. I'll post my solution separately, but I hope there is a more elegant one.
Reading your code a little more closely, instead of your NameFilterable abstract class, could you not do something like this:
public interface IHasPredicateGetter<T> {
[NotNull] Expression<Func<T, bool>> GetPredicateFromString([NotNull] string pValue);
}
public class Movie : IHasPredicateGetter<Movie> {
public int ID { get; set; }
public string Name { get; set; }
public Expression<Func<Movie, bool>> GetPredicateFromString(string pValue) {
return m => m.Name.Contains(pValue);
}
}
This prevents you from needing a cast, for example. It's so hard to grasp just what you're trying to do here, so I'm not sure this is the whole thing or not. You're still stuck with an instance method that should probably be a static method, but it couldn't implement an interface otherwise.
My solution looks like this.
[1] The base class:
public abstract class NameFilterable
{
protected static Expression<Func<T, bool>> False<T>() { return f => false; }
public virtual Expression<Func<T, bool>> GetNameContainsPredicate<T>(string filterString)
{
return False<T>();
}
}
[2] The Person class (I'll omit the Movie class, it is more simple):
public class Person : NameFilterable
{
...
public override Expression<Func<T, bool>> GetNameContainsPredicate<T>(string filterString)
{
return entity =>
String.IsNullOrEmpty(filterString) ||
(entity as Person).LastName.Contains(filterString) ||
(entity as Person).FirstName.Contains(filterString) ||
(((entity as Person).MiddleName != null) && (entity as Person).MiddleName.Contains(filterString))
;
}
}
[3] The filter methods in MoviesContext:
private static IQueryable<T> _filterDbSet<T>(DbSet<T> set, Expression<Func<T, bool>> filterPredicate) where T : class
{
return set
.Where(filterPredicate)
.Select(ent => ent);
}
private static IQueryable<T> _filterDbSet<T>(DbSet<T> set, string search = null) where T : NameFilterable, new()
{
T ent = new T();
return _filterDbSet<T>(set, (ent as NameFilterable).GetNameContainsPredicate<T>(search));
}
public static ICollection<T> Filter<T>(DbSet<T> set, string search = null) where T : NameFilterable, new()
{
return _filterDbSet(set, search).ToList();
}
And it seems that all this works pretty well. But I can't say it is very elegant.
[1] I have to use a generic T, though on the Person level I always work with Person objects (or descendants). So I have to convert T to Person (as Person).
[2] In GetNameContainsPredicate method, I can't write (because of LINQ for Entities):
return entity =>
{
Person p = entity as Person;
String.IsNullOrEmpty(filterString) ||
p.LastName.Contains(filterString) ||
p.FirstName.Contains(filterString) ||
((p.MiddleName != null) && p.MiddleName.Contains(filterString))
};
[3] I can't use static methods (statics couldn't be overridden), so I have to create a dummy T object (T ent = new T();).
[4] I still can't use a FullName.Contains(filterString)
So, the question remains: Maybe I miss something and there is a more elegant solution to the problem?
You could create a method that is responsible for search if a type has a particular property and filter for that property,if the object has not the property simply return null. Whith this you can create an expression that filters for this property
//gets the property info of the property with the giving name
public static PropertyInfo GetPropetyInfo<T>(string name)
{
var type = typeof(T);
var property = type.GetProperty(name);
return property;
}
//Creates an expression thats represents the query
public static Func<T, bool> GetFilterExpression<T>( string propertyName, object propertyValue)
{
var prop = GetPropetyInfo<T>(propertyName);
if(prop==null)return t=>false;
var parameter = Expression.Parameter(typeof(T), "t");
Expression expression = parameter;
var left = Expression.Property(expression, prop);
if (prop.PropertyType == typeof(string))
{
var toLower = typeof(string).GetMethods().FirstOrDefault(t => t.Name.Equals("ToLower"));
var tlCall = Expression.Call(left, toLower);
var right = Expression.Constant(propertyValue.ToString().ToLower());
var contains = Expression.Call(tlCall, typeof(string).GetMethod("Contains"), right);
var containsCall = Expression.IsTrue(contains);
expression = Expression.AndAlso(Expression.NotEqual(left, Expression.Constant(null)), containsCall);
}
else
{
if (prop.PropertyType.ToString().ToLower().Contains("nullable"))
{
var getValue = prop.PropertyType.GetMethods().FirstOrDefault(t => t.Name.Equals("GetValueOrDefault"));
var getValueCall = Expression.Call(left, getValue);
var right = Expression.Constant(propertyValue);
expression = Expression.Equal(getValueCall, right);
}
else
{
var value = Convert.ChangeType(propertyValue,prop.PropertyType);
var right = Expression.Constant(value);
expression = Expression.Equal(left, right);
}
}
return Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { parameter }).Compile();
}
The you can use it as follow
var expression = YOURCLASS.GetFilterExpression<Person>("LastName", "Jhon");
var result=dbset.Where(expression);
There are a few things I've done to get polymorphism with EF, but in your specific case of wanting reusable filters, I'm not sure it's worth the trouble. I've basically tried to do the same exact thing, but every time I end up realizing that there's no point. Ask yourself: what exactly are the benefits of doing this, and how is it any more flexible than what a Where clause already offers?
There are two issues. One is that it's hard or nigh impossible to get a filter to be used between two separate classes by using a shared interface (INamedObject for example). This is because you need a strongly typed expression. You can make a function that returns a strongly typed expression, but why would you not have just wrote the expression in the first place? The other issue is that you need a new filter expression for every search value, which is pretty close to where we are already.
If you perfected this, what would you have? The ability to infer type, specify a search value, and get an expression you could use? Isn't that essentially what we already have? The way Where clauses already are, they already have strong typing, and the ability to use dynamic search values. While it might feel a tiny bit redundant to say x => x.Name == value in more than one place, really the ability to specify such a concise and powerful filter statement is already a pretty amazing place to be.

Would reflection be the best method of searching for a value of a specific property in a list of Objects?

I have a Class with the following:
public class TestClass {
string Account1 {get;set;}
string Account2 {get;set;}
string Account3 {get;set;}
}
What I would like is to be able to have a method that is similar to the following:
public TestClass[] GetTestClass(string value, string AccountName)
where i can pass in a value say "John" and the AccountName would be "Account1"
and it will go through a list of TestClass and return an array or list of TestClass objects where there exists a value "John" in the Property "Account1"
Is there a better method of doing this or any thoughts would help.
Note: This is a model based of a SQL Table
You can use the reflection to get what you want, Your method will look like this,
public List<TestClass> GetTestClass(string value, string AccountName)
{
foreach(TestClass test in yourListOfTestClass)
{
if (test.GetType().GetProperty(AccountName).GetValue(test, null).Equals(value))
listToReturn.Add(test);
}
return listToReturn
}
Note - Code is not tested. Might have synatx error.
You can send in a method for accessing the property instead of the property name:
public TestClass[] GetTestClass(string value, Func<TestClass, string> getAccountName) {
return accounts.Where(x => getAccountName(x) == value).ToArray();
}
Usage:
TestClass[] johnsAccounts = GetTestClass("John", a => a.Account1);
Or simply use it directly:
TestClass[] johnsAccounts = accounts.Where(a => a.Account1 == "John").ToArray();
Is there a better method of doing this or any thoughts would help.
You can use List of strings instead of multiple string variables as you are returning array from GetTestClass. It will keep it simple as well.
public class TestClass
{
List<string> Accounts = new List<string>();
}
Now you will only pass the value to method. Using the LinQ will filter out the desired string list using Where.
public List<string> GetTestClass(string value)
{
return Accounts.Where(account => account == value).ToList();
}
Edit The OP want two things AccountName and the Name, this would require to have a account class instead of string.
class Account
{
string AccountName { get; set; }
string Name { get; set; }
}
public class TestClass
{
List<Account> Accounts = new List<Account>();
}
public Account GetTestClass(Account account)
{
return Accounts.Where(account => a.AccountName == account.AccountName && a.Name == account.Name).FirstOrDefault();
}
Yes you can do this.
Take a look into Reflection. This should get you started.
Example:
public TestClass[] GetTestClass(string value, string AccountName)
{
var propertyInfo = typeof(TestClass).GetProperty(AccountName);
var list = new List<TestClass>();
foreach(var tc in [YOUR_OBJECTS])
{
if(propertyInfo.GetValue(tc, null) == value)
{
list.add(tc);
}
}
return list.ToArray();
}
Well, you can achieve that using Reflection. Though, I don't think I would do that this way, since there are other ways to implement what you want differently.
A better way in my opinion would be to create a type-safe enum that would do that. In the members of the type-safe enum, you could specify an action that would run against a 'TestClass' object.
You would be able to call 'GetTestClass' in this way:
var accounts1 = GetTestClass(testClassesCollection, "john", TestProperties.Account1);
var accounts2 = GetTestClass(testClassesCollection, "john", TestProperties.Account2);
var accounts3 = GetTestClass(testClassesCollection, "john", TestProperties.Account3);
Where 'testClassesColleciton' is the collection of 'TestClass' that you have. You could remove this argument if the method is an object member.
The type-safe enum is implemented this way:
public sealed class TestProperties
{
public static readonly TestProperties Account1 = new TestProperties((t, name) => t.Account1 == name);
public static readonly TestProperties Account2 = new TestProperties((t, name) => t.Account2 == name);
public static readonly TestProperties Account3 = new TestProperties((t, name) => t.Account3 == name);
private Func<TestClass, string, bool> _checkFunc;
private TestProperties(Func<TestClass, string, bool> func)
{
_checkFunc = func;
}
public bool IsApplicable(TestClass test, string name)
{
return _checkFunc(test, name);
}
}
Then, you can implement the GetTestClass this way:
public TestClass[] GetTestClass(IEnumerable<TestClass> testClasses, string value, TestProperties property)
{
return testClasses.Where( t => property.IsApplicable(t)).ToArray();
}
I wouldn't use reflection because of it's performance overhead and it's maintenance danger. With a plain simple reflection, we will pass the property's name as a string parameter. What would happen if somebody has changed the property's name in the class? Even with Visual Studio refactoring (ctrl + R, R), the string parameter value will not be updated. Since the code will compile normally, the bug will be discovered only at run-time.
I agree with #Rouby that a plain simple reflection will be faster to develop than the type-safe enum way that I have suggested. Though, personally, I don't think that it will be that much cheaper (development-cost wise) and I also think that the potentially dangerous scenario that I talked about earlier (regarding refactoring) would have a bigger development-cost penalty, specially, when it is a legacy/old code.

Linq Expression to String

Here's a simple method in MVC that converts into html tag.
#Html.HiddenFor(model => model.myName);
This is converted into the below html.
<input type="hidden" name="myName" value="ABC..." />
How this expression is converted into the string. Let's suppose If I want to write this kind of method, how can I know the property name and its value. Suppose from the HiddenFor argument, how myName & myName value will be extracted.
This is not specific to MVC but related to LINQ expression.
Thanks a lot in advance.
This has nothing to do with LINQ (except that LINQ makes heavy use of those "expression trees").
model => model.MyProperty is a lambda expression which can be parsed when treated as an Expression<Func<Model, T>> instead of just a Func<Model, T>.
Let me answer your question by a (mostly self-explanatory) example:
public static string GetPropertyName<T>(Expression<Func<Model, T>> expr)
{
var member = (MemberExpression)expr.Body;
var property = (PropertyInfo)member.Member;
return property.Name;
}
which can be used as follows:
public class Model
{
public int MyProperty { get; set; }
}
static void Main(string[] args)
{
// Prints "MyProperty"
Console.WriteLine(GetPropertyName(model => model.MyProperty));
}
Have a look at the ExpressionHelper class in the Mvc source
https://github.com/aspnet/Mvc/blob/9c545aa343ccb1bf413888573c398fe56017d9ee/src/Microsoft.AspNet.Mvc.Core/Rendering/Expressions/ExpressionHelper.cs
The method GetExpressionText converts the lambda expression in question
Parsing expressions is hard. I had the same issue and there is a much simpler solution.
Although it is not as elegant, it is quick, easy, short and it handles subclasses.
Expression.Body.ToString() actually works and returns the whole expression as a string. The hardest work is to process the string afterward:
class Program
{
public class Car
{
public int Id;
public CarModel Model;
public string Owner ;
}
public class CarModel
{
public string Name;
public string Brand;
}
public static void Main(string[] args)
{
Console.WriteLine(ExpToString(x => x.Id));
Console.WriteLine(ExpToString(x => x.Owner));
Console.WriteLine(ExpToString(x => x.Model));
Console.WriteLine(ExpToString(x => x.Model.Name));
Console.WriteLine(ExpToString(x => x.Model.Brand));
}
// The inelegant solution that works
public static string ExpToString<T>(Expression<Func<Car, T>> exp)
{
var s = exp.Body.ToString();
return s.Remove(0, s.IndexOf('.') + 1);
}
}
The output:
Id
Owner
Model
Model.Name
Model.Brand
I am actually using this in WinForms for data binding initializations and I am pretty satisfied with it.

Given this lambda, how can I write it manually with expression trees?

class Program
{
static void Main(string[] args)
{
Expression<Func<string[], Poco>> exp = a => new Poco { MyProperty1 = a[0], MyProperty2 = a[1], MyProperty3 = a[2] };
var lambda = exp.Compile();
var output = lambda(new[] {"one", "two", "three"});
Console.WriteLine(output.MyProperty1);
}
}
class Poco
{
public string MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
public string MyProperty3 { get; set; }
}
I'm not interested in the part calling the lambda, thats just for completeness. I get completely lost trying to navigate expression trees, and this might teach me how to fish.
private static Expression<Func<string[], Poco>> CreateExpr()
{
ParameterExpression paramExpr = Expression.Parameter(typeof(string[]), "a");
var newExpr = Expression.New(typeof(Poco));
var memberExprs = Enumerable.Range(0, 3)
.Select(i =>
{
string propertyName = "MyProperty" + (i + 1);
var property = typeof(Poco).GetProperty(propertyName);
Expression.Bind(property, Expression.ArrayIndex(paramExpr, Expression.Constant(i)));
});
var expr = Expression.MemberInit(newExpr, memberExprs);
return Expression.Lambda<Func<string[], Poco>>(expr, paramExpr);
}
I don't have time right now to translate the complete tree, but one thing you can do is compile your code and then use ildasm (or reflector etc) to look at what the compiler's doing. You can't always do exactly the same in your own code, but it gives you an idea of the kind of expressions you'll want. In particular, in this case you'll want:
Expression.Parameter to create the parameter (a)
Expression.New to create the new instance
Expression.Bind to create a property assignment
Expression.MemberInit to assign the properties in the new object
Expression.ArrayIndex for each array access (a[0] etc)
Expression.Constant for the array indexes themselves (0, 1, 2)
Expression.Lambda to create an Expression<TDelegate> for the whole thing
If I get time later on, I'll try to construct a complete working example.

Categories