"Object reference not set to an instance of an object" - but nothing is null?

Dion V. picture Dion V. · Nov 26, 2014 · Viewed 16.6k times · Source

Yeah, you probably think; "God, another one?".

Yes, another one.

"Object reference not set to an instance of an object."

I've been working with EF6 lately and after developing for some time, I found that a little bit more optimization was needed. Alot has been reworked without problems, but it seems I can't figure out this one.

In my application I've been using this piece of pseudo code to get items from the database.

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;
    }
}

Note that the DbContext is only there for clarification. It gets disposed when it's needed.

This approach "works" but it would mean that if I had, say, 20 names to look up, I would hit the database for about 20 times. Ouch!

Therefore I started looking for a way to implement a single query. I've found a way, but it's not really working as it should. This is my current approach;

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;
    }
}

This should, to my knowledge, translate in SELECT ... FROM Names WHERE ... IN (...). However, my application crashes at foreach(var name in query) as soon as it hits name, throwing the feared NullReferenceException. It does, however, pass if(query == null), meaning the query is not null. At this point, I was confused. How can it not be null, but still throw this error?

I was not sure if the query gets executed if I try to access it with this approach. Therefore, I tried to create a list from the query using ToList(), but this throws the same exception upon creating the list.

It seems like everytime I make a call to query, it gives me a NullReferenceException. However, it still passes if(query == null). So, my question is;

Why is it passing the test, but is it not accessible? Did I misinterpret IQueryable<>? And if I did misinterpret it, how should it be done properly?

EDIT

I have debugged before posting. I know for sure that;

  • names is not null.
  • context is not null.

Code calling the function:

//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
    }

    //...
}

EDIT 2

After some more debugging, I found that the query being created is somewhat different. It is supposed to be;

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

However, I get this;

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

As the actual query.

The stack trace;

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

Answer

Renatas M. picture Renatas M. · Nov 26, 2014

After you posted stacktrace I spotted that you using MySQL and so my guess is that you hit this bug: Exception when using IEnumera.Contains(model.property) in Where predicate

So solution would be to ensure you have versions of MySQL Connector/NET 6.7.6 / 6.8.4 / 6.9.5 and newer. Or try to use Any method instead of Contains.

P.s. This bug report came from this post by Alnedru: Int[].Contains doesn't work in EF6