2017-02-23 44 views
1

這只是選擇從汽車的一些列:DataContext的翻譯<>匿名類型

var qs = myDataContext.Cars 
    .Select(c => new { c.id, c.color }) 
    .ToList(); 

我需要的是功能,即會做同樣的,而是通過SqlCommand的,這樣我就可以改變的過程。它的(簡化)代碼是在這裏

public static IEnumerable<P> ProjectionFunction<T, P>(
    this System.Data.Linq.Table<T> input, 
    Func<T, P> projection 
    ) where T : class 
{ 
    System.Data.Linq.DataContext dc = input.Context; 

    string paramList = string.Join(
     ",", 
     typeof(P).GetProperties().Select(s => s.Name) 
     ); 

    System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand(
     "SELECT " + paramList + " FROM " + typeof(T).Name, 
     (System.Data.SqlClient.SqlConnection)dc.Connection 
     ); 

    cmd.CommandType = CommandType.Text; 

    if (cmd.Connection.State == ConnectionState.Closed) 
    { 
     cmd.Connection.Open(); 
    } 

    return dc.Translate<P>(cmd.ExecuteReader()); // <-- the problem is here 
} 

功能非常適用於普通班這樣

private class X 
{ 
    public int? id { get; set; } 
    public string color { get; set; } 
} 

var qx = myDataContext.Cars 
    .ProjectionFunction(c => new X() { id = c.id, color = c.color }) 
    .ToList(); 

但它未能對匿名類型

var qa = myDataContext.Cars 
    .ProjectionFunction(c => new { c.id, c.color }) 
    .ToList(); 

我得到運行時錯誤

類型 <> f__AnonymousType20`2 [System.Nullable`1 [System.Int32],System.String] 必須聲明一個默認(無參數)構造函數,以便在映射期間構造爲 。

對於Translate<>功能。我試過的ExecuteQuery<>也一樣。很難相信DataContext不知道如何構建匿名類型,這是他一直都在做的事情。我錯過了什麼?我怎麼能讓他爲我做這件事?

單獨的一個使用類的問題是,它的屬性必須與原始類的屬性的類型和名稱顯式同步,這使得這種方法有些不切實際。

回答

0

我仍然想知道是否有辦法將DataContext轉換爲匿名類型,但很可能它並不公開此類行爲。在這種情況下,必須使用另一個物化器。最後,編寫一個並不困難,所以我分享了它,以防有人感興趣。初始化匿名類型的唯一方法是通過new運算符。幸運的是表達式可以在運行時建立。

private static Func<object[], P> getMaterializer<P>(
    System.Reflection.PropertyInfo[] props, IEnumerable<string> propertyNames) 
{ 
    Type[] propertyTypes = props.Select(p => p.PropertyType).ToArray(); 
    ParameterExpression arrExpr = Expression.Parameter(typeof(object[])); 
    var constructor = typeof(P).GetConstructor(propertyTypes); 

    if (constructor == null || !constructor 
     .GetParameters() 
     .Select(p => p.Name) 
     .SequenceEqual(propertyNames)) 
    { 
     return null; 
    } 

    Expression[] paramExprList = propertyTypes.Select((type, i) => 
    { 
     Expression ei = Expression.ArrayIndex(arrExpr, Expression.Constant(i)); 

     if (type.IsGenericType || type == typeof(string)) 
     { 
      return (Expression)Expression.Condition(
       Expression.Equal(ei, Expression.Constant(DBNull.Value)), 
       Expression.Convert(Expression.Constant(null), type), 
       Expression.Convert(ei, type) 
       ); 
     } 
     else 
     { 
      return Expression.Convert(ei, type); 
     } 
    }).ToArray(); 

    return Expression.Lambda<Func<object[], P>>(
     Expression.New(constructor, paramExprList), 
     arrExpr 
     ).Compile(); 
} 


private static System.Collections.Concurrent.ConcurrentDictionary<Type, Tuple<string, string, object>> cachedProjections = 
    new System.Collections.Concurrent.ConcurrentDictionary<Type, Tuple<string, string, object>>(); 

private static Tuple<string, string, object> getProjection<T, P>() 
{ 
    Type typeP = typeof(P); 
    Tuple<string, string, object> projection; 

    if (!cachedProjections.TryGetValue(typeP, out projection)) 
    { 
     Type typeT = typeof(T); 
     System.Reflection.PropertyInfo[] props = typeP.GetProperties(); 
     List<string> propertyNames = props.Select(p => p.Name).ToList(); 

     projection = new Tuple<string, string, object>(
      string.Join(",", propertyNames), 
      typeT.Name, 
      typeT == typeP ? null : getMaterializer<P>(props, propertyNames) 
      ); 

     cachedProjections.TryAdd(typeP, projection); 
    } 

    return projection; 
} 

private static IEnumerable<P> Materialize<P>(SqlCommand cmd, 
    Func<object[], P> materializer) 
{ 
    using (var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) 
    { 
     object[] obj = new object[reader.VisibleFieldCount]; 

     while (reader.Read()) 
     { 
      reader.GetValues(obj); 
      yield return materializer(obj); 
     } 
    } 
} 

public static IEnumerable<P> ProjectionFunction<T, P>(
    this System.Data.Linq.Table<T> input, 
    Func<T, P> projectionFunction 
) where T : class 
{ 
    var projection = getProjection<T, P>(); 

    using (SqlCommand cmd = new SqlCommand(
     "SELECT " + projection.Item1 
     + " FROM " + projection.Item2, 
     new SqlConnection(input.Context.Connection.ConnectionString) 
     )) 
    { 
     cmd.CommandType = CommandType.Text; 
     cmd.Connection.Open(); 

     var materializer = (Func<object[], P>)projection.Item3; 
     if (materializer == null) 
     { 
      return input.Context.Translate<P>(cmd.ExecuteReader(CommandBehavior.CloseConnection)); 
     } 
     else 
     { 
      return Materialize(cmd, materializer); 
     } 
    } 
}