How to create dynamic multicast selection in IEnumerable <T> at runtime?

I asked a very similar question yesterday, but only today I realized that the answer I accepted did not solve all my problems. I have the following code:

public Expression<Func<TItem, object>> SelectExpression<TItem>(string fieldName) { var param = Expression.Parameter(typeof(TItem), "item"); var field = Expression.Property(param, fieldName); return Expression.Lambda<Func<TItem, object>>(field, new ParameterExpression[] { param }); } 

Used as follows:

 string primaryKey = _map.GetPrimaryKeys(typeof(TOriginator)).Single(); var primaryKeyExpression = SelectExpression<TOriginator>(primaryKey); var primaryKeyResults = query.Select(primaryKeyExpression).ToList(); 

This allows me to pull primary keys from IQueryable<TUnknown> . The problem is that this code only works with one primary key, and I need to add support for multiple PCs.

So, is there any way to adapt the SelectExpression method above to take an IEnumerable<string> (which is my list of primary key property names) and return an expression method that selects these keys?

those. Given the following:

 var knownRuntimePrimaryKeys = new string[] { "CustomerId", "OrderId" }` 

My Select needs to do the following (at run time):

 var primaryKeys = query.Select(x=> new { x.CustomerId, x.OrderId }); 
+6
source share
3 answers

There is no easy way to do what you want, because you need to dynamically create a new type (anonymous types are created by the compiler when they are known statically). While this is certainly possible, it is probably not the easiest option ...

You can achieve a similar result using tuples :

 public Expression<Func<TItem, object>> SelectExpression<TItem>(string[] propertyNames) { var properties = propertyNames.Select(name => typeof(TItem).GetProperty(name)).ToArray(); var propertyTypes = properties.Select(p => p.PropertyType).ToArray(); var tupleTypeDefinition = typeof(Tuple).Assembly.GetType("System.Tuple`" + properties.Length); var tupleType = tupleTypeDefinition.MakeGenericType(propertyTypes); var constructor = tupleType.GetConstructor(propertyTypes); var param = Expression.Parameter(typeof(TItem), "item"); var body = Expression.New(constructor, properties.Select(p => Expression.Property(param, p))); var expr = Expression.Lambda<Func<TItem, object>>(body, param); return expr; } 
+3
source

You can use Tuple<> because anonymous types must be known at compile time:

 public Expression<Func<TItem, object>> SelectExpression<TItem>(params string[] fieldNames) { var param = Expression.Parameter(typeof(TItem), "item"); var fields = fieldNames.Select(x => Expression.Property(param, x)).ToArray(); var types = fields.Select(x => x.Type).ToArray(); var type = Type.GetType("System.Tuple`" + fields.Count() + ", mscorlib", true); var tuple = type.MakeGenericType(types); var ctor = tuple.GetConstructor(types); return Expression.Lambda<Func<TItem, object>>( Expression.New(ctor, fields), param ); } 

and then:

 var primaryKeyExpression = SelectExpression<TOriginator>("CustomerId", "OrderId"); 

will create the following expression:

 item => new Tuple<string, string>(item.CustomerId, item.OrderId) 
+2
source

As someone already pointed out, you are essentially trying to get an anonymous type created at runtime that won't work.

Is there an alternative to using an anonymous type and still achieve what I need?

It really depends on what you mean. The obvious answers are to use runtime constructs like dictionary, tuple, etc. But you are probably fully aware of this yourself, so I assume that you want to get a compiled result with real field names so that any misuse of primaryKeys gets caught at compile time.

If so, then I am afraid that your only option is to create the appropriate code before compiling. This is not as bad as it may seem, but not completely transparent: when changing the scheme, you must somehow restart the code generation.

In our company, we did just that, inspired by SubSonic , but when we discovered that SubSonic itself wasn’t exactly what we wanted, I think it was good.

+1
source

Source: https://habr.com/ru/post/906836/


All Articles