用于EF核心的ConstantExpression中的正确集合

问题描述 投票:1回答:1

我试图实现我自己的Expression serializator / deserializator,以便通过服务传递它(我想实现我自己的EF Core服务终结点)。因此,现在我在LambdaExpressions中遇到了Collections问题。例如,

var dataQuery = testDb.Users.Include(e => e.EmployeeInfo).Include(f => f.Notifications).Where(s => tstList.Contains(s.Id)).Select(e => e.FullName);
var tstEspressionBase = dataQuery.Expression;
var tstEspression = new ReflectionLocalValculationVisitor().Visit(tstEspressionBase);

这里

public class ReflectionLocalValculationVisitor : ExpressionVisitor
{
    protected override Expression VisitMember(MemberExpression memberExpression)
    {
        var expression = Visit(memberExpression.Expression);

        if (expression is ConstantExpression)
        {
            object container = ((ConstantExpression)expression).Value;
            var member = memberExpression.Member;
            if (member is FieldInfo)
            {
                object value = ((FieldInfo)member).GetValue(container);
                return Expression.Constant(value);
            }
            if (member is PropertyInfo)
            {
                object value = ((PropertyInfo)member).GetValue(container, null);
                return Expression.Constant(value);
            }
        }
        return base.VisitMember(memberExpression);
    }
}

var tstList = new List<Guid>()
{
   new Guid("D45E1A1A-F546-48DB-77BA-08D7775C6A93"),
   new Guid("5B21C782-9B95-48F2-77BD-08D7775C6A93")
};

使用此代码执行

var providerAsync = testDb.GetService<IAsyncQueryProvider>();
var toListAsyncMethodInfo = typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ToListAsync)).MakeGenericMethod(typeof(string));
var s3 = await toListAsyncMethodInfo.InvokeAsync(null, new object[] { providerAsync.CreateQuery(tstEspression), default(CancellationToken) }).ConfigureAwait(false);

给我正确的结果。

因此,在使用Newtonsoft Json进行序列化/反序列化之后,我从Lambda方法的Where中收集数据时遇到问题:

错误消息:LINQ表达式'DbSet.where(u => List {d45e1a1a-f546-48db-77ba-08d7775c6a93,5b21c782-9b95-48f2-77bd-08d7775c6a93,} .Contains(s.Id))'翻译。以一种可以翻译的形式重写查询,或通过插入呼叫明确切换到客户评估AsEnumerable(),AsAsyncEnumerable(),ToList()或ToListAsync()。参见https://go.microsoft.com/fwlink/?linkid=2101038更多信息。

我试图实现此“推荐”,但没有任何效果(请参见下面的代码):

var asEnumerableMethod = typeof(Enumerable).GetMethod(nameof(Enumerable.AsEnumerable)).MakeGenericMethod(GenericTypes.Select(e => e.FromNode()).ToArray());
var asEnumerabled = asEnumerableMethod.Invoke(null, new object[] { Value });

此处Value对象是JSON.NET反序列化后生成的List<Guid>。因此,我在Value中比较了ConstantExpression的已实现接口,该接口表示序列化之前和反序列化之后的List<guid>-都实现8个接口。

所以,也许有人遇到相同的问题。

谢谢。

