Since you are calling .AsEnumerable() , queries are not evaluated using LINQ to Entities, but rather LINQ to Objects.
This means that the first one is likely to complete two rounds: one to pull all the cars and one to pull all the CarTypes. He then performs the connection locally using any LINQ algorithm for the objects used for such operations.
The second probably does N + 1 round-trip, where N is the number of CarTypes. You will travel around the world to capture all the cars, and every time one of these cars has a CarTypeId that the Entity Framework has not loaded yet, it returns to the database to select this CarType.
If you use the SQL tab in LINQPad, you can see all the LINQ queries that are executed by your program.
The best practice that you should apply in this case is not to call .AsEnumerable() on an Entity Framework object set. Instead, compose your entire query, and then call .ToList() at the end to commit the results. You are probably calling .AsEnumerable() as a workaround because Guid.Parse() does not work inside the LINQ to Entities query, but you can easily remove this part from the query. In LINQPad, press Ctrl - 2 to switch to C # Statement (s) mode, and then run the query as follows:
var guid = Guid.Parse("0501cc96-5610-465d-bafc-16b30890c224"); var carTypeNames = (from c in Cars where c.CarId == guid select new CarType { Name = c.CarType.Name }).ToList(); carTypeNames.Dump();
These two queries should have roughly equivalent performance when executed correctly, so you should prefer navigation properties, as they are more concise and readable. Or, according to your preference, you can turn the query around and make it based on the CarType collection:
var guid = Guid.Parse("0501cc96-5610-465d-bafc-16b30890c224"); var carTypeNames = (from ct in CarTypes where ct.Cars.Any(c => c.CarId == guid) select new CarType { Name = c.CarType.Name }).ToList(); carTypeNames.Dump();
Update
Avoid creating an entity object as follows:
public class CarTypeSummary { public string Name{get;set;} } void Main() { var guid = Guid.Parse("0501cc96-5610-465d-bafc-16b30890c224"); var carTypeNames = (from ct in CarTypes where ct.Cars.Any(c => c.CarId == guid) select new CarTypeSummary { Name = c.CarType.Name }).ToList(); carTypeNames.Dump(); }
In production code, it is often nice to separate your API from the underlying data type to give you more room for change, without having to change the code anywhere.
public interface ICarTypeSummary{string Name{get;}} public class CarTypeSummary : ICarTypeSummary { public string Name{get;set;} } public ICarTypeSummary GetCarTypeSummaryForCar(Guid guid) { return (from ct in CarTypes where ct.Cars.Any(c => c.CarId == guid) select new CarTypeSummary { Name = c.CarType.Name }).FirstOrDefault(); }
Thus, if in the future you decide that you want to return the real CarType in order to use the Entity Framework caching mechanisms, you can change your implementation without entering the API: