2014-05-16 16 views
6

Tôi có ba lớp thực thể liên quan sau đây:Strange null trong LINQ truy vấn với EF

public class ContextInstance 
{ 
    public Int64 Id { get; set; } 

    public virtual List<ContextParamValue> 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; } 
} 

Tôi đã thiết lập mối quan hệ thông thạo như sau:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 
     modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>(); 

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

tôi có lớp "Trình trợ giúp" sau, phương thức ParamValueToList trong đó liên tục ném một ngoại lệ không tham chiếu null :

public class RuntimeHelper : IDisposable 
    { 
     DocumentDbContext db; 

     ConfigurationHelper ch; 

     private RuntimeHelper() 
     { 
     } 

     public RuntimeHelper(DocumentDbContext context) 
     { 
      db = context; 
      ch = new ConfigurationHelper(context); 
     } 

     public List<ContextParamValue> 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<string, string> d = new Dictionary<string, string>(); 

      for (int i = 0; i < ParamNames.Length; i++) 
      { 
       string pName = ParamNames[i]; 
       string pValue = ParamValues[i]; 
       d.Add(pName, pValue); 
       Trace.TraceInformation("ParamValuesToList Key: " + pName + "; Value: " + pValue + ";"); 
      } 

      Trace.TraceInformation("Value of db:" + db.ContextParamValues.ToString()); 

      var cpvList = db.ContextParamValues 
       .Include(x => x.ContextParam) 
       .ToArray<ContextParamValue>(); 

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

      Trace.TraceInformation("-- ParamValuesToList executed --"); 

      return lst; 
     } 

     public List<ContextInstance> GetContextInstances(List<ContextParamValue> 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<ContextInstance>() // <-- Allows boolean method as part of LINQ query 
        .Where(ci => IsSubset(ci.ContextParamValues, ContextParamValues)) 
        .ToList<ContextInstance>(); 
      else 
       return db.ContextInstances 
        .Include(x => x.ContextClass) 
        .Include(x => x.ContextParamValues.Select(p => p.ContextParam)) 
        .Include(x => x.Documents) 
        .AsNoTracking() 
        .AsEnumerable<ContextInstance>()// <-- Allows boolean method as part of LINQ query 
        .Where(ci => IsSubset(ci.ContextParamValues, ContextParamValues)) 
        .ToList<ContextInstance>(); 
     } 

     public List<ContextInstance> GetContextInstances(string[] ParamNames, string[] ParamValues, bool AsNoTracking = false) 
     { 

      return GetContextInstances(ParamValuesToList(ParamNames, ParamValues), AsNoTracking); 
     } 
} 

Tuyên bố cụ thể từ các phương pháp trên được ném lỗi là

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

Trường hợp ngoại lệ tham chiếu null là KHÔNG ném dưới điều kiện sau đây:

  • Có tồn tại, đối với một ContextInstance cụ thể, chỉ 1 ContextParamValue
  • Ví dụ, ContextParamValue.ContextParam.Name = "ClientId" và ContextParamValue1.Value = "1"

Tham chiếu ngoại lệ vô được ném dưới điều kiện sau đây:

  • Có tồn tại, đối với một ContextInstance cụ thể, hai hoặc nhiều ContextParamValues ​​
  • Ví dụ, ContextParamValue1.ContextParam.Name = "ClientId" và ContextParamValue1.Value = "1" PLUS ContextParamValue2.Co ntextParam.Name = "MotivationId" và ContextParamValue2.Value = "1".

tôi có thể xác nhận điều sau về phương pháp helper trong câu hỏi:

  • d không phải là null cũng không chứa bất kỳ KeyValuePairs với null giá trị
  • cpvList không phải là null và không có sản phẩm nào khi lỗi xảy ra.
  • ContextParam không tải cho cha mẹ ContextParamValue thực thể trong mọi trường hợp (nó chỉ tải cho trường hợp ContextParamValue đầu tiên nhưng đối với các trường hợp tiếp theo chỉ có một giá trị null được nạp).
  • Không có mục nhập ContextParam rỗng trong cơ sở dữ liệu ... Tất cả ContextParamValues ​​có một mục nhập ContextParam.

Sau đây dấu vếtstacktrace thông tin được tạo ra trong thời gian chạy:

