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.
Related
I'm trying to determine the MemberExpression of all the parts of a structure with the use of reflection. These are some of the objects to illustrate the issue:
public class Entity
{
public Part FirstPart { get; set; }
}
public class Part
{
public int Id { get; set; }
}
public class SubPart : Part
{
public int ExtraProperty { get; set; }
}
The function I used to determine the MemberExpression of every component, works fine for the following object structure:
Entity entity = new Entity() { FirstPart = new Part() { Id = 1 } };
The function is:
var param = Expression.Parameter(entity.GetType());
String[] childProperties = ("FirstPart.Id").Split('.');
var propExpression = Expression.PropertyOrField(param, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
}
But this doesn't work for the following, due to inheritance:
Entity entity = new Entity() { FirstPart = new SubPart() { ExtraProperty = 1 } };
In order to retrace the properties we need to change the path to "FirstPart.ExtraProperty":
var param = Expression.Parameter(entity.GetType());
String[] childProperties = ("FirstPart.ExtraProperty").Split('.');
var propExpression = Expression.PropertyOrField(param, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
}
The error message states that: 'ExtraProperty' is not a member of Part. Does anyone have an idea how to overcome this issue?
You can't. Think of expressions as code, which is compiled at runtime instead of compile time. There is no magic and similar rules apply (expressions are low level and more restrictive, so many syntactic sugar which are available at C# code level are not available in expressions). Saying that, since entity.FirstPart.ExtraProperty is not valid in C# code, it won't be valid in expressions as well.
You could insert explicit cast - but then you assume that instance will actually be of type SubPart, so why don't you define member FirstPart of type SubPart instead of Part. Or you could create type test logic using TypeIs expression and then cast the same way as you would in C# code.
EDIT:
After rereading your problem, I see that what you actually are trying to implement is property walker over arbitrary objects. So TypeIs expression will not help you here since it requires that type, which you are testing against, is known at compile time. But in your case there can be arbitrary class derived from Part in FirstPart member with arbitrary additional properties. In this case, there is no other option, but to evaluate each property access one by one and retrieve actual type from intermediate values. For example:
Entity entity = new Entity() { FirstPart = new SubPart() { ExtraProperty = 1 } };
object currentObjectInChain = entity;
String[] childProperties = ("FirstPart.ExtraProperty").Split('.');
foreach (var property in childProperties)
{
if (currentObjectInChain == null)
{
throw new ArgumentException("Current value is null");
}
var type = currentObjectInChain.GetType();
var param = Expression.Parameter(type);
var lambda = Expression.Lambda(
Expression.PropertyOrField(param, property),
param).Compile(); // cache based on type and property name
currentObjectInChain = lambda.DynamicInvoke(currentObjectInChain);
}
At the end of loop currentObjectInChain will hold your value.
I need an efficient way for summing arbitrary properties in two objects of the same type.
I have a class with a large number of properties of different numerical types:
public class MyClass
{
public int Field1 { get; set; }
public long Field2 { get; set; }
public float Field3 { get; set; }
public double Field4 { get; set; }
//...
public uint Field100 { get; set; }
}
At runtime, I allow users to select an arbitrary subset of those fields:
List<PropertyInfo> props = new List<PropertyInfo>();//Field1, Field5, Field99 etc...
I then need to iterate over all selected properties on two objects, sum them, and assign back to the first object:
MyClass mc1 = new MyClass();
MyClass mc2 = new MyClass();
SumProps(mc1, mc2, props);
Which for the example above using fields 1, 5 and 99, would have the effect of doing:
mc1.Field1 += mc2.Field1;
mc1.Field5 += mc2.Field5;
mc1.Field99 += mc2.Field99;
I am currently using reflection with PropertyInfo.GetValue()/SetValue() and manually casting to the appropriate type. This is far too slow since this is a performance critical part of the code that will get called billions of times.
So I need a way of generating an Expression Lambda which will generate the code for summing all requested fields, using the appropriate types. I will then call that lambda like:
MySumPropsLambda(c1, c2);
From what I've researched it will involve BlockExpression and Expression.AddAssign but I can't quite wrap my head around how to actually accomplish it.
I am using Visual Studio 2013 with .NET 4.5.1
Edit: Thanks to Akash Kava below, I've made a slight modification and am using this solution:
public static Action<T, T> MakePropertySummationAction<T>(PropertyInfo[] props)
{
var mc1 = Expression.Parameter(typeof(T));
var mc2 = Expression.Parameter(typeof(T));
var exps = new List<Expression>();
foreach (var pi in props)
{
var p1 = Expression.Property(mc1, pi.Name);
var p2 = Expression.Property(mc2, pi.Name);
exps.Add(Expression.AddAssign(p1, p2));
}
var blockExpr = Expression.Block(exps);
return Expression.Lambda<Action<T, T>>(blockExpr, mc1, mc2).Compile();
}
Printing the block expression strings proves it's generated the desired code:
(Param_0.Field1 += Param_1.Field1)
(Param_0.Field2 += Param_1.Field2)
(Param_0.Field3 += Param_1.Field3)
(Param_0.Field4 += Param_1.Field4)
Performance for doing one million summations in milliseconds:
Direct took 4.8ms
Compiled lambda took 177.5ms
Reflection took 5376.7ms
Working fiddle
https://dotnetfiddle.net/xPrXXG
mc1.Field1 += mc2.Field2;
Lambda Expression equivalent is....
Action<MyClass,MyClass> CreateMethod(string propertyName){
var mc1 = Expression.Parameter(typeof(MyClass));
var mc2 = Expression.Parameter(typeof(MyClass));
var p1 = Expression.Property(mc1, propertyName);
var p2 = Expression.Property(mc2, propertyName);
var assign = Expression.AddAssign(p1,p2);
return Expression.Lambda<Action<MyClass,MyClass>>
(assign,mc1,mc2).Compile();
}
You can call as...
var addAssign1 = CreateMethod("Field1");
// equivalent to mc1.Field1 += mc2.Field1;
addAssign1(mc1,mc2);
What you're looking for is operator overloading:
Example, say you have T a, and T b,
you could do:
public class T{
public int Field1{get;set;}
public static T operator +(T c1, T c2)
{
return new T{Field1=c1.Field1+c2.Field1;}
}
}
and then you can do:
T a,b; //initilaize here
T c= a+b;
To get a new compiled value as the sum of a and b
Edit : Formatting is awful
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.
I have such class:
public class SomeClass
{
public string Text1 { get; set; }
public string Text2 { get; set; }
public int Number { get; set; }
}
And I have list of this classes objects:
List<SomeClass> myList = new List<SomeClass>();
I want to query this list using LINQ (lambda syntax):
var result = myList.Where(obj => obj.Text1 == "SomeString");
Is there any way to pass property(eg. by string name), by which I want this LINQ query to be performed? In this example, I search by Text1 property, but let's say I want to invoke this search dynamically on Text1 or Text2 (determined in runtime). I want to be able to pass property name, on which this search is performed, and check whether this property is string, so that I'm sure this search CAN be performed first.
Is that possible? I know Reflections and Expressions have something to do about it, but I don't know them very well.
Thanks
The approach using Reflection:
var result = myList.Where(obj => obj.GetType()
.GetProperty("Text1")
.GetValue(obj)
.Equals("SomeString"));
With this way you can change from "Text1" to "Text2" property.
Another approach you can use dynamic linq:
var result = myList.AsQueryable().Where("Text1=#0", "SomeString");
Dynamic LINQ is also available via nuget.
You could use expression-trees?
string memberName = "Text1", value = "SomeString";
var p = Expression.Parameter(typeof(SomeClass), "obj");
var predicate = Expression.Lambda<Func<SomeClass, bool>>(
Expression.Equal(
Expression.PropertyOrField(p, memberName),
Expression.Constant(value,typeof(string))
), p);
var result = myList.AsQueryable().Where(predicate);
or alternative for the last line:
var result = myList.Where(predicate.Compile());
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);