P.S。我不知道为什么EF Core给我Where(u => ...而不是Where(s => ...,因为在此表达式的DebugView模式下,我看到正确的Where(s => ...表示形式。

让我们看一下序列化/(反序列化和还原)的表达式(来自DebugView的数据):

.Call System.Linq.Queryable.Select(
    .Call System.Linq.Queryable.Where(
        .Call Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Include(
            .Call Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Include(
                .Constant<Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[EFCoreDataModel.DataClasses.Users.Base.User]>(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[EFCoreDataModel.DataClasses.Users.Base.User]),
                '(.Lambda #Lambda1<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,EFCoreDataModel.DataClasses.Users.Employ.EmployeeInfo]>))
            ,
            '(.Lambda #Lambda2<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.Collections.Generic.ICollection`1[EFCoreDataModel.DataClasses.Notifications.Notification]]>))
        ,
        '(.Lambda #Lambda3<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.Boolean]>)),
    '(.Lambda #Lambda4<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.String]>))

.Lambda #Lambda1<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,EFCoreDataModel.DataClasses.Users.Employ.EmployeeInfo]>(EFCoreDataModel.DataClasses.Users.Base.User $e)
{
    $e.EmployeeInfo
}

.Lambda #Lambda2<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.Collections.Generic.ICollection`1[EFCoreDataModel.DataClasses.Notifications.Notification]]>(EFCoreDataModel.DataClasses.Users.Base.User $f)
{
    $f.Notifications
}

.Lambda #Lambda3<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.Boolean]>(EFCoreDataModel.DataClasses.Users.Base.User $s)
{
    .Call .Constant<System.Collections.Generic.List`1[System.Guid]>(System.Collections.Generic.List`1[System.Guid]).Contains($s.Id)
}

.Lambda #Lambda4<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.String]>(EFCoreDataModel.DataClasses.Users.Base.User $e)
{
    $e.FullName
}

原始表达式(来自DebugView):

.Call System.Linq.Queryable.Select(
    .Call System.Linq.Queryable.Where(
        .Call Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Include(
            .Call Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Include(
                .Constant<Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[EFCoreDataModel.DataClasses.Users.Base.User]>(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[EFCoreDataModel.DataClasses.Users.Base.User]),
                '(.Lambda #Lambda1<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,EFCoreDataModel.DataClasses.Users.Employ.EmployeeInfo]>))
            ,
            '(.Lambda #Lambda2<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.Collections.Generic.ICollection`1[EFCoreDataModel.DataClasses.Notifications.Notification]]>))
        ,
        '(.Lambda #Lambda3<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.Boolean]>)),
    '(.Lambda #Lambda4<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.String]>))

.Lambda #Lambda1<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,EFCoreDataModel.DataClasses.Users.Employ.EmployeeInfo]>(EFCoreDataModel.DataClasses.Users.Base.User $e)
{
    $e.EmployeeInfo
}

.Lambda #Lambda2<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.Collections.Generic.ICollection`1[EFCoreDataModel.DataClasses.Notifications.Notification]]>(EFCoreDataModel.DataClasses.Users.Base.User $f)
{
    $f.Notifications
}

.Lambda #Lambda3<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.Boolean]>(EFCoreDataModel.DataClasses.Users.Base.User $s)
{
    .Call .Constant<System.Collections.Generic.List`1[System.Guid]>(System.Collections.Generic.List`1[System.Guid]).Contains($s.Id)
}

.Lambda #Lambda4<System.Func`2[EFCoreDataModel.DataClasses.Users.Base.User,System.String]>(EFCoreDataModel.DataClasses.Users.Base.User $e)
{
    $e.FullName
}

因此,它们是相等的。而且序列化/反序列化在Lambda中没有u参数,可能只是's'。

c# linq .net-core entity-framework-core expression-trees
1个回答
1
投票

最有可能是由于反序列化之后此处的未绑定lambda表达式参数引起的问题

s => tstList.Contains(s.Id)

条件并不重要。并且调试显示具有误导性。 s中的s =>ParameterExpression中的s是相同的s.Id实例。这在C#编译时表达式中不会发生,但可以使用Expression类方法轻松完成。请注意,从表达式树的角度来看,参数的名称无关紧要,仅与实例无关。例如,以下代码段

var param1 = Expression.Parameter(typeof(User), "s"); var param2 = Expression.Parameter(typeof(User), "s"); var body = Expression.Equal( Expression.Property(param2, "Id"), Expression.Constant(new Guid("D45E1A1A-F546-48DB-77BA-08D7775C6A93")) ); var predicate = Expression.Lambda<Func<Blog, bool>>(body, param1);

创建一个有效的(!?)lambda表达式,该表达式在LINQ查询中使用

var test = testDb.Set<User>().Where(predicate).ToList();

将产生与所讨论的异常相似的异常。

话虽这么说,请忘记常量表达式中的集合,而专注于lambda表达式以查找并修复导致上述参数表达式差异的代码。

© www.soinside.com 2019 - 2024. All rights reserved.