“对象引用未设置为对象的实例” - 但没有啥是空的?

Posted

技术标签:

【中文标题】“对象引用未设置为对象的实例” - 但没有啥是空的?【英文标题】:"Object reference not set to an instance of an object" - but nothing is null?“对象引用未设置为对象的实例” - 但没有什么是空的? 【发布时间】:2015-01-24 21:24:11 【问题描述】:

是的,你可能认为; “天哪,还有一个?”

是的,另一个。

“对象引用未设置为对象的实例。”

我最近一直在使用 EF6,经过一段时间的开发,我发现需要进行更多优化。很多都已经重做没有问题,但我似乎无法弄清楚这一点。

在我的应用程序中,我一直在使用这段伪代码从数据库中获取项目。

DbContext context = new DbContext();

public IEnumerable<string> GetExistingNames(IEnumerable<string> names)

    foreach(string name in names)
    
        string existingName = context.Names.Where(n => n.Name == name).FirstOrDefault();
        if(existingName == null) continue;
        yield return existingName;
    

请注意,DbContext 仅用于澄清。它会在需要时被丢弃。

这种方法“有效”,但这意味着如果我有 20 个名字要查找,我将访问数据库大约 20 次。哎哟!

因此我开始寻找一种方法来实现单个查询。我找到了一种方法,但它并没有真正发挥应有的作用。这是我目前的做法;

public IEnumerable<string> GetExistingNames(ICollection<string> names)

    IQueryable<Names> query = context.Names.Where(n => names.Contains(n.Name));
    if(query == null) yield break;
    foreach(var name in query)
    
        yield return name.Name;
    

据我所知,这应该翻译成SELECT ... FROM Names WHERE ... IN (...)。但是,我的应用程序在遇到name 时立即在foreach(var name in query) 崩溃,从而引发了令人恐惧的NullReferenceException。 但是,它确实传递了if(query == null),这意味着查询不为空。此时,我很困惑。怎么可能不为null,还抛出这个错误?

如果我尝试使用这种方法访问它,我不确定是否会执行查询。因此,我尝试使用ToList() 从查询中创建一个列表,但这会在创建列表时引发相同的异常。

似乎每次我打电话给query,它都会给我一个NullReferenceException。但是,它仍然通过if(query == null)。所以,我的问题是;

为什么它通过了测试,但它无法访问?我误解了IQueryable&lt;&gt; 吗?如果我确实误解了,应该如何正确处理?

编辑

发帖前我已经调试过了。我很确定;

names 不为空。 context 不为空。

调用函数的代码:

//A wrapper for the DbContext. This is only used for some methods
//which require the DbContext
DbContextWrapper wrapper = new DbContextWrapper();

public void ProcessNames(List<string> inputNames)

    //...

    foreach(string existingName in wrapper.GetExistingNames(inputNames))
    
        //Do something with the names
    

    //...

编辑 2

经过更多调试,我发现正在创建的查询有些不同。应该是这样的;

SELECT `Extent1`.`Name` 
FROM `Names` AS `Extent1` 
WHERE (`Extent1`.`Name` IN ( @gp1,@gp2))

但是,我明白了;

System.Data.Entity.Infrastructure.DbQuery<MyDbContext.Names>

作为实际查询。

堆栈跟踪;

