Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/288.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 重用排序逻辑_C#_Linq_Entity Framework Core_Npgsql_.net 5 - Fatal编程技术网

C# 重用排序逻辑

C# 重用排序逻辑,c#,linq,entity-framework-core,npgsql,.net-5,C#,Linq,Entity Framework Core,Npgsql,.net 5,我有一个枚举,描述了一篇文章的某种排序顺序: 枚举后序 { TitleAsc, 标题ESC, ScoreAsc, ScoreDesc, } 以及重用排序逻辑的扩展方法: 静态类IQueryableExtensions { 公共静态IOrderedQueryable OrderByCommon(此IQueryable可查询,PostOrderBy) =>orderBy开关 { PostOrder.TitleAsc=>queryable.OrderBy(x=>x.Title), PostOrder

我有一个枚举,描述了一篇文章的某种排序顺序:

枚举后序
{
TitleAsc,
标题ESC,
ScoreAsc,
ScoreDesc,
}
以及重用排序逻辑的扩展方法:

静态类IQueryableExtensions
{
公共静态IOrderedQueryable OrderByCommon(此IQueryable可查询,PostOrderBy)
=>orderBy开关
{
PostOrder.TitleAsc=>queryable.OrderBy(x=>x.Title),
PostOrder.TitleDesc=>queryable.OrderByDescending(x=>x.Title),
PostOrder.ScoreAsc=>queryable.OrderBy(x=>x.Score)。然后是By(x=>x.Title),
PostOrder.ScoreDesc=>queryable.OrderByDescending(x=>x.Score)。然后是by(x=>x.Title),
_=>抛出新的NotSupportedException(),
};
}
扩展方法在正常上下文中使用时有效,但在此处失败:

var输入=PostOrder.ScoreDesc;
var dbContext=newQuestionContext();
var users=dbContext.users
.选择(x=>new
{
用户=x,
Top3Posts=x.Posts.AsQueryable()
.OrderByCommon(输入)
.采取(3)
托利斯先生()
}).ToList();
出现此错误时:

The LINQ expression 'MaterializeCollectionNavigation(
    Navigation: User.Posts,
    subquery: NavigationExpansionExpression
        Source: DbSet<Post>()
            .Where(p => EF.Property<Nullable<int>>(u, "Id") != null && object.Equals(
                objA: (object)EF.Property<Nullable<int>>(u, "Id"), 
                objB: (object)EF.Property<Nullable<int>>(p, "AuthorId")))
        PendingSelector: p => NavigationTreeExpression
            Value: EntityReference: Post
            Expression: p
        .Where(i => EF.Property<Nullable<int>>(NavigationTreeExpression
            Value: EntityReference: User
            Expression: u, "Id") != null && object.Equals(
            objA: (object)EF.Property<Nullable<int>>(NavigationTreeExpression
                Value: EntityReference: User
                Expression: u, "Id"), 
            objB: (object)EF.Property<Nullable<int>>(i, "AuthorId")))
    .AsQueryable()
    .OrderByCommon(__input_0)
    .Take(3)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
