我有以下包含LINQ语句的代码:
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);
}
我收到的错误是:
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.
视图:
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。
有没有办法让它在数据库中运行?还是说我完全走错了路?
发布于 2020-01-29 15:21:00
作为替代方法,您可以使用原始sql查询。在实体框架代码中,我们需要定义一个POCO类和一个用于该类的DbSet。在这种情况下,您需要定义一些YourClass
public DbQuery<YourClass> YourClasses { get; set; }
要执行的代码:
var result = context.YourClasses.FromSql("YOURSQL_SCRIPT").ToList();
var asyncresult = await context.YourClasses.FromSql("YOURSQL_SCRIPT").ToListAsync();
发布于 2020-01-29 15:50:02
是的,欢迎来到EfCore 3.1的奇妙世界,在那里你所能做的就是“你好世界”。
您的查询有各种“问题”,因为除了超级简单的情况外,EfCore并不真正执行LINQ处理。
.Union(a.Members)
无法转换为运行服务器端,并且未启用客户端处理。你唯一的选择是:
在目前的时间点上,我只能建议您抛弃EfCore,使用EntityFramework,根据Framework3.1的说法,它又可以使用了。或者使用实体框架经典版,它是一个运行在netstandard 2.0上的端口,具有全局查询过滤器(这是我喜欢的EfCore的一个特性)。最后,这就是我目前所要做的,因为--好吧--“更好,但是没有任何功能,不能工作”对我来说是不合适的。
在这一点上,EfCore是否会被扩展(他们似乎不认为它是一个修复程序)来处理除最基本的LINQ语句(有时甚至不是那些语句)之外的任何事情都是未知的- 3.1中的许多变化都相当令人沮丧。
你也许可以把它移到视图等等--但是你可能很快就会发现EfCore有更多的限制,而且维护所有的视图也变得相当繁琐。我遇到了严重的问题,因为即使在最简单的情况下,我也不能在任何投影之前放置任何条件。即使是简单的bug也会被评论为“我们觉得改变管道不舒服,请等待11月的版本5”。示例?https://github.com/dotnet/efcore/issues/15279。
发布于 2020-01-29 16:09:46
如果你想把这个视图转换成Linq...
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
)
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。
https://stackoverflow.com/questions/59961674
复制相似问题