at mysql.Data.Entity.SqlGenerator.Visit(DbPropertyExpression expression)
at MySql.Data.Entity.SqlGenerator.Visit(DbInExpression expression)
at System.Data.Entity.Core.Common.CommandTrees.DbInExpression.Accept[TResultType](DbExpressionVisitor`1 visitor)
at MySql.Data.Entity.SqlGenerator.VisitBinaryExpression(DbExpression left, DbExpression right, String op)
at MySql.Data.Entity.SqlGenerator.Visit(DbAndExpression expression)
at System.Data.Entity.Core.Common.CommandTrees.DbAndExpression.Accept[TResultType](DbExpressionVisitor`1 visitor)
at MySql.Data.Entity.SelectGenerator.Visit(DbFilterExpression expression)
at System.Data.Entity.Core.Common.CommandTrees.DbFilterExpression.Accept[TResultType](DbExpressionVisitor`1 visitor)
at MySql.Data.Entity.SqlGenerator.VisitInputExpression(DbExpression e, String name, TypeUsage type)
at MySql.Data.Entity.SelectGenerator.VisitInputExpressionEnsureSelect(DbExpression e, String name, TypeUsage type)
at MySql.Data.Entity.SelectGenerator.Visit(DbProjectExpression expression)
at System.Data.Entity.Core.Common.CommandTrees.DbProjectExpression.Accept[TResultType](DbExpressionVisitor`1 visitor)
at MySql.Data.Entity.SelectGenerator.GenerateSQL(DbCommandTree tree)
at MySql.Data.MySqlClient.MySqlProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree)
at System.Data.Entity.Core.Common.DbProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree, DbInterceptionContext interceptionContext)
at System.Data.Entity.Core.Common.DbProviderServices.CreateCommandDefinition(DbCommandTree commandTree, DbInterceptionContext interceptionContext)
at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition..ctor(DbProviderFactory storeProviderFactory, DbCommandTree commandTree, DbInterceptionContext interceptionContext, IDbDependencyResolver resolver, BridgeDataReaderFactory bridgeDataReaderFactory, ColumnMapFactory columnMapFactory)
at System.Data.Entity.Core.EntityClient.Internal.EntityProviderServices.CreateCommandDefinition(DbProviderFactory storeProviderFactory, DbCommandTree commandTree, DbInterceptionContext interceptionContext, IDbDependencyResolver resolver)
at System.Data.Entity.Core.EntityClient.Internal.EntityProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree, DbInterceptionContext interceptionContext)
at System.Data.Entity.Core.Common.DbProviderServices.CreateCommandDefinition(DbCommandTree commandTree, DbInterceptionContext interceptionContext)
at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.CreateCommandDefinition(ObjectContext context, DbQueryCommandTree tree)
at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.Prepare(ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Boolean streaming, Span span, IEnumerable`1 compiledQueryParameters, AliasGenerator aliasGenerator)
at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__6()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__5()
at System.Data.Entity.Infrastructure.DefaultExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
at MyNameSpace.DbContextWrapper.<GetExistingNames>d__1b.MoveNext() in c:~omitted~\DbContextWrapper.cs:line 70
at MyNameSpace.NameProcessor.ProcessNames(List<string> inputNames) in c:~omitted~\NameProcessor.cs:line 60

【问题讨论】:

query 项可能不为空,但foreach 围绕它构建一个迭代器。基本上,更多的代码在你没有进入的后台运行,而这些代码就是query (IQueryable&lt;T&gt;) 的实现——这就是 EF 的东西。 @AdamHouldsworth 我知道foreach 尝试创建一个迭代器并调用MoveNext(),如果query 为空,这很容易出现NullRef - 但它通过if ! names 是否包含引用?你确定context 包含参考吗? names null 中是否有任何项目?请注意,只是说“是的,我知道,nullrefereneexception,但这是不同的”并没有使它不同。围绕这个问题的所有问题都是不同的,但解决方案总是一样的,调试、调试、调试。我在这里看不到任何新东西。 @LasseV.Karlsen names 绝对不为空,context 也绝对不为空。当然,我在这里发布之前进行了调试。我将把它添加到问题中。 要明确query 永远不会为空,即使您的 where 子句不会返回任何记录。很难说 WHAT 到底是什么 null 但你为什么不把整个方法改成return context.Where(n =&gt; names.Contains(n)).Select(x =&gt; x.Name) 【参考方案1】:

在您发布 stacktrace 之后,我发现您使用的是 MySQL,所以我猜您遇到了这个错误:Exception when using IEnumera.Contains(model.property) in Where predicate

因此,解决方案是确保您拥有 MySQL Connector/NET 6.7.6 / 6.8.4 / 6.9.5 和更新版本。 或者尝试使用Any 方法而不是Contains

附:此错误报告来自 Alnedru 的这篇文章:Int[].Contains doesn't work in EF6

【讨论】:

这个其实不错,没找到。我使用的是 Connector/NET 6.9.3,所以我猜它不包含这个错误修复? 据我了解,您需要至少将其更新到 6.9.5 才能获得修复。 顺便说一句,我通过第一个堆栈跟踪条目进行搜索并提出了我添加到答案中的 SO 问题。所以你可以看到堆栈跟踪有帮助;) 我昨天下班了,但今天早上我马上测试了它,升级效果很好。非常感谢! 很高兴听到这个消息!祝你好运。【参考方案2】:

您对查询的空值检查永远不会失败,因为它返回的是 IQueryable 对象(即正在构建的查询)。您已经对其进行了实例化并开始构建查询,因此它将始终通过。

要明确 - IQueryable 大致相当于包含 ADO.Net Select 语句的字符串。它本质上不是实际数据。

这并没有解释为什么它会抛出一个空异常,但它确实解释了为什么空检查通过,而 foreach 仍然可能失败。

编辑:尝试复制此内容时,我发现使用以下代码时出现异常:

public IEnumerable<string> GetExistingNames(ICollection<string> names)

    IQueryable<Names> query = Names.Where(n => names.Contains(n.Name));
    if (query == null) yield break;
    foreach (var name in query)
    
        yield return name.Name;
    

它不是NullReferenceException,而是NotSupportedException,因为ICollections Contains 不支持SQL 转换。将参数切换为List 导致问题消失:

public IEnumerable<string> GetExistingNames(List<string> names)

或者您可以将其动态转换为列表:

IQueryable<Names> query = Names.Where(n => names.ToList().Contains(n.Name));

【讨论】:

枚举IQueryable&lt;&gt; 确实会命中数据库。虽然IQueryable&lt;&gt;如何获取数据的秘诀,但对其进行枚举,从而生成IEnumerator&lt;&gt; 对象将获取数据。 如果我尝试使用这种方法访问它,我不确定是否会执行查询。因此,我尝试使用 ToList() 从查询中创建一个列表,但这会在创建列表时引发相同的异常 - 不幸的是,ToList() 的调用将导致 NullReferenceException @LasseV.Karlsen 公平点 - 我之前一直在记忆,我已经删除了我的答案的那部分。 它确实回答了为什么它通过了测试,但它无法访问?,因此我赞成它。但是它并不能解决我的问题,因此我无法将其标记为已回答。 @DionV。我对此感到有些困惑,尽管我在使用 ICollection 时确实发现了一个不同的异常。更新了我的答案。【参考方案3】:

你为什么不添加一个扩展方法来减轻它给你带来的压力。 试试这段代码

namespace HelperExtensionMethods

    public static class ExtensionMethods
    
        public static string UpdateNullString(this string testNullstring)
        
            if (TestNullstring == null)
                return "";
            return Testullstring;
        
    

然后这样称呼它

using HelperExtesionMethods
DbContext context = new DbContext();

public IEnumerable<string> GetExistingNames(ICollection<string> names)

    IQueryable<Names> query = context.Names.Where(n => names.UpdateNullString().Contains(n.Name.UpdateNullString()));
    if(query == null) yield break;
    foreach(var name in query)
    
        yield return name.Name;
    

【讨论】:

1) 这个问题很久以前就已经回答过了,2) 你不能只为string 创建一个扩展并期望 EntityFramework 从中创建一个查询。您是否尝试过自己运行此代码?仅仅因为它编译并不意味着它可以工作。 3) 问题不是string 为空,而是query 对象。

以上是关于“对象引用未设置为对象的实例” - 但没有啥是空的?的主要内容,如果未能解决你的问题,请参考以下文章

“对象引用未设置为对象的实例”我遇到了这个错误,我不知道为啥[重复]

对象引用未设置为对象实例的自动映射器问题

我的gridview中的“对象引用未设置为对象的实例”

Discord.NET '对象引用未设置为对象的实例。 C# [重复]

对象引用未设置为对象的实例...错误?

stsadm -addsolution“对象引用未设置为对象的实例”