0

I have classes that I use with EntityFramework:

public partial class BaseDocument
{
   public BaseDocument()
   {
       DocumentLinks = new List<DocumentLink>();
   }
   public int Id {set;get;}
   public virtual List<DocumentLink> DocumentLinks {set;get;}
}

public partial class Payment:BaseDocument
{

}

public partial class Bill:BaseDocument
{

}

public partial class DocumentLink
{
   public int Id{set;get;}
   public int StartDocId{set;get;}
   public int EndDocId{set;get;}

   public virtual BaseDocument StartDoc{set;get;}
   public virtual BaseDocument EndDoc{set;get;}
}

Now I select document with Linq and want to iterate through list of his DocumentLinks.

var payment = dbContext.Payments.First(t=>t.Id = id);
foreach(var link in payment.DocumentLinks)
{
   if (link is Payment)
   {
     //do something
   }
   else if (link is Bill)
   {
     //do something
   }
}

And my code works very slowly at the line if (link is Payment). After this line everything works quickly.

What is wrong?

Loofer
  • 6,841
  • 9
  • 61
  • 102
Kliver Max
  • 5,107
  • 22
  • 95
  • 148
  • Can you define what is slow? What time you measure? `link` is instantiated before `if(link ...)`, do you mean that? Or do you mean `// do something` code in `is Paymant` branch is too slow (but you didn't show it)? – Sinatr Feb 12 '16 at 11:01
  • You are probably a victim of "the n+1 problem": http://stackoverflow.com/questions/97197/what-is-the-n1-selects-issue – Rune Feb 12 '16 at 11:01

3 Answers3

2

You mean it is slow in the line that is actually executing the database query? Hint - this is why it is slow.

var payment = dbContext.Payments.First(t=>t.Id = id);

I fail to see how the payment includes the DocumentLiks - which means they are lazy loaded. Which means this happens in the foreach. And there you go. Slow.

Include them in the initial query.

TomTom
  • 61,059
  • 10
  • 88
  • 148
  • in `DocumentLinks` of my example document i don't have any Payments but its works slow on this line anyway. And i dont cleary understand how can i `include` DocumentLinks in query? – Kliver Max Feb 12 '16 at 11:03
  • 1
    If yo udo not know how to use LINQ, or entity framework, you should read the documentation. Include (in 2 versions, one taking a string, one a lambda) are part of LINQ for EF methods (System.Data.Entity namespace) and tell EF to reload related elements. THis is fundamental - the type of question I ask a junior developer for a EF job. – TomTom Feb 12 '16 at 11:05
  • correct, just wanting to mention: of course the .First() call will also execute a DB call. however, this DB call will only query this one table, since no navigation properties are queried (yet), the Joins (i.e. the table scan for FK) is happening in the foreach call. – DevilSuichiro Feb 12 '16 at 11:20
0

Not a direct answer to your question, but a suggestion that you shouldn't type-sniff like this. Polymorphism allows you to ignore the exact type of an object, use it.

Put whatever behavior you need into BaseDocument and remove the is Payment and is Bill:

var payment = dbContext.Payments[id];
foreach(var link in payment.DocumentLiks)
{
    link.DoSomething();
}
Gary McLean Hall
  • 984
  • 7
  • 16
0

This may be because of Lazy loading. In your DBContext configuration specify:

 this.Configuration.LazyLoadingEnabled = false;
S. Nadezhnyy
  • 582
  • 2
  • 6