2017-02-23 45 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); 
     } 
    } 
}