【问题标题】:DataContext Translate<> for anonymous typesDataContext Translate<> 用于匿名类型
【发布时间】:2017-07-13 08:39:31
【问题描述】:

这只是从 Cars 中选择的一些列:

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&lt;&gt; 函数。我试过的ExecuteQuery&lt;&gt; 也是如此。很难相信 DataContext 不知道如何构建匿名类型,这是他一直在做的事情。我错过了什么?我怎样才能让他为我这样做?

单独使用一个类的问题在于,它的属性必须与原始类的属性的类型和名称显式同步,这使得这种方法有些不切实际。

【问题讨论】:

    标签: c# linq linq-to-sql projection anonymous-types


    【解决方案1】:

    我仍然想知道是否有办法让 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);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多