使用EF的LINQ查询中的奇怪空引用exception

我有以下三个相关的实体类:

public class ContextInstance { public Int64 Id { get; set; } public virtual List ContextParamValues { get; set; } } public class ContextParamValue { public Int64 Id { get; set; } public virtual Int64 ContextParamId { get; set; } public virtual ContextParam ContextParam { get; set; } public virtual ContextInstance ContextInstance { get; set; } public virtual Int64 ContextInstanceId { get; set; } public string Value { get; set; } } public class ContextParam { public Int64 Id { get; set; } [Required] public string Name { get; set; } [DefaultValue("")] public string Description { get; set; } } 

我建立了如下流畅的关系:

 modelBuilder.Conventions.Remove(); modelBuilder.Conventions.Remove(); modelBuilder.Entity() .HasMany(ci => ci.ContextParamValues) .WithRequired(cpv => cpv.ContextInstance) .HasForeignKey(cpv => cpv.ContextInstanceId) .WillCascadeOnDelete(true); 

我有以下“Helper”类,其ParamValueToList方法间歇性地抛出空引用exception

 public class RuntimeHelper : IDisposable { DocumentDbContext db; ConfigurationHelper ch; private RuntimeHelper() { } public RuntimeHelper(DocumentDbContext context) { db = context; ch = new ConfigurationHelper(context); } public List ParamValuesToList(string[] ParamNames, string[] ParamValues) { Trace.TraceInformation("-- ParamValuesToList invoked --"); if (ParamNames != null && ParamNames.Length != ParamValues.Length) throw new System.ArgumentException("ParamNames and ParamValues may not differ in length."); Dictionary d = new Dictionary(); for (int i = 0; i  x.ContextParam) .ToArray(); List lst = cpvList .Where(pv => d.Contains(new KeyValuePair(pv.ContextParam.Name, pv.Value))) //.Where(pv => true == true) .ToList(); Trace.TraceInformation("-- ParamValuesToList executed --"); return lst; } public List GetContextInstances(List ContextParamValues, bool AsNoTracking = false) { if (!AsNoTracking) return db.ContextInstances .Include(x => x.ContextClass) .Include(x => x.ContextParamValues.Select(p => p.ContextParam)) .Include(x => x.Documents) .AsEnumerable() //  IsSubset(ci.ContextParamValues, ContextParamValues)) .ToList(); else return db.ContextInstances .Include(x => x.ContextClass) .Include(x => x.ContextParamValues.Select(p => p.ContextParam)) .Include(x => x.Documents) .AsNoTracking() .AsEnumerable()//  IsSubset(ci.ContextParamValues, ContextParamValues)) .ToList(); } public List GetContextInstances(string[] ParamNames, string[] ParamValues, bool AsNoTracking = false) { return GetContextInstances(ParamValuesToList(ParamNames, ParamValues), AsNoTracking); } } 

抛出错误的上述方法的具体说法是

 List lst = cpvList .Where(pv => d.Contains(new KeyValuePair(pv.ContextParam.Name, pv.Value))) .ToList(); 

在以下条件下引发空引用exception:

  • 对于给定的ContextInstance,只存在1个ContextParamValue
  • 示例,ContextParamValue.ContextParam.Name =“ClientId”和ContextParamValue1.Value =“1”

在以下条件下抛出空引用exception:

  • 对于给定的ContextInstance,存在两个或更多个ContextParamValues
  • 示例,ContextParamValue1.ContextParam.Name =“ClientId”和ContextParamValue1.Value =“1”PLUS ContextParamValue2.ContextParam.Name =“MotivationId”和ContextParamValue2.Value =“1”。

我可以确认以下关于有问题的帮助方法:

  • d不为null,也不包含任何具有空值的keyvaluepairs
  • 发生错误时,cpvList不为null且不为空。
  • 在所有情况下,ContextParam都不会为父ContextParamValue实体加载(它仅为第一个ContextParamValue实例加载,但对于后续实例,仅加载空值)。
  • 数据库中没有空的ContextParam条目…所有ContextParamValues都有一个ContextParam条目。

运行时期间会生成以下跟踪堆栈 跟踪信息:

应用程序:2014-05-16T19:00:20 PID [4800]错误System.NullReferenceException:对象引用未设置为对象的实例。 应用程序:在DocumentManagement.Helpers.RuntimeHelper。 c__DisplayClass28.b__27(ContextParamValue pv)中的c:\ Users \ xxx \ Dropbox \ xxx \ Active Projects \ xxx \ DocumentManagement \ Helpers \ DocsHelper_RT.cs:第229行应用程序:at System。 Linq.Enumerable.WhereArrayIterator1.MoveNext()Application:at System.Collections.Generic.List1..ctor(IEnumerable1 collection)Application:at System.Linq.Enumerable.ToList [TSource](IEnumerable1 source)Application:at DocumentManagement.Helpers。 RuntimeHelper.ParamValuesToList(String [] ParamNames,String [] ParamValues)在c:\ Users \ xxx \ Dropbox \ xxx \ Active Projects \ xxx \ DocumentManagement \ Helpers \ DocsHelper_RT.cs:第228行应用程序:在DocumentManagement.Helpers.RuntimeHelper。 GetContextInstances(String [] ParamNames,String [] ParamValues,Boolean AsNoTracking)在c:\ Users \ xxx \ Dropbox \ xxx \ Active Projects \ xxx \ DocumentManagement \ Helpers \ DocsHelper_RT.cs:第262行应用程序:at xxx.Controllers.ClientController c:\ Users \ xxx \ D中的.LoadStep2(Int64 ClientId,String Error) ropbox \ xxx \ Active Projects \ xxx \ xxx \ Views \ Client \ ClientController.cs:第198行

在此处输入图像描述

在此处输入图像描述

如果pv.ContextParam为null,则代码抛出exception的唯一方法是,因为这是您解除引用可能导致空指针exception的内容的唯一位置。

如果您具有ContextParamValues记录而没有相应的ContextParam记录,则会发生这种情况,因此ContextParam将为null。 由于我们无法看到您的数据模型,因此您必须检查它。

添加这行代码并检查调试器以查看它是否为真:

 bool containsNulls = db.ContextParamValues .Include(x => x.ContextParam) .Any(x => x.ContextParam == null) 

编辑(删除所有中间步骤,如果您感兴趣,请查看历史记录):

嗯,这实际上并没有回答这个问题,但它可以解决你的问题。 让我们重写您的代码,使其更简单,更高效。 如果我正确地阅读了您的代码,那么您要做的就是返回将ContextValueParams与提供的名称/值对相关联的ContextInstances,对吗?

为什么不这样做(根据需要添加包括):

 public List GetContextInstances( string[] ParamNames, string[] ParamValues, bool AsNoTracking = false) { var p = ParamNames.Zip(ParamValues, (a,b) => a+b); var ctx = db.ContextInstances .Where(x => p.All(y => x.ContextParamValues .Select(z => z.ContextParam.Name + z.Value).Contains(y))); return (AsNoTracking ? ctx.AsNoTracking() : ctx).ToList(); }