这只是从汽车中选择一些列:
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();
我收到运行时错误
类型 &LT;&GT; f__AnonymousType20`2 [System.Nullable`1 [System.Int32],System.String] 必须声明一个默认(无参数)构造函数 在绘图过程中构建。
表示Translate<>
功能。我尝试的ExecuteQuery<>
也是如此。很难相信DataContext不知道如何构建匿名类型,这是他一直在做的事情。我错过了什么?我怎么能让他这样做呢?
单独使用一个类的问题是,它的属性必须与原始类的属性的类型和名称显式同步,这使得这种方法有点不切实际。
答案 0 :(得分: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);
}
}
}