7

Using C#, .NET 4.5.2, Entity Framework 6.1.3 and System.Linq I have encountered a confusing exception. The exception itself does not seem to contain useful information to determined why it is being raised.

The following line of code when executed results in a NullReferenceException: dbCtx.Customers.ToList();

However, the following line runs without exception and returns the correct result: (dbCtx.Customers).ToList();

Running the parenthesis surrounded expression first will allow both forms to execute without exception:

var result1 = (dbCtx.Customers).ToList();
var result2 = dbCtx.Customers.ToList();

I would also like to note that adding entities works as expected:

dbCtx.Customers.Add(new Customer() { Enabled = true, Name = "Test" });

Customer entity class:

public sealed class Customer : BaseEntity
{
    public bool Enabled { get; set; }

    [Required]
    public string Name { get; set; }
}

BaseEntity class:

public abstract class BaseEntity
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
}

DbContext class:

public class MyDbContext : DbContext
{
    public MyDbContext() : base(@"Server=.\SQLExpress;Database=MyDatabase;Trusted_Connection=Yes;")
    {
        Configuration.LazyLoadingEnabled = true;
    }

    public virtual DbSet<Customer> Customers { get; set; }
}

What could possibly be causing this behavior?

EDIT: This problem occurs with any entity when anything like .ToList(), .Count(), etc is invoked.

Exception Details:

System.NullReferenceException occurred
    HResult=0x80004003
    Message=Object reference not set to an instance of an object.
    Source=EntityFramework
    StackTrace:
      at System.Data.Entity.Internal.Linq.InternalSet`1.get_Expression()
      at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Expression()
      at MyProjTests.Test1.Test(MyDbContext dbCtx) in E:\ProgrammingProjects\WorkInProgress\MyProjRoot\MyProjTests\Test1.cs:line 51
      at MyProjTests.Test1.TestMethod1() in E:\ProgrammingProjects\WorkInProgress\MyProjRoot\MyProjTests\Test1.cs:line 43

EDIT 2:

After experimentation I've narrowed it down to a call to dbCtx.Database.CompatibleWithModel(bool). Whether the argument supplied is true or false makes no difference. When I commented it out no NullReferenceException is raised later in the code. I have no idea why. Calling dbCtx.Database.Exists() works fine. In addition, dbCtx.Database.Initialize(false); Also reliably produces the error (not at callsite, but on xyz.ToList()).

  • 1
    Where does the stack trace for the exception indicate that it is actually being thrown? It seems rather screwy regardless but that might be interesting to know. Also, does the issue persist if you don't allow lazy loading? – jmcilhinney Jun 21 '17 at 04:07
  • @jmcilhinney I've updated the question with the exception details. Setting lazy loading to false still yields the same results. – Ben Manning Jun 21 '17 at 04:14
  • 2
    Maybe this is because `Customer` is `sealed` and EF is trying to create a change tracking proxy and it fails? See this answer https://stackoverflow.com/a/5599270/235671 – t3chb0t Jun 21 '17 at 04:19
  • @t3chb0t I had tried that before, adding or removing `sealed` doesn't seem to make any difference. – Ben Manning Jun 21 '17 at 04:23

1 Answers1

0

I have found a resolution. As best as I can tell, it is caused when certain methods like Database.Initialize() and Database.CompatibleWithModel() are called from outside the scope of a InitializeDatabase() method of a child of IDatabaseInitializer. Perhaps there is some unknown side effect involved with those methods..?

I'll include an example specialization of IDatabaseInitializer just for clarity for anyone else who may stumble across this problem:

public class CreateOrMigrateDatabaseInitializer<TContext, TConfiguration>
    : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext>
    where TContext : DbContext
    where TConfiguration : DbMigrationsConfiguration<TContext>, new()
{

    void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context)
    {
        if (context.Database.Exists())
        {
            if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false))
            {
                var migrationInitializer = new MigrateDatabaseToLatestVersion<TContext, TConfiguration>(true);
                migrationInitializer.InitializeDatabase(context);
            }
        }

        base.InitializeDatabase(context);
    }
}