【问题标题】:EF6 NullReferenceException with any ToList()EF6 NullReferenceException 与任何 ToList()
【发布时间】:2017-06-21 03:53:17
【问题描述】:

使用 C#、.NET 4.5.2、Entity Framework 6.1.3 和 System.Linq 我遇到了一个令人困惑的异常。异常本身似乎不包含确定引发它的原因的有用信息。

以下代码行在执行时会导致 NullReferenceException: dbCtx.Customers.ToList();

但是,以下行无异常运行并返回正确的结果: (dbCtx.Customers).ToList();

首先运行括号括起来的表达式将允许两个表单无异常地执行:

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

我还想指出,添加实体按预期工作:

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

客户实体类:

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

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

BaseEntity 类:

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

DbContext 类:

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

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

什么可能导致这种行为?

编辑: 当调用 .ToList()、.Count() 等时,任何实体都会出现此问题。

异常详情:

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

编辑 2:

经过实验后,我将其范围缩小为致电dbCtx.Database.CompatibleWithModel(bool)。提供的参数是真还是假没有区别。当我将其注释掉时,代码稍后不会引发 NullReferenceException。我不知道为什么。调用dbCtx.Database.Exists() 工作正常。 此外,dbCtx.Database.Initialize(false); 也可靠地产生错误(不是在调用点,而是在 xyz.ToList())。

【问题讨论】:

  • 异常的堆栈跟踪在哪里表明它实际上是被抛出的?无论如何,这似乎很古怪,但这可能会很有趣。另外,如果您不允许延迟加载,问题是否仍然存在?
  • @jmcilhinney 我已经用异常详细信息更新了问题。将延迟加载设置为 false 仍然会产生相同的结果。
  • 也许这是因为 Customersealed 并且 EF 正在尝试创建更改跟踪代理但它失败了?看到这个答案stackoverflow.com/a/5599270/235671
  • @t3chb0t 我之前尝试过,添加或删除 sealed 似乎没有任何区别。

标签: c# entity-framework-6 nullreferenceexception


【解决方案1】:

据我所知,这两个表达式(带和不带括号)是绝对等价的,不可能导致不同的行为。您可以通过查看生成的 IL 代码来检查这一点(方法如下:A tool for easy IL code inspection)。在像这样晦涩难懂的情况下,我通常会怀疑某种形式的多线程是罪魁祸首。尝试将有问题的代码隔离到最低限度,看看是否仍然可以重现。

【讨论】:

  • 这是一个有用的评论,但肯定不是问题的答案
  • 我已更新问题以反映进一步实验/隔离后的新发现。
  • 问题的一部分是:如何在语句周围添加括号使某些事情起作用并且没有括号事情开始破坏 - 答案是:这是不可能的,你的观察是错误的,寻找一个不同的原因,比如多线程。不远了,恕我直言。
【解决方案2】:

我找到了解决办法。据我所知,这是在IDatabaseInitializer 的子对象的InitializeDatabase() 方法范围之外调用某些方法(如Database.Initialize()Database.CompatibleWithModel())引起的。也许这些方法涉及一些未知的副作用..?

我将包含一个 IDatabaseInitializer 的特化示例,只是为了让其他可能偶然发现此问题的人更清楚:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多