Reputation: 1148
I'm working on a code-first EF6 project using MySql. I have setup my data classes such that their related data objects should be lazy-loaded, but they seem to run very slowly each time they are called.
This is an example of one of my entity classes:
[Table("Carrier")]
public class DBCarrier
{
[Key]
[Column("CarrierId")]
public int carrierId { get; set; }
[MaxLength(128), Required]
public string CarrierName { get; set; }
public virtual ICollection<DBDepot> Depots { get; set; }
public virtual ICollection<DBZone> Zones { get; set; }
}
But when I call the Zones
on an object of this class like this (this was all in one statement, but I separated it out to try and find out where the problem was):
ICollection<DBZone> zones = carrier.Zones;
IEnumerable<DBZone> zones1 = zones.Where(x => x.Postcode == postcode);
return zones.Select(x => x.ZoneName).FirstOrDefault();
Every time I call carrier.Zones
it takes about 8 seconds to run. I thought that by defining zones
as an ICollection would delay execution of the database query.
Upvotes: 1
Views: 913
Reputation: 14498
ICollection
means the data is definitely already materialized in memory. The only way to get your desired behavior is to use an interface which supports deferred execution and there's only two:
Expressions
and is used to fire queries upon remote data sources (for example sql database).Delegates
and is used to fire queries upon datasets already in memory.Using any of these 2 interface types will cause your query to be executed when you iterate the data using First()
, FirstOrDefault()
, ToList()
or ToArray()
.
But seeing you only need the names of the zones your query is pretty inefficient. You fetch the whole DBZone
object but you only take the name, I would suggest just fetching the names:
return carrier.Zones.FirstOrDefault(z => z.Postcode == postcode).Select(z => z.ZoneName);
For this you need to keep carrier
as an IQueryable
of course.
Edit: Just noticed "Every time I call carrier.Zones it takes about 8 seconds to run."
This is the expected behavior for lazy loading. The data will get fetched from the database when you access it => Database call for the carrier, and another call to database for the zones when you access them. This can be avoided using eager loading (Include
method) to get all the desired data in one trip to the database.
Upvotes: 2