2017-06-21 51 views
7

使用C#,.NET 4.5.2,实体框架6.1.3和System.Linq我遇到了一个令人困惑的异常。这个例外本身似乎并没有包含有用的信息来确定它为什么会被提出。EF6 NullReferenceException与任何ToList()

下面的代码线被执行时在一个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" }); 

Customer实体类:

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);也可靠地产生错误(不是在callsite上,而是在xyz.ToList()上)。

+1

哪里了异常的堆栈跟踪表明,它实际上是被抛出?它似乎相当棘手,无论如何,这可能是有趣的知道。此外,如果您不允许延迟加载,问题是否会持续存在? – jmcilhinney

+0

@jmcilhinney我已经更新了异常细节的问题。将延迟加载设置为false仍然会得到相同的结果。 –

+2

也许这是因为'客户'被'密封',EF正试图创建一个更改跟踪代理,并且它失败了?看到这个答案https://stackoverflow.com/a/5599270/235671 – t3chb0t

回答

0

我找到了一个解决方案。尽我所知,这是由于子女IDatabaseInitializer的方法范围之外的某些方法如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); 
    } 
} 
0

对于我所知道的两个表达式(有和没有括号)是绝对等价的,不可能导致不同的行为。您可以通过查看生成的IL代码来检查它(以下是:A tool for easy IL code inspection)。在这种难懂的案例中,我通常会怀疑某种形式的多线程是罪魁祸首。尝试隔离违规代码到最低限度,并看看你是否仍然可以重现这一点。

+2

这是一个有用的评论,但它肯定不是问题的答案 –

+0

我已经更新了这个问题,以便在进一步的实验/分离后反映新的发现。 –

+0

问题的一个部分是:如何在一个语句中添加括号可以使某些内容有效并且没有方括号开始突破 - 答案是:这不可能,您的观察错误,寻找不同的原因,像多线程。这不是太遥远,恕我直言。 – dnickless