实体框架6.1.1 DbJoinExpression获取所有左列

时间:2015-05-25 05:29:19

标签: c# linq entity-framework expression-trees

我正在尝试让表达式树访问者在查询某些实体时将连接添加到另一个表。如果我将.Select()方法与投影包含在匿名类型中并直接指定要查询的特定实体的列,则该表达式非常有用:

var joinEntity = expression.Target.EntityContainer.BaseEntitySets.Single(s => s.Name == "TestJoinTable");

return expression.InnerJoin(
    DbExpressionBuilder.Scan(joinEntity),
        (l, r) => DbExpressionBuilder.And(
                DbExpressionBuilder.Equal(
                    DbExpressionBuilder.Property(l, "JoinId"),
                    DbExpressionBuilder.Property(r, "JoinId")
                )
                , DbExpressionBuilder.Equal(
                    DbExpressionBuilder.Property(r, "UserId"),
                    DbExpression.FromInt32(_userId)
                )
            )
        )
    .Select(exp =>
        new { // these are the 3 columns from one specific entity I have called Resources
                ResourceId = exp.Property("l").Property("ResourceId"),
                ResourceName = exp.Property("l").Property("ResourceName"),
                JoinId = exp.Property("l").Property("JoinId"),
            }
    );

导致此表​​达式逻辑在此特定情况下运行的LINQ to Enties查询是:

List<Resource> resources = db.Resources.ToList();

问题是,表达式访问者代码旨在针对许多不同的实体查询运行,而不仅仅是资源实体查询。我需要一种动态方式来从左实体中选择所有列。我需要指定类似的内容:

.Select( exp => exp.Property("l").Property("*") )

上面的表达式无法编译。它会抛出错误:No property with the name '*' is declared by the type 'ScratchDbModel.Store.Resource'

我也试过这个,但它失败并出现类似的错误消息:

.Select( exp => exp.Property("l.*") )

我尝试选择这样的左侧实体:

.Select( exp => exp.Property("l") )

上面的表达式编译并看起来很有希望直到它实际运行查询,这导致了这个SQL:

SELECT 
    [Extent1].[ResourceId] AS [ResourceId], 
    [Extent1].[ResourceName] AS [ResourceName], 
    [Extent1].[JoinId] AS [JoinId]
    FROM ( SELECT [l]
        FROM  [dbo].[Resource] AS [l]
        INNER JOIN [sec].[TestJoinTable] AS [r]
    ON ([l].[JoinId] = [r].[JoinId]) AND ([r].[UserId] = 2)
    )  AS [Extent1]

由于内部SELECT选择[l]而不是[l.*]或列名列表,因此查询执行失败并显示以下消息:

Invalid column name 'l'.
Invalid column name 'ResourceId'.
Invalid column name 'ResourceName'.
Invalid column name 'JoinId'.

我尝试了很多不同的东西,但似乎无法弄清楚如何做到这一点。如果我完全放弃.Select(),我会收到错误:

An exception of type 'System.ArgumentOutOfRangeException' occurred in EntityFramework.dll but was not handled in user code

Additional information: No property with the name 'ResourceId' is declared by the type 'Transient.rowtype[(l,ScratchDbModel.Store.Resource(Nullable=True,DefaultValue=)),(r,ScratchDbModel.Store.TestJoinTable(Nullable=True,DefaultValue=))]'.

当然,这是因为实体框架期望接收IEnumerable<Resource>,而不是某些多表连接结果。

有没有办法一般指定“左表中的所有列”或创建某种类型的动态匿名类型或包含左实体的所有列引用的动态投影?

1 个答案:

答案 0 :(得分:0)

要选择属性的所有属性,只需选择整个:

return expression.InnerJoin(
    DbExpressionBuilder.Scan(joinEntity),
        (l, r) => DbExpressionBuilder.Equal(
                    DbExpressionBuilder.Property(l, "JoinId"),
                    DbExpressionBuilder.Property(r, "JoinId")
                )
    )
    .Select(exp => exp.Property("l"));

这将自动生成查询,选择“l”所有的列。