LINQ表达式“MaterializeCollectionNavigation”(
导航:User.Posts,
子查询:NavigationExpression
资料来源:DbSet()
其中(p=>EF.Property(u,“Id”)!=null&&object.Equals(
objA:(object)EF.Property(u,“Id”),
objB:(object)EF.Property(p,“AuthorId”))
PendingSelector:p=>NavigationTreeExpression
Value:EntityReference:Post
表达式:p
.Where(i=>EF.Property(NavigationTreeExpression
值:EntityReference:用户
表达式:u,“Id”)!=null&&object.Equals(
objA:(对象)EF.Property(NavigationTreeeExpression
值:EntityReference:用户
表达式:u,“Id”),
objB:(object)EF.Property(i,“AuthorId”))
.AsQueryable()
.OrderByCommon(\u\u输入\u 0)
.Take(3)无法翻译。请以可以翻译的形式重写查询,或通过插入对AsEnumerable()、AsAsAsAsyncEnumerable()、ToList()或ToListSync()的调用显式切换到客户端计算。请参阅https://go.microsoft.com/fwlink/?linkid=2101038 了解更多信息。
可能是因为它正在
表达式中使用

我怎样才能在那里工作



可以找到一个可复制的项目。

这是众所周知的问题,没有通用的解决方案

表达式树翻译的一般问题是,它完全基于知识-实际上没有调用任何方法,已知的方法通过签名标识并根据其已知语义进行翻译。这就是为什么不能翻译自定义方法/属性/委托

这个问题通常可以通过使用一些表达式操纵库来解决。在我使用EF6/EF Core的过程中,我尝试了很多方法-LinqKit、NeinLinq、AutoMapper、最近的DelegateDecompiler。所有这些方法都允许用相应的原始表达式替换(扩展)表达式树的部分,就像手动编写它们一样

这种特殊情况下的问题更复杂,因为为了进行翻译,必须实际调用自定义方法。但是如何调用呢?特别是,
IQueryble
参数是什么?请注意

x.Posts.AsQueryable()
您没有
x
实例,因此没有
Posts
集合实例来调用
AsQueryable()
并将其传递给自定义方法

一种可能的解决方案是调用将伪LINQ传递给对象
IQueryable
,然后在结果查询表达式树中查找并用实际表达式替换它的方法

以下是上述理念的实施情况:

partial class IQueryableExtensions
{ 
    public static IQueryable<T> Transform<T>(this IQueryable<T> source)
    {
        var expression = new QueryableMethodTransformer().Visit(source.Expression);
        return expression == source.Expression ? source : source.Provider.CreateQuery<T>(expression);
    }

    class QueryableMethodTransformer : ExpressionVisitor
    {
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            if (node.Method.DeclaringType == typeof(IQueryableExtensions) &&
                node.Method.IsStatic &&
                typeof(IQueryable).IsAssignableFrom(node.Method.ReturnType) &&
                node.Arguments.Count > 1 &&
                node.Arguments[0].Type.IsGenericType &&
                node.Arguments[0].Type.GetGenericTypeDefinition() == typeof(IQueryable<>))
            {
                // Extract arguments
                var args = new object[node.Arguments.Count];
                int index = 1;
                while (index < args.Length && TryExtractValue(Visit(node.Arguments[index]), out args[index]))
                    index++;
                if (index == args.Length)
                {
                    var source = node.Arguments[0];
                    var elementType = source.Type.GetGenericArguments()[0];
                    // Create fake queryable instance
                    var fakeSource = args[0] = EmptyQueryableMethod
                        .MakeGenericMethod(elementType)
                        .Invoke(null, null);
                    // Invoke the method with it
                    var result = (IQueryable)node.Method.Invoke(null, args);
                    // Replace it with the actual queryable expression
                    return new ConstValueReplacer
                    {
                        From = fakeSource,
                        To = source
                    }.Visit(result.Expression);
                }
            }
            return base.VisitMethodCall(node);
        }

        static IQueryable<T> EmptyQueryable<T>() => Enumerable.Empty<T>().AsQueryable();

        static readonly MethodInfo EmptyQueryableMethod = typeof(QueryableMethodTransformer)
            .GetMethod(nameof(EmptyQueryable), BindingFlags.NonPublic | BindingFlags.Static);

        static bool TryExtractValue(Expression source, out object value)
        {
            if (source is ConstantExpression constExpr)
            {
                value = constExpr.Value;
                return true;
            }
            if (source is MemberExpression memberExpr && TryExtractValue(memberExpr.Expression, out var instance))
            {
                value = memberExpr.Member is FieldInfo field ? field.GetValue(instance) :
                    ((PropertyInfo)memberExpr.Member).GetValue(instance);
                return true;
            }
            value = null;
            return source == null;
        }
    }

    class ConstValueReplacer : ExpressionVisitor
    {
        public object From;
        public Expression To;
        protected override Expression VisitConstant(ConstantExpression node) =>
            node.Value == From ? To : base.VisitConstant(node);
    }
}

现在,可以通过将
QueryableMethodTransformer
插入EF Core查询转换管道来避免
Transform
调用,但仅调用一个方法就需要大量管道代码。请注意,它必须插入查询预Translator,因为
imethodCallTransformer
无法处理
IQueryable
(以及通常的
IEnumerable
)参数。如果您感兴趣,我的答案将显示如何将DelegateDecompiler插入EF Core,相同的代码可以直接用于插入其他代码(包括此处提供的代码)自定义表达式基于访问者的预处理器。

您能详细说明一下错误吗?如果您更改了
OrderBy
扩展方法,使其不做任何事情,只返回输入queryable,您会得到相同的错误吗?如果它只返回
queryable.Where(x=>x.Title)
?如果它们都有效,那么这就是你对枚举所做的事情。你能编辑问题并向我们展示你在扩展方法中所做的相关部分吗?我在移动ATM上,稍后我会用代码编辑问题,但Entity.Entities属性只是一个Entity列表。它是一个延迟加载的导航属性。基本我认为,唯一的问题是,除非您告诉EF Core如何翻译自定义扩展函数,否则它不知道如何翻译。这在表达式上下文之外并不重要,但在表达式上下文中很重要,其中的所有内容都必须可翻译为SQL。@Shoe这意味着代码的重要部分是issing-无论
后面是什么,这里的东西
OrderBy(x=>x.Title)
都可以毫无问题地进行翻译,因此您的代码肯定做得更多complex@PanagiotisKanavos我改进了这个问题,并添加了一个可复制的项目。
var users = dbContext.Users
    .Select(x => new
    {
        User = x,
        Top3Posts = x.Posts.AsQueryable()
            .OrderByCommon(input)
            .Take(3)
            .ToList()
    })
    .Transform() // <--
    .ToList();