Ứng dụng: 2014-05-16T19: 00: 20 PID [4800] Lỗi System.NullReferenceException: Tham chiếu đối tượng không được đặt thành một thể hiện của một đối tượng. Ứng dụng: tại DocumentManagement.Helpers.RuntimeHelper. <> c__DisplayClass28.b__27 (ContextParamValue pv) trong c: \ Users \ xxx \ Dropbox \ xxx \ Active Dự án \ xxx \ DocumentManagement \ Helpers \ DocsHelper_RT.cs: dòng 229 Ứng dụng: tại System.Linq.Enumerable.WhereArrayIterator1. MoveNext() ứng dụng: ứng dụng tại System.Collections.Generic.List1..ctor (IEnumerable1 bộ sưu tập): tại System.Linq.Enumerable.ToList [TSource] (IEnumerable1 nguồn) ứng dụng: tại DocumentManagement.Helpers.RuntimeHelper .ParamValuesToList (Chuỗi [] ParamNames, String [] ParamValues) trong c: \ Users \ xxx \ Dropbox \ xxx \ Active Dự án \ xxx \ DocumentManagement \ Helpers \ DocsHelper_RT.cs: dòng 228 Ứng dụng: tại DocumentManagement.Helpers.RuntimeHelper.GetContextInstances (Chuỗi [] ParamNames, String [] ParamValues, Boolean AsNoTracking) trong c: \ Users \ xxx \ Dropbox \ xxx \ Active Dự án \ xxx \ DocumentManagement \ Helpers \ DocsHelper_RT.cs: dòng 262 Ứng dụng: tại xxx.Controllers.ClientController.LoadStep2 (Int64 ClientId, String Error) trong c: \ Users \ xxx \ Dropbox \ xxx \ Active Projects \ xxx \ xxx \ Views \ Client \ ClientController.cs: line 198

enter image description here

enter image description here

+0

nếu "lst" không phải là null, không có nghĩa rằng truy vấn đầu tiên của bạn đã thành công? Nếu nó ném một ngoại lệ, "lst" sẽ không được chỉ định ở nơi đầu tiên. Bạn liệt kê truy vấn bằng cách gọi ToList() trên đó, do đó truy vấn sẽ được thực hiện thành công với "lst" được gán, hoặc ném một ngoại lệ, để lại "lst" chưa được gán. –

+0

Để gỡ lỗi, tôi đề nghị bạn đặt biến vị ngữ của bạn từ mệnh đề Where trong một phương thức riêng biệt (chúng ta hãy gọi nó là "predicate") và thay đổi mệnh đề Where thành '.Where (predicate)'. Đặt một trình xử lý ngoại lệ trong phương thức dự đoán của bạn cho phép bạn thiết lập một điểm ngắt sẽ được kích hoạt khi một ngoại lệ xảy ra - điều này sẽ giúp bạn chẩn đoán nơi bạn có một giá trị null. Bởi vì, một điều dường như chắc chắn dựa trên lời giải thích của bạn: Truy vấn LINQ dường như giải quyết một tham chiếu null ở đâu đó ... – elgonzo

+0

hiển thị stacktrace – Pawel

Trả lời

5

Cách duy nhất mà mã của bạn sẽ ném một ngoại lệ sẽ là nếu pv.ContextParam là null, bởi vì đó là nơi duy nhất bạn đang dereferencing cái gì đó có thể gây ra một ngoại lệ con trỏ null.

Điều này sẽ xảy ra nếu bạn có các bản ghi ContextParamValues ​​không có bản ghi ContextParam tương ứng, do đó ContextParam sẽ là rỗng. Vì chúng tôi không thể thấy mô hình dữ liệu của bạn, bạn sẽ phải kiểm tra điều đó.

Thêm dòng mã này và kiểm tra trong debugger để xem nếu nó là sự thật:

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

EDIT (loại bỏ tất cả các bước trung gian, kiểm tra lịch sử nếu bạn quan tâm):

Vâng, điều này không thực sự trả lời câu hỏi, nhưng nó sẽ giải quyết vấn đề của bạn. Hãy viết lại mã của bạn để đơn giản và hiệu quả hơn. Nếu tôi đọc mã của bạn đúng, tất cả những gì bạn đang muốn làm là trả về ContextInstances có liên kết ContextValueParams với cặp tên/giá trị được cung cấp, đúng không?

Tại sao không chỉ làm điều này (thêm bao gồm như bạn thấy phù hợp):

public List<ContextInstance> 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(); 
} 
Các vấn đề liên quan