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内部。

posted @ 2013-04-29 00:56  junchu25  阅读(839)  评论(0编辑  收藏  举报