LINQ 查询因可为空变量 ormlite 而失败

LINQ query fails with nullable variable ormlite

我正在尝试使用 ServiceStack Ormlite 编写以下 LINQ 查询。

dbConn.Select<Product>(p => p.IsActive.HasValue && p.IsActive.Value)

在这里,Product 是我的项目 class,"IsActive" 是 class 中的 Nullable Bool 属性。当这一行执行时,它总是抛出 "InvalidOperationException" 消息

变量 'p' 类型 '' 从范围 '' 引用,但未定义

我尝试了如下不同的变体,但仍然出现相同的异常结果

dbConn.Select<Product>(p => p.IsActive.HasValue == true && p.IsActive.Value == true)
dbConn.Select<Product>(p => p.IsActive != null && p.IsActive.Value == true)

但如果我只写

dbConn.Select<Product>(p => p.IsActive.HasValue)

然后就可以了。

我很纳闷这是什么问题?这是 servicestack ormlite 问题吗?

这是 Linq 的本质。为了达到你的需要,你需要使用两个where closes:

 dbConn.Where<Product>(p => p.IsActive.HasValue).Where(p=>p.Value==true);

我的回答可以使用 servicestack ormlite 处理像 "value" 和 "HasValue" 这样的 Nullable 值。而且还有 datetime 可以为空,比如 'createdate.value.Year'。 你必须改变两个地方。

  1. 修改VisitMemberAccess方法:

protected virtual object VisitMemberAccess(MemberExpression m)
        {
            if (m.Expression != null)
            {
                 if (m.Member.DeclaringType.IsNullableType())
                 {
                    if (m.Member.Name == nameof(Nullable<bool>.Value))
                        return Visit(m.Expression);
                    if (m.Member.Name == nameof(Nullable<bool>.HasValue))
                    {
                        var doesNotEqualNull = Expression.NotEqual(m.Expression, Expression.Constant(null));
                            return Visit(doesNotEqualNull); // Nullable<T>.HasValue is equivalent to "!= null"
                    }
                    throw new ArgumentException(string.Format("Expression '{0}' accesses unsupported property '{1}' of Nullable<T>", m, m.Member));
                }
                if (m.Member.DeclaringType == typeof(DateTime))
                {
                    var ExpressionInfo = m.Expression as MemberExpression;
                    if (ExpressionInfo.Member.DeclaringType.IsNullableType())
                    {
                        if (ExpressionInfo.Member.Name == nameof(Nullable<bool>.Value))
                        {
                            var modelType = (ExpressionInfo.Expression as MemberExpression).Expression.Type;
                            var tableDef = modelType.GetModelDefinition();
                            var columnName = (ExpressionInfo.Expression as MemberExpression).Member.Name;
                            var QuotedColumnName = GetQuotedColumnName(tableDef, columnName);
                            if (m.Member.Name == "Year")
                            {
                                return new PartialSqlString(string.Format("DATEPART(yyyy,{0})", QuotedColumnName));
                            }
                            if (m.Member.Name == "Month")
                                return new PartialSqlString(string.Format("DATEPART(mm,{0})", QuotedColumnName));
                        }                            
                        if (ExpressionInfo.Member.Name == nameof(Nullable<bool>.HasValue))
                        {
                            var doesNotEqualNull = Expression.NotEqual(ExpressionInfo.Expression, Expression.Constant(null));
                            return Visit(doesNotEqualNull); // Nullable<T>.HasValue is equivalent to "!= null"
                        }
                    }
                    else
                    {
                        var modelType = ExpressionInfo.Expression.Type;
                        var tableDef = modelType.GetModelDefinition();
                        var columnName = ExpressionInfo.Member.Name;
                        var QuotedColumnName = GetQuotedColumnName(tableDef, columnName);
                        if (m.Member.Name == "Year")
                            return new PartialSqlString(string.Format("DATEPART(yyyy,{0})", QuotedColumnName));
                        if (m.Member.Name == "Month")
                            return new PartialSqlString(string.Format("DATEPART(mm,{0})", QuotedColumnName));
                    }
                }
                if (m.Expression.NodeType == ExpressionType.Parameter || m.Expression.NodeType == ExpressionType.Convert)
                { 
                    var propertyInfo = (PropertyInfo)m.Member;

                    var modelType = m.Expression.Type;
                    if (m.Expression.NodeType == ExpressionType.Convert)
                    {
                        var unaryExpr = m.Expression as UnaryExpression;
                        if (unaryExpr != null)
                        {
                            modelType = unaryExpr.Operand.Type;
                        }
                    }

                    var tableDef = modelType.GetModelDefinition();
                    if (propertyInfo.PropertyType.IsEnum)
                        return new EnumMemberAccess(
                            GetQuotedColumnName(tableDef, m.Member.Name), propertyInfo.PropertyType);

                    return new PartialSqlString(GetQuotedColumnName(tableDef, m.Member.Name));
                }
            }

            var member = Expression.Convert(m, typeof(object));
            var lambda = Expression.Lambda<Func<object>>(member);
            var getter = lambda.Compile();
            return getter();
        }

  1. 修改 VisitLambda 方法 :

protected virtual object VisitLambda(LambdaExpression lambda)
        {
            if (lambda.Body.NodeType == ExpressionType.MemberAccess && sep == " ")
            {
                MemberExpression m = lambda.Body as MemberExpression;

                if (m.Expression != null)
                {
                    string r = VisitMemberAccess(m).ToString();
                    if (m.Member.DeclaringType.IsNullableType())
                        return r;
                    return string.Format("{0}={1}", r, GetQuotedTrueValue());
                }

            }
            return Visit(lambda.Body);
        }