C# EF核心搜索多列空错误

C# EF核心搜索多列空错误,c#,entity-framework,asp.net-core,entity-framework-core,C#,Entity Framework,Asp.net Core,Entity Framework Core,我使用的是EF核心,我需要能够在多个列上进行搜索,但有时一个项目可能为空 var myObject = _Context.CurrentTransformers.Where(a => a.ID.ToString().Contains(search) || a.ADMSKey.Contains(search) || a.AccuracyClass.ToString().Contains(search) || a.CoreCount.Contains(searc

我使用的是EF核心,我需要能够在多个列上进行搜索,但有时一个项目可能为空

var myObject = _Context.CurrentTransformers.Where(a =>
    a.ID.ToString().Contains(search) ||
    a.ADMSKey.Contains(search) ||
    a.AccuracyClass.ToString().Contains(search) ||
    a.CoreCount.Contains(search) ||
    a.PrimaryCurrentRatio.Contains(search) ||
    a.SecondaryCurrentRatio.Contains(search) ||
    a.EOLXINIVVC.Contains(search));
我知道在以前的版本中,我可以使用
UseCSharpNullComparisonBehavior
,但是我无法在EF Core中找到
ContextOptions
。上面的代码正在抛出一个未设置为对象错误实例的对象引用。

我这样做:

var myObject = _Context.CurrentTransformers.Where(a => 
    a.ID.ToString().Contains(search) ||
    a.ADMSKey != null && a.ADMSKey.ToLower().Contains(search.ToLower()) ||
    a.AccuracyClass != null && a.AccuracyClass.ToString().ToLower().Contains(search.ToLower()) ||
    a.CoreCount != null && a.CoreCount.ToLower().Contains(search.ToLower()) ||
    a.PrimaryCurrentRatio != null && a.PrimaryCurrentRatio.ToLower().Contains(search.ToLower()) ||
    a.SecondaryCurrentRatio != null && a.SecondaryCurrentRatio.ToLower().Contains(search.ToLower()) ||
    a.EOLXINIVVC != null && a.EOLXINIVVC.ToLower().Contains(search.ToLower()));

这是可行的,但有那么脏吗?还是可以这样做?

我扩展了发布的
PredicateBuilder
,包括一个名为
搜索的扩展方法

你可以这样称呼它

var myObject = _Context.CurrentTransformers.Search(search);
代码

public static class PredicateBuilder
{
    public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }

    public static IQueryable<T> Search<T>(this IQueryable<T> self, string keyword)
    {
        var predicate = False<T>();
        var properties = typeof(T).GetTypeInfo().DeclaredProperties;
        foreach (var propertyInfo in properties)
        {
            if (propertyInfo.GetGetMethod().IsVirtual)
                continue;

            var parameter = Expression.Parameter(typeof(T), "x");
            var property = Expression.Property(parameter, propertyInfo);

            var propertyAsObject = Expression.Convert(property, typeof(object));
            var nullCheck = Expression.NotEqual(propertyAsObject, Expression.Constant(null, typeof(object)));

            var propertyAsString = Expression.Call(property, "ToString", null, null);
            var keywordExpression = Expression.Constant(keyword);
            var contains = Expression.Call(propertyAsString, "Contains", null, keywordExpression);

            var lambda = Expression.Lambda(Expression.AndAlso(nullCheck, contains), parameter);

            predicate = predicate.Or((Expression<Func<T, bool>>)lambda);
        }
        return self.Where(predicate);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>
              (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
    }
}
公共静态类谓词生成器
{
公共静态表达式True(){return f=>True;}
公共静态表达式False(){return f=>False;}
公共静态IQueryable搜索(此IQueryable self,字符串关键字)
{
var谓词=False();
var properties=typeof(T).GetTypeInfo().DeclaredProperties;
foreach(属性中的var propertyInfo)
{
if(propertyInfo.GetMethod().IsVirtual)
继续;
var参数=表达式参数(类型为(T),“x”);
var property=Expression.property(参数,propertyInfo);
var propertyAsObject=Expression.Convert(property,typeof(object));
var nullCheck=Expression.NotEqual(propertyAsObject,Expression.Constant(null,typeof(object));
var propertyastring=Expression.Call(属性“ToString”,null,null);
var关键字表达式=表达式.常量(关键字);
var contains=Expression.Call(propertyAsString,“contains”,null,关键字表达式);
var lambda=Expression.lambda(Expression.AndAlso(nullCheck,contains),参数);
谓词=谓词。或((表达式)lambda);
}
返回self.Where(谓词);
}
公共静态表达式或(此表达式expr1,表达式expr2)
{
var invokedExpr=Expression.Invoke(expr2,expr1.Parameters.Cast());
返回Expression.Lambda(Expression.OrElse(expr1.Body,invokedExpr),expr1.Parameters);
}
公共静态表达式和(此表达式expr1,表达式expr2)
{
var invokedExpr=Expression.Invoke(expr2,expr1.Parameters.Cast());
返回表达式.Lambda
(Expression.AndAlso(expr1.Body,invokedExpr),expr1.Parameters);
}
}

这完全可以,但有点冗长。如果属性的类型为字符串,则可以使用此表达式
(someProperty???).ToLower()
。因此,与其
a.ADMSKey!=null&&a.ADMSKey.ToLower().Contains(search.ToLower())
您可以编写
(a.ADMSKey??).ToLower().Contains(search)
。另外,
搜索
只需使用闭包(在Where中)转换为较低的字符串一次即可。@这主意太棒了-我从来没有想过使用
(某物??)
而不是
!=空
检查!你能把你的重构作为一个答案吗?@StaffordWilliams,这真的是必要的,这只是一个小提示。此外,它仅适用于属性为string的情况(对于其他类型,在使用所使用的
ToString()
之前,我们必须先检查null)。我注意到你查询中的一些属性看起来真的很像数字而不是字符串(
CoreCount
PrimaryCurrentRatio
,…),但是你把它们当作字符串来对待?@没希望我不是OP:)我只是建议你发布重构作为鼓励OP和其他人的一种贡献-没有压力伙计。@StaffordWilliams很遗憾,我只是以为你是OP,没有真正看名字:)-如果是的话,我有更好的理由不添加答案。先生,你就是那个人。这太好了!