NHibernate UpdateBuilder.Set create lambda expression by propery info c# - c#

I want to create generic lambda expression according to property info
var pInfo = typeof(User).GetProperty("Counter");
var parameter = Expression.Parameter(typeof(User), #"source");
var property = Expression.Property(parameter, pInfo);
var lambda = Expression.Lambda<Func<User, object>>(property, parameter); // this line
//should be created according to the property type
//Expression.Lambda<Func<User, int>>
//Expression.Lambda<Func<User, string>>
//Expression.Lambda<Func<User, bool>>
//update fields in db nhibernate
updateBuilder.Set(lambda, Convert.ChangeType(pu.Value, pInfo.PropertyType, null));
Is there a way to create lambda expression when type is unknown without if syntax for each property?
Thank you

Well, I am not 100% sure what exactly you are trying to do, may be you should edit the question to explain in details.
But, following function will give you the generic lambda from property name as string. You can easily extract string name from property info. Hope this helps you.
public static Expression<Func<TEntity, object>> GetLambdaFromString<TEntity>(string property) where TEntity : BaseEntity
{
var x = System.Linq.Expressions.Expression.Parameter(typeof(TEntity), "x");
var body = System.Linq.Expressions.Expression.Convert
(
System.Linq.Expressions.Expression.Property(x, property), typeof(object)
);
var lambda = System.Linq.Expressions.Expression.Lambda<Func<TEntity, object>>(body, x);
return lambda;
}

Related

Lambda expression to initialize class with parameter and properties

I have a method that is used to generate a NewExpression.
public NewExpression CreateNewExpression( Expression<Func<T>> expression )
{
return expression.Body as NewExpression;
}
You can then do stuff like this.
CreateNewExpression( () => new MyType() );
This is later on used to create a delegate the can be executed to create an object.
var body = Expression.MemberInit( constructorExpression, bindings );
var funcType = typeof( Func<> ).MakeGenericType( objectType );
var lambda = Expression.Lambda( funcType, body ).Compile();
This works great, but now I would like to pass some data into the method so it can be used in the construction of the object.
public NewExpression CreateNewExpression( Expression<Func<T, Data>> expression )
This would then be used like this.
CreateNewExpression( data => new MyType( data.Id ) );
I can't for the life of me figure out how to create the proper expression to pass the data in for the constructor to use.
If I don't extract the body from the expression in CreateNewExpression and do Expression.Invoke, I don't know how to set the properties to it after.
If I extract the body, I can set the properties using Expression.MemberInit, but then I can't figure out how to pass the data to it.
How can I do this?
When you want to use an expression that contains ParameterExpression, you need to reuse that same ParameterExpression in the final expression too.
To do this, you can access the Parameters of the original expression and then pass the parameter to Expression.Lambda(). The result could look something like this:
public Delegate M<T>(Expression<Func<Data, T>> expression)
{
var objectType = typeof(T);
var constructorExpression = (NewExpression)expression.Body;
var constructorParameterExpression = expression.Parameters.Single();
var bindings = …;
var body = Expression.MemberInit( constructorExpression, bindings );
var funcType = typeof( Func<,> ).MakeGenericType( typeof(Data), objectType );
var lambda = Expression.Lambda( funcType, body, constructorParameterExpression )
.Compile();
return lambda;
}
I think what could be the answer to your problem is Expression.Parameter.
I found a blog entry on geeks with blogs which utilizes this. The only downside of this solution is that you'd have to create new overloads for when you need more than three arguments. But I guess this could be easily solved with params object[].

Dynamic select projection expression

I have the following class, for which usage is not important. What is important is method SetCacheItemSelector which takes one parameter, a select expression that projects Account entity to AccountCacheDTO:
public class AccountRepositoryCache : RepositoryCache<Account, AccountCacheDTO>
{
public AccountRepositoryCache()
{
SetCacheItemSelector(x => new AccountCacheDTO
{
Id = x.Id,
Login = x.Login
});
}
}
So signature for this method is:
public void SetCacheItemSelector(Expression<Func<TEntity, TCacheItem>> selector)
In this case, TEntity is Account class, and TCacheItem is AccountCacheDTO class.
Is there a way to use reflection to build select expression dynamically for all the properties that are matching for both Account class and AccountCacheDTO class?
Goal is to have method that would look like this:
public Expression<Func<TEntity, TCacheItem>> BuildSelector<TEntity, TCacheItem>()
{
... // implementation with reflection goes here
}
EDIT:
Here is final implementation (pretty much the same as the accepted answer):
public static Expression<Func<TSource, TTarget>> BuildSelector<TSource, TTarget>()
{
Type targetType = typeof(TTarget);
Type sourceType = typeof(TSource);
ParameterExpression parameterExpression = Expression.Parameter(sourceType, "source");
List<MemberBinding> bindings = new List<MemberBinding>();
foreach (PropertyInfo sourceProperty in sourceType.GetProperties().Where(x => x.CanRead))
{
PropertyInfo targetProperty = targetType.GetProperty(sourceProperty.Name);
if (targetProperty != null && targetProperty.CanWrite && targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
MemberExpression propertyExpression = Expression.Property(parameterExpression, sourceProperty);
bindings.Add(Expression.Bind(targetProperty, propertyExpression));
}
}
NewExpression newExpression = Expression.New(targetType);
Expression initializer = Expression.MemberInit(newExpression, bindings);
return Expression.Lambda<Func<TSource, TTarget>>(initializer, parameterExpression);
}
I didn't test it, but you should be able to do something like: This is just to convey a general idea and you should be able to tweak it for your requirements.
public Expression<Func<TEntity, TCacheItem>> BuildSelector<TEntity, TCacheItem>(TEntity entity)
{
List<MemberBinding> memberBindings = new List<MemberBinding>();
MemberInitExpression body = null;
foreach (var entityPropertyInfo in typeof(TEntity).GetProperties())
{
foreach (var cachePropertyInfo in typeof(TCacheItem).GetProperties())
{
if (entityPropertyInfo.PropertyType == cachePropertyInfo.PropertyType && entityPropertyInfo.Name == cachePropertyInfo.Name)
{
var fieldExpressoin = Expression.Field(Expression.Constant(entity), entityPropertyInfo.Name);
memberBindings.Add(Expression.Bind(cachePropertyInfo, fieldExpressoin));
}
}
}
var parameterExpression = Expression.Parameter(typeof(TEntity), "x");
var newExpr = Expression.New(typeof(TCacheItem));
body = Expression.MemberInit(newExpr, memberBindings);
return Expression.Lambda<Func<TEntity, TCacheItem>>(body, parameterExpression);
}
Of course, the #Aravol's answer can make sense, but it is a little different which required in OP. Here is the solution which is more suitable to OP requirement.
public Expression<Func<TEntity, TCacheItem>> BuildSelector<TEntity, TCacheItem>()
{
Type type = typeof(TEntity);
Type typeDto = typeof(TCacheItem);
var ctor = Expression.New(typeDto);
ParameterExpression parameter = Expression.Parameter(type, "p");
var propertiesDto = typeDto.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var memberAssignments = propertiesDto.Select(p =>
{
PropertyInfo propertyInfo = type.GetProperty(p.Name, BindingFlags.Public | BindingFlags.Instance);
MemberExpression memberExpression = Expression.Property(parameter, propertyInfo);
return Expression.Bind(p, memberExpression);
});
var memberInit = Expression.MemberInit(ctor, memberAssignments);
return Expression.Lambda<Func<TEntity, TCacheItem>>(memberInit, parameter);
}
Your best bet is to get very comfortable with the System.Linq.Expressions namespace, which contains all of the methods you'll need to dynamically metacode your method calls and compile them into delegates. See especially Expression.Call and Lambda.Compile methods. Note that using Lambda.Compile, you can also have a true, compiled Delegate, instead of an expression tree (Expression) wrapping the call to your desired method. (NOTE: You can also forgo the Compile step if you really want that expression tree for later)
As for building your set, that's Assembly scanning, and is going to be a matter of iterating over all classes in your Assembly. I highly recommend you utilize, at the very least, a custom Attribute on your assembly or future assemblies to mark them for this scan, lest this process end up much more costly. At the most, you should consider using a custom Attribute to mark which properties you want scanned for this expression build.
the actual code to this tends to start with
AppDomain.CurrentDomain // Necessary to get all available Assemblies
.GetAssemblies() // Gets all the assemblies currently loaded in memory that this code can work with
.AsParallel() // Highly recommended to make the attribute-checking steps run asynchronously
// Also gives you a handy .ForAll Method at the end
// TODO: .Where Assembly contains Attribute
.SelectMany(assembly => assembly.GetTypes())
// TODO: .Where Type contains Attribute
.SelectMany(type => type.GetProperties)
// TODO: Make sure Property has the right data...
.Select(CompileFromProperty)
Where CompileFromProperty is a method taking PropertyInfo and returning the desired Expression.
Look into ToList() and ToDictionary after that, as you may need to break out of the parallelization once you start pushing values to your cache
Addendum: you also have .MakeGenericType on the Type class, which will allow you to specify Generic parameters from other Type variables, which will prove invaluable when building the Expressions. Don't forget about Contravariance when you define the generic types!

Create Generic Expression from string property name

I have a variable called sortColumn, which contains the text of a column that I want to sort a query result by. I also have a generic repository which takes as a parameter an Expression that contains the field I want to sort by. I can't seem to get from the string property name to an Expression.
So the generic repository that I have contains the following method
public IEnumerable<TEntity> Get<TOrderBy>(Expression<Func<TEntity, bool>> criteria,
Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex,
int pageSize,
bool isAssendingOrder = true,
EnumDeletePolicy deletePolicy = EnumDeletePolicy.ExcludeDeleted)
Notice the second parameter to this Get is Expression-Func-TEntity, TOrderBy. As I mentioned I have a variable called sortColumn, which contains the string for a property on my TEntity object I need to convert this string into an Expression that I can pass to the Get method.
Here is what I have right now.
var parameter = Expression.Parameter(typeof(IContract));
var memberExpression = Expression.Property(parameter, data.SortColumn);
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
Which creates an object of type LambdaExpression. The actual type of this LambdaExpression is an Expression-Func-IContract, string (or whatever the type sortColumn of the property is). If I call the Get method and pass in this LambdaExpression and explicitly cast it to the Expression type then it will work fine. The problem is I don't know what the Expression type is, it could be a string, int, int?, etc. It all depends on the type of the property that is specific in the sortColumn property.
Can you help me make this last jump to the right Expression type?
Edit based on Marc's suggestions:
I nearly have this working, actually based specifically on the question it is working, but I have 1 remaining problem.
The IContract which is the Entity Type that I'm querying against actually inherits from IRelationship. If I specify a field from the IContract interface then the code above works. If I specify a field from the IRelationship interface then the following line fails.
var memberExpression = Expression.Property(parameter, data.SortColumn);
If I try something like below so that I'm grabbing the MemberExpression from the IRelationship, but building the Lambda based on IContract I get an error from the repository.
var parameter = Expression.Parameter(typeof(IRelationship));
var memberExpression = Expression.Property(parameter, data.SortColumn);
var orderBy = Expression.Lambda(memberExpression, Expression.Parameter(typeof(IContract)));
The error that I get is "The parameter '' was not bound in the specified LINQ to Entities query expression."
The final expression to get it working was this
var parameter = Expression.Parameter(typeof(IContract));
var memberExpression = Expression.Property(parameter, typeof(IRelationship), data.SortColumn);
var orderBy = Expression.Lambda(memberExpression, parameter);
So I needed to specify the middle parameter to the memberExpression line, to say look in the inherited Relationship interface for the property
You kinda need to use the correct generic overload - which used to mean you had to use MakeGenericMethod; however, you can also use dynamic to avoid the need to use MakeGenericMethod here, for example (in this case via Where, but the important point is how it works):
IQueryable<Foo> source = new[] { new Foo { Bar = 123 } }.AsQueryable();
Expression<Func<Foo,bool>> typed = x=>x.Bar == 123;
LambdaExpression untyped = typed;
IQueryable<Foo> filtered = Queryable.Where(source, (dynamic)untyped);
Note: you can't use extension methods here - hence why you need to use Queryable.*.
For an OrderBy example using your code:
var parameter = Expression.Parameter(typeof(Foo));
var memberExpression = Expression.Property(parameter, "Bar");
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
LambdaExpression untyped = lambdaExpression;
IQueryable<Foo> sorted = Queryable.OrderBy(source, (dynamic)untyped);
var all = sorted.ToArray();
Re the edit:
var parameter = Expression.Parameter(typeof(IRelationship));
var memberExpression = Expression.Property(
Expression.Convert(parameter, typeof(IContract)), data.SortColumn);
var orderBy = Expression.Lambda(memberExpression, parameter);

How to set parameter of method using reflection

The following code is a helper I've created for a system which allows administrators to create their own queries on a database. It returns a lambda expression based on the method and value provided.
I am attempting to find a way to pass parameters to the method call - in this example I am using the StartsWith parameter of String, and attempting to set StringComparison.OrdinalIgnoreCase as a parameter.
There will be others parameters required too, depending on the type of the property specified. I'm hoping that understanding the method of supplying the string comparison property will enable me to add the rest later.
The underlying code works correctly, I just need it to be case-insensitive.
I have used this question as a guide, but the solution does not seem applicable here.
Here is the code:
public static class LambdaExpressionHelper<T> {
public static Expression<Func<T, bool>> Build(string propertyName, string method, string propertyValue) {
PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName);
ParameterExpression e = Expression.Parameter(typeof(T), "e");
MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
ConstantExpression c = Expression.Constant(propertyValue, m.Type);
MethodInfo mi = m.Type.GetMethod(method, new Type[] { m.Type }, );
// The below caused errors
//object classInstance = Activator.CreateInstance(typeof(T), null);
//object[] paramArray = new object[] { StringComparison.OrdinalIgnoreCase };
//mi.Invoke(classInstance, paramArray);
Expression call = Expression.Call(m, mi, c);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
return lambda;
}
}
// External code:
var lambda = LambdaExpressionHelper<MailingListMember>.Build("EmailAddress", "StartsWith", "RoRy#");
Thanks for any help.
In the general case this should work:
public static Expression<Func<T, bool>>
Build(string propertyName, string method, params object[] args)
{
var propertyInfo = typeof(T).GetProperty(propertyName);
var e = Expression.Parameter(typeof(T), "e");
var m = Expression.MakeMemberAccess(e, propertyInfo);
var mi = m.Type.GetMethod(method, args.Select(a => a.GetType()).ToArray());
var c = args.Select(a => Expression.Constant(a, a.GetType())).ToArray();
Expression call = Expression.Call(m, mi, c);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
return lambda;
}
The idea is that you accept any number of constant arguments, and use their types to select the appropriate overload when calling GetMethod. After that, you create the appropriate number of constant expressions from those arguments to pass to Expression.Call.
So with the above code, you could do:
var l1 = LambdaExpressionHelper<MailingListMember>.Build(
"EmailAddress", "StartsWith", "RoRy#");
var l2 = LambdaExpressionHelper<MailingListMember>.Build(
"EmailAddress", "StartsWith", "RoRy#", StringComparison.OrdinalIgnoreCase);
If you also need to get the value StringComparison.OrdinalIgnoreCase from the string "StringComparison.OrdinalIgnoreCase", I would factor this out into a separate method so that the interface of Build can remain generic. Exactly how to do it is covered in Getting Enum value via reflection (and I guess in lots of similar questions as well).
Note: I don't have convenient access to a compiler right now, so please excuse any mistakes.
I'm not sure i understand your question exactly, but hopefully this will help you on the way.
If you are using .NET 4.0 you can use the new keyword "dynamic" for much easier reflection code:
dynamic myDynamicObj = "Hello World!"; // or Activator.CreateInstance...
var doesIndeed = myDynamicObj.StartsWith("Hello", StringComparison.OrdinalIgnoreCase);
This does evaluate at run time so make sure spelling/case etc is correct.
Edit:
Assuming you always wanted to call methods with one String-arg returning bool, a possible solution would be to create a delegate type -
delegate bool CompareString(String str);
and than have the second argument of Build as that type:
Build(String .., CompareString cs, String ...)
But this does not work if you need to add extra arguments, as in the second arg of type StringComparison. A if/switch could be the answer there though, i.e if(CompareString is StartsWith)...
Sorry, not at a windows-computer so i can't test further.

