由于您正在调用.AsEnumerable()
,因此不会使用 LINQ to Entities 评估查询,而是使用 LINQ to Objects 评估查询。
这意味着第一个可能会进行两次往返:一次拉出所有 Cars,一次拉出所有 CarTypes。然后它使用 LINQ to Objects 用于此类操作的任何算法在本地执行连接。
第二个可能是进行 N + 1 次往返,其中 N 是 CarType 的数量。您进行一次往返以获取所有汽车,然后每次其中一辆汽车具有实体框架尚未加载的 CarTypeId,它会返回数据库以选择该 CarType。
如果您使用 LINQPad 中的 SQL 选项卡,您可以看到您的程序正在执行的所有 LINQ 查询。
在这种情况下,您应该应用的最佳实践是不调用.AsEnumerable()
实体框架对象集。相反,编写整个查询,然后.ToList()
在最后调用以捕获结果。您可能将调用.AsEnumerable()
作为一种解决方法,因为Guid.Parse()
它在 LINQ to Entities 查询中不起作用,但您可以轻松地从查询中删除该部分。在 LINQPad 中,按Ctrl-2切换到 C# 语句模式,然后运行如下查询:
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();
如果正确完成,给出的两个查询应该具有大致相同的性能,因此您应该更喜欢导航属性,因为它们更简洁且更易于阅读。或者,根据您的偏好,您可以翻转查询并使其基于 CarType 集合:
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();
更新
避免像这样创建实体对象:
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();
}
在生产代码中,将 API 与底层数据类型分离通常是一个好主意,这样您就可以更灵活地更改内容,而无需在任何地方修改代码。
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();
}
这样,如果您将来决定只返回一个实际的 CarType,以利用 Entity Framework 的缓存机制,您可以更改您的实现而不会弄乱 API:
// Make the Entity class implement the role interface
public partial class CarType : ICarTypeSummary {}
public ICarTypeSummary GetCarTypeSummaryForCar(Guid guid)
{
return CarTypes.FirstOrDefault(
ct => ct.Cars.Any(c => c.CarId == guid));
}