bob*_*tko 8 linq optimization navigational-properties entity-framework-4.1
我正在尝试更多地了解Linq查询和实体框架(4.1).请查看以下两个查询.两个查询都返回汽车类型名称(CarType.Name)
在第一个查询中,我使用了join并忽略了导航属性CarType
from c in Cars.AsEnumerable().
Where(e => e.CarId == Guid.Parse("0501cc96-5610-465d-bafc-16b30890c224"))
join ct in CarTypes on c.CarTypeId equals ct.CarTypeId
select new CarType {
Name = ct.Name
}
Run Code Online (Sandbox Code Playgroud)
第二,我使用了导航属性CarType
from c in Cars.AsEnumerable()
where c.CarId == Guid.Parse("0501cc96-5610-465d-bafc-16b30890c224")
select new CarType {
Name = c.CarType.Name
}
Run Code Online (Sandbox Code Playgroud)
我在LinqPad中运行了两个,因此有Guid.Parse函数.
当我运行这些时,第一个语句运行得更快.LinqPad报告00:00:036.第二个语句运行较慢,LinqPad报告00:00:103
看看结果似乎Linq查询使用连接而不是导航属性更快.真的是这样吗?请有人对此表示赞赏.在编写Linq查询时,是否有任何一般指导,我应该遵循的最佳实践?
谢谢
由于您正在调用.AsEnumerable()
,因此不使用LINQ to Entities来评估查询,而是使用LINQ to Objects.
这意味着第一个可能会进行两次往返:一次是拉动所有的汽车,一次是拉动所有的CarTypes.然后它使用LINQ to Objects用于此类操作的任何算法在本地执行连接.
第二个可能是进行N + 1次往返,其中N是CarTypes的数量.你进行往返以获取所有汽车,然后每当其中一辆汽车具有尚未加载实体框架的CarTypeId时,它会返回数据库以选择该CarType.
如果使用LINQPad中的SQL选项卡,则可以查看程序正在执行的所有LINQ查询.
在这种情况下应该应用的最佳实践是不要调用.AsEnumerable()
Entity Framework对象集.相反,编写整个查询,然后.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();
Run Code Online (Sandbox Code Playgroud)
给出的两个查询在完成时应具有大致相同的性能,因此您应该更喜欢导航属性,因为它们更简洁,更易于阅读.或者,根据您的偏好,您可以转换查询并使其基于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();
Run Code Online (Sandbox Code Playgroud)
避免创建像这样的实体对象:
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();
}
Run Code Online (Sandbox Code Playgroud)
在生产代码中,将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();
}
Run Code Online (Sandbox Code Playgroud)
这样,如果您将来决定只返回一个实际的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));
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1852 次 |
最近记录: |