Выражение для создания кортежа с переменным числом аргументов универсального типа
Я пытаюсь построить выражение для создания общего Tuple<>
экземпляр с переменным числом аргументов универсального типа.
Идея для сгенерированного Tuple<>
Например, для динамического создания значения составного ключа для типов объектов на основе свойств, которые имеют KeyAttribute
в теме. Составной ключ будет затем использоваться в качестве ключа в Dictionary<object, TEntity>
, Таким образом, лямбда-выражение должно быть построено для определенного типа сущности, и лямбда будет вызвана, передавая экземпляр TEntity
вернуть составной ключ в виде Tuple<>
,
Пример модели объекта
public class MyEntityModel
{
[Key]
public string Key1 { get; set; }
[Key]
public Guid Key2 { get; set; }
public int OtherProperty { get; set; }
}
Какое выражение должно делать
public Func<MyEntityModel, object> BuildKeyFactory()
{
// This is how the LambdaExpression should look like, but then for a generic entity type instead of fixed to MyEntityModel
return new Func<MyEntityModel, object>(entity => new Tuple<string, Guid>(entity.Key1, entity.Key2));
}
Но, конечно, модель сущностей должна быть универсального типа.
Что у меня пока
public Func<TEntity, object> BuildKeyFactory<TEntity>()
{
var entityType = typeof(TEntity);
// Get properties that have the [Key] attribute
var keyProperties = entityType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.GetCustomAttribute(typeof(KeyAttribute)) != null)
.ToArray();
var tupleType = Type.GetType($"System.Tuple`{keyProperties.Length}");
if (tupleType == null) throw new InvalidOperationException($"No tuple type found for {keyProperties.Length} generic arguments");
var keyPropertyTypes = keyProperties.Select(x => x.PropertyType).ToArray();
var tupleConstructor = tupleType.MakeGenericType(keyPropertyTypes).GetConstructor(keyPropertyTypes);
if (tupleConstructor == null) throw new InvalidOperationException($"No tuple constructor found for key in {entityType.Name} entity");
// The following part is where I need some help with...
var newTupleExpression = Expression.New(tupleConstructor, keyProperties.Select(x => ????));
return Expression.Lambda<Func<TEntity, object>>(????).Compile();
}
Как видите, я не могу понять, как мне нужно создавать выражения свойств для передачи в Expression.New()
позвонить (наверное что то с Expression.MakeMemberAccess(Expression.Property())
но не знаю, как пройти TEntity
экземпляр из аргумента лямбда) и как я могу "связать" это с Expression.Lambda
вызов. Любая помощь будет высоко оценен!
1 ответ
Вы довольно близко
// we need to build entity => new Tuple<..>(entity.Property1, entity.Property2...)
// arg represents "entity" above
var arg = Expression.Parameter(typeof(TEntity));
// The following part is where I need some help with...
// Expression.Property(arg, "name) represents "entity.Property1" above
var newTupleExpression = Expression.New(tupleConstructor, keyProperties.Select(c => Expression.Property(arg, c)));
return Expression.Lambda<Func<TEntity, object>>(newTupleExpression, arg).Compile();