2017-11-11 100 views
3

我試圖建立一個表達式與泛型類型參數個數可變創建通用Tuple<>實例變量數量的元組。表達對創建的泛型類型參數

生成的Tuple<>實例的想法是基於其上具有KeyAttribute的屬性爲實體類型動態創建組合鍵值。然後組合鍵將被用作Dictionary<object, TEntity>中的鍵。所以lambda表達式應該建立一定的實體類型和拉姆達會被調用,傳遞的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())創建屬性表達式,但不知道如何從lambda參數中傳遞TEntity實例),以及如何通過Expression.Lambda調用將其鏈接起來。任何幫助將不勝感激!

回答

3

你是相當接近。

// 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();