首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >LINQ语句不可翻译

LINQ语句不可翻译
EN

Stack Overflow用户
提问于 2020-01-29 14:39:12
回答 3查看 443关注 0票数 2

我有以下包含LINQ语句的代码:

代码语言:javascript
运行
复制
public async Task<HashSet<long>> GetMembersRecursive(IEnumerable<long> groupIds)
{
    var containsGroupId = InExpression<Group>("Id", groupIds);
    var containsParentId = InExpression<RecursiveGroupModel>("ParentId", groupIds);

    var groupIdsArray = groupIds as long[] ?? groupIds.ToArray();
    return new HashSet<long>(await MyContext
        .Groups
        .Where(containsGroupId)
        .Select(a => new
        {
            Members = MyContext
                .ViewWithRecursiveGroups
                .Where(containsParentId)
                .SelectMany(c => c.Group.Members)
                .Union(a.Members)
                .Where(b => !b.User.IsActive)
        })
        .SelectMany(a => a.Members.Select(b => b.MemberId))
        .Distinct()
        .ToListAsync());
}

private static Expression<Func<T, bool>> InExpression<T>(string propertyName, IEnumerable<long> array)
{
    var p = Expression.Parameter(typeof(T), "x");
    var contains = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
        .Single(x => x.Name == "Contains" && x.GetParameters().Length == 2)
        .MakeGenericMethod(typeof(long));
    var property = Expression.PropertyOrField(p, propertyName);
    var body = Expression.Call(
        contains
        , Expression.Constant(array)
        , property
    );

    return Expression.Lambda<Func<T, bool>>(body, p);
}

我收到的错误是:

代码语言:javascript
运行
复制
Microsoft.EntityFrameworkCore: Processing of the LINQ expression 'DbSet<RecursiveGroupModel>
     .Where(b => __groupIdsArray_1
         .Contains(b.ParentId))
     .SelectMany(c => c.Group.GroupMembers)
     .Union((MaterializeCollectionNavigation(
         navigation: Navigation: Group.GroupMembers,
         subquery: (NavigationExpansionExpression
             Source: DbSet<GroupMember>
                 .Where(l0 => EF.Property<Nullable<long>>(l, "Id") != null && EF.Property<Nullable<long>>(l, "Id") == EF.Property<Nullable<long>>(l0, "GroupId1"))
             PendingSelector: l0 => (NavigationTreeExpression
                 Value: (EntityReference: GroupMember)
                 Expression: l0)
         )
             .Where(i => EF.Property<Nullable<long>>((NavigationTreeExpression
                 Value: (EntityReference: Group)
                 Expression: l), "Id") != null && EF.Property<Nullable<long>>((NavigationTreeExpression
                 Value: (EntityReference: Group)
                 Expression: l), "Id") == EF.Property<Nullable<long>>(i, "GroupId1"))))' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.

视图:

代码语言:javascript
运行
复制
CREATE VIEW [dbo].[View_WithRecursiveGroups] AS
     WITH RecursiveGroups (GroupId, ParentId) AS
    (
        SELECT Id, ParentId
        FROM Group
        WHERE ParentId IS NOT NULL
        UNION ALL
        SELECT Group.Id, t.ParentId
        FROM GroupTree t
        JOIN Group ON t.GroupId = Group.ParentId
    )

    SELECT * FROM RecursiveGroups

如果某些变量名不匹配,请提前道歉--我必须在发帖前进行清理。

我知道它不能将代码转换成SQL,所以它要求我提前枚举或重写,以便它是可翻译的。我已经尝试过重新排列查询并将其分解为较小的查询,但是递归视图上的SelectMany似乎无法转换为SQL。

有没有办法让它在数据库中运行?还是说我完全走错了路?

EN

回答 3

Stack Overflow用户

发布于 2020-01-29 15:21:00

作为替代方法,您可以使用原始sql查询。在实体框架代码中,我们需要定义一个POCO类和一个用于该类的DbSet。在这种情况下,您需要定义一些YourClass

代码语言:javascript
运行
复制
public DbQuery<YourClass> YourClasses { get; set; }

要执行的代码:

代码语言:javascript
运行
复制
var result = context.YourClasses.FromSql("YOURSQL_SCRIPT").ToList();
var asyncresult = await context.YourClasses.FromSql("YOURSQL_SCRIPT").ToListAsync();
票数 0
EN

Stack Overflow用户

发布于 2020-01-29 15:50:02

是的,欢迎来到EfCore 3.1的奇妙世界,在那里你所能做的就是“你好世界”。

您的查询有各种“问题”,因为除了超级简单的情况外,EfCore并不真正执行LINQ处理。

.Union(a.Members)

无法转换为运行服务器端,并且未启用客户端处理。你唯一的选择是:

  • 强制执行两部分的服务器(使用AsEnumerable),然后在客户端执行联合。只有当您不将其用作更大语句的一部分(即intersect)时,这才有效,否则就是“将所有数据拉到客户端”的时间,这是不好的。

在目前的时间点上,我只能建议您抛弃EfCore,使用EntityFramework,根据Framework3.1的说法,它又可以使用了。或者使用实体框架经典版,它是一个运行在netstandard 2.0上的端口,具有全局查询过滤器(这是我喜欢的EfCore的一个特性)。最后,这就是我目前所要做的,因为--好吧--“更好,但是没有任何功能,不能工作”对我来说是不合适的。

在这一点上,EfCore是否会被扩展(他们似乎不认为它是一个修复程序)来处理除最基本的LINQ语句(有时甚至不是那些语句)之外的任何事情都是未知的- 3.1中的许多变化都相当令人沮丧。

你也许可以把它移到视图等等--但是你可能很快就会发现EfCore有更多的限制,而且维护所有的视图也变得相当繁琐。我遇到了严重的问题,因为即使在最简单的情况下,我也不能在任何投影之前放置任何条件。即使是简单的bug也会被评论为“我们觉得改变管道不舒服,请等待11月的版本5”。示例?https://github.com/dotnet/efcore/issues/15279

票数 0
EN

Stack Overflow用户

发布于 2020-01-29 16:09:46

如果你想把这个视图转换成Linq...

代码语言:javascript
运行
复制
CREATE VIEW [dbo].[View_WithRecursiveGroups] AS
     WITH RecursiveGroups (GroupId, ParentId) AS
    (
        SELECT Id, ParentId
        FROM Group
        WHERE ParentId IS NOT NULL
        UNION ALL
        SELECT Group.Id, t.ParentId
        FROM GroupTree t
        JOIN Group ON t.GroupId = Group.ParentId
    )

代码语言:javascript
运行
复制
var data1 = db.Group.where(x=>x.ParentId != nul)
            .Select(x=>new {x.Id, x.ParentId})
            .Tolist()

var data2 = (from g in db.Groups
            join gt in db.GroupTree on g.ParentId equals gt.GroupId
            select new { d.Id, ParentId })
            .ToList();

创建一个重新处理数据的类,并让查询以已知类型的列表的形式返回,然后将两个列表合并。

linqpad是一个非常有用的工具,可以帮助您学习如何创建linq,从而为您提供所需的sql。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59961674

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档