zoukankan      html  css  js  c++  java
  • Entity Framework 5.0运行.NET Framework 4.0之上在查询表达式中使用显示转换的一个问题

    EF使用版本File Version:4.4.20627.0、Product Version:5.0.0.net40,今天在其中一个测试环境(Windows Server 2008 R2、.NET Framework 4.0)发现执行一个查询会抛出如下异常信息(由于寄宿体的原因这个异常通过sos的!StopOnException命令还无法直接捕捉):

    System.ArgumentException: The specified value is not an instance of type 'Edm.Int32' Parameter name: value at System.Data.Common.CommandTrees.ExpressionBuilder.Internal.ArgumentValidation.ValidateConstant(TypeUsage constantType, Object value) at System.Data.Objects.ELinq.ExpressionConverter.ConstantTranslator.TypedTranslate(ExpressionConverter parent, ConstantExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.MemberAccessTranslator.TypedTranslate(ExpressionConverter parent, MemberExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.UnaryTranslator.TypedTranslate(ExpressionConverter parent, UnaryExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.EqualsTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.BitwiseBinaryTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpre...

    经过排查缩减导致这个错误的原因是一个Int32赋值生成的查询表达式所致,简略实体定义参考如下:

    /// <summary>
    /// 消息处理状态
    /// </summary>
    public enum HandleStatus
    {
        /// <summary>
        /// 成功
        /// </summary>
        /// <value>0</value>
        Success = 0,
    
        /// <summary>
        /// 失败
        /// </summary>
        /// <value>1</value>
        Failed,
    }
    
    /// <summary>
    /// 消息处理队列查询条件
    /// </summary>
    public sealed class HandleQueueCondition
    {
        /// <summary>
        /// 发送队列状态
        /// </summary>
        public Nullable<HandleStatus> Status { get; set; }
    }
    
    /// <summary>
    /// 消息处理队列项
    /// </summary>
    [Table("Core_Messaging_HandleQueue"), Serializable]
    public sealed class HandleQueueItem
    {
        /// <summary>
        /// 发送状态
        /// </summary>
        [NotMapped]
        public HandleStatus Status
        {
            get
            {
                return (HandleStatus)this.StatusValue;
            }
            set
            {
                this.StatusValue = (Int32)value;
            }
        }
    
        /// <summary>
        /// 发送状态值
        /// </summary>
        /// <remarks>用户EF的实体映射</remarks>
        [Column("Status")]
        public Int32 StatusValue { get; set; }
    }

    为了解决EF 5.0不支持枚举值的现状,我们通过一个中间值保存返回它。生成表达式的部分代码如下:

    /// <summary>
    /// 查询列表
    /// </summary>
    /// <param name="dbContext">数据库上下文</param>
    /// <param name="condition">查询条件</param>
    /// <returns>查询列表</returns>
    private IEnumerable<HandleQueueItem> QueryList(MessagingDbContext dbContext, HandleQueueCondition condition)
    {
        Expression<Func<HandleQueueItem, Boolean>> predicate = PredicateExtension.True<HandleQueueItem>();
    
        if (condition.Status.HasValue)
            predicate = predicate.And<HandleQueueItem>(p => p.StatusValue == (Int32)condition.Status.Value);
    
        return dbContext.HandleQueueItems.Where(predicate)
            .OrderByDescending(p => p.DateCreated)
            .Skip(condition.PageIndex * condition.PageSize)
            .Take(condition.PageSize)
            .ToList();
    }

    PredicateExtension是针对Expression<Func<T, Boolean>>的扩展,用于合并多个条件表达式。用WinDbg + sos调试,设置断点在System.Data.Entity.dll的System.Data.Common.CommandTrees.ExpressionBuilder.Internal.ArgumentValidation.ValidateConstant函数上发现constantType的名称是Edm.Int32、而value并不是转换后的Int32值而是枚举类型HandleStatus。相同的代码在安装.NET Framework 4.5的测试环境没有问题,通过升级到4.5版本可以解决这个缺陷。

    4.0和4.5对于包含转换层级的函数解析表达式都会失败(比如:Convert.Toxxx),但是对于一个显示转换则不同,看更简单的代码段:

    dbContext.HandleQueueItems.Where(p => p.StatusValue == Convert.ToInt32(condition.Status.Value))
        .OrderByDescending(p => p.DateCreated)
        .Skip(condition.PageIndex * condition.PageSize)
        .Take(condition.PageSize)
        .ToList();

    显示转换下两个版本输出的表达式相同((p.StatusValue == Convert(value(ConsoleApplication1.Program+<>c__DisplayClass0).condition.Status.Value))):

    dbContext.HandleQueueItems.Where(p => p.StatusValue == ((Int32)condition.Status.Value))
        .OrderByDescending(p => p.DateCreated)
        .Skip(condition.PageIndex * condition.PageSize)
        .Take(condition.PageSize)
        .ToList();

    真正的变化在于System.Data.Entity的System.Data.Objects.ELinq.ExpressionConverter内部。

  • 相关阅读:
    hisi3516/3519开发(二)—xshell连接串口
    linux svn使用
    IdentityServer4 源码介绍
    想写博客
    # VS2019 快捷键插入当前时间
    # 使用 vscode markdown 遇到的问题
    # 学Vue
    teXt使用
    Linux基础
    NopCommerce(Core)学习目录
  • 原文地址:https://www.cnblogs.com/junchu25/p/3050250.html
Copyright © 2011-2022 走看看