带有任何 ToList() 的 EF6 NullReferenceException
EF6 NullReferenceException with any ToList()
使用 C#、.NET 4.5.2、Entity Framework 6.1.3 和 System.Linq 我遇到了一个令人困惑的异常。异常本身似乎不包含确定引发异常的原因的有用信息。
执行以下代码行会导致 NullReferenceException:
dbCtx.Customers.ToList();
然而,下面的行运行无一例外,returns正确的结果:
(dbCtx.Customers).ToList();
运行 括号括起来的表达式首先将允许两种形式无一例外地执行:
var result1 = (dbCtx.Customers).ToList();
var result2 = dbCtx.Customers.ToList();
我还要注意,添加实体按预期工作:
dbCtx.Customers.Add(new Customer() { Enabled = true, Name = "Test" });
客户实体class:
public sealed class Customer : BaseEntity
{
public bool Enabled { get; set; }
[Required]
public string Name { get; set; }
}
基础实体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; }
}
什么可能导致此行为?
编辑:
当调用 .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() 上)。
据我所知,这两个表达式(带括号和不带括号)是绝对等价的,不可能导致不同的行为。您可以通过查看生成的 IL 代码来检查这一点(方法如下:A tool for easy IL code inspection)。在这种模糊的情况下,我通常会怀疑某种形式的多线程是罪魁祸首。尝试将有问题的代码隔离到最低限度,看看您是否仍然可以重现它。
我找到了解决办法。据我所知,它是在 Database.Initialize()
和 Database.CompatibleWithModel()
等某些方法从 IDatabaseInitializer
的子方法的 InitializeDatabase()
范围之外调用时引起的。也许这些方法有一些未知的副作用..?
我将包含一个 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);
}
}
使用 C#、.NET 4.5.2、Entity Framework 6.1.3 和 System.Linq 我遇到了一个令人困惑的异常。异常本身似乎不包含确定引发异常的原因的有用信息。
执行以下代码行会导致 NullReferenceException:
dbCtx.Customers.ToList();
然而,下面的行运行无一例外,returns正确的结果:
(dbCtx.Customers).ToList();
运行 括号括起来的表达式首先将允许两种形式无一例外地执行:
var result1 = (dbCtx.Customers).ToList();
var result2 = dbCtx.Customers.ToList();
我还要注意,添加实体按预期工作:
dbCtx.Customers.Add(new Customer() { Enabled = true, Name = "Test" });
客户实体class:
public sealed class Customer : BaseEntity
{
public bool Enabled { get; set; }
[Required]
public string Name { get; set; }
}
基础实体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; }
}
什么可能导致此行为?
编辑: 当调用 .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() 上)。
据我所知,这两个表达式(带括号和不带括号)是绝对等价的,不可能导致不同的行为。您可以通过查看生成的 IL 代码来检查这一点(方法如下:A tool for easy IL code inspection)。在这种模糊的情况下,我通常会怀疑某种形式的多线程是罪魁祸首。尝试将有问题的代码隔离到最低限度,看看您是否仍然可以重现它。
我找到了解决办法。据我所知,它是在 Database.Initialize()
和 Database.CompatibleWithModel()
等某些方法从 IDatabaseInitializer
的子方法的 InitializeDatabase()
范围之外调用时引起的。也许这些方法有一些未知的副作用..?
我将包含一个 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);
}
}