How to convert PropertyInfo to property expression and use it to invoke generic method?

How to convert PropertyInfo to property expression which can be used to invoke StructuralTypeConfiguration<TStructuralType>.Ignore<TProperty>(Expression<Func<TStructuralType, TProperty>> propertyExpression) method?
I tried to use Expression.Property() to construct expression but I am getting following error when I use this expression as propertyExpression parameter:
The type arguments for method cannot be inferred from the usage. Try specifying the type arguments explicitly.
This error probably refers to TProperty type parameter which I don't know how to specify having only PropertyInfo.
I am doing this in relation to: Use Entity Framework's StructuralTypeConfiguration.Ignore() to Ignore all properties but specified set.
UPDATE
Code which is not working:
var propertyInfo = typeof(Foo).GetProperties()[0];
var expression = Expression.Default(typeof(Foo));
var expressionProperty = Expression.Property(expression, propertyInfo);
Ignore(expressionProperty);
var entityType = propertyInfo.DeclaringType;
var parameter = Expression.Parameter(entityType, "entity");
var property = Expression.Property(parameter, propertyInfo);
var funcType = typeof(Func<,>).MakeGenericType(entityType, propertyInfo.PropertyType);
var lambda = Expression.Lambda(funcType, property, parameter);
structureConfiguration.GetType()
.GetMethod("Ignore")
.MakeGenericMethod(propertyInfo.PropertyType)
.Invoke(structureConfiguration, new[]{lambda});
Property expressions require the property access to be on a specific object. There's a few options you can take here. First, if this is being done within one of your entity objects, you can simple use a ConstantExpression to build the property expression:
// Already have PropertyInfo in propInfo
Expression.Property(Expression.Constant(this, this.GetType()), propInfo)
However, since you need a Expression<Func<TStructuralType, TProperty>>, then it seems like you're going to have to build it using a ParameterExpression:
ParameterExpression pe = Parameter.Expression(typeof(MyEntity), "eParam");
Expression propExp = Expression.Property(pe, propInfo);
HOWEVER, here's the kicker... This is just a MemberExpression. To convert to the expression you need, you need to use Expression.Lambda to get a Func<> expression of the type you need. The problem? You don't know the type of the property to define the generic parameters of the lambda expression!
Expression<Func<MyEntity, ????>> eFunc = Expression.Lambda<Func<MyEntity, ????>>(propExp, pe);
This is the crux of the problem of doing it this way. That's not to say it can't be done... It's just that using this method IN THIS WAY isn't going to work. You'll have to use a bit runtime and static typing trickery (as well as judicious use of Actions instead of Funcs) to get this to work correctly.
TProperty exists only in the c# source code text. The compiler always resolves it to a concrete type. If you have a method
void Test<T>(T arg)
{
}
and call it like this
Test("hello");
Test(3);
The compiler generates code for two methods!
void Test(string arg)
{
}
void Test(int arg)
{
}
This means that you have to supply concrete types for your generic parameters if you want to have an invokable method.
This code will get you an Expression<Func<>> of the desired type. Note that there is an Expression.Lambda(...) override that doesn't need you to specify the type of the Func returned.
var t = typeof(Foo);
var pi = t.GetProperty(...);
var prm = Expression.Parameter(t, t.Name);
var prx = Expression.Property(prm, pi);
var lambda = Expression.Lambda(prx, prm);
Note that in many cases you don't have to bother with creating the Expression<Func<>> this way, assuming structureConfiguration below is a StructureConfiguration<Foo>, type inference will allow you to write something like this:
structureConfiguration.Ignore(f => f.Bar);

Categories