我有一个层次结构,我从数据库中提取并尝试使用LINQ
进行恢复。当我对集合运行LINQ
查询时,它似乎没有击中我的祖母对象。
我的对象的层次结构如下
One Project -> Many Sections
One Section -> Many BidItems
One BidItem -> Many SubItems
它们都通过数据库中的外键关联并映射到我的模型对象。以下是模型的简化版本。
public class Section
{
public int SectionId { get; set; }
public int ProjectId { get; set; }
}
public class BidItem
{
public int BidItemId { get; set; }
public int SectionId { get; set; }
}
public class SubItem
{
public int SubItemId { get; set; }
public int BidItemId { get; set; }
}
public class SectionViewModel : BaseChangeNotify
{
private readonly Section section;
private readonly List<BidItemViewModel> bidItems;
public SectionViewModel(Project project, Section section)
{
var repository = new ProjectRepository();
this.section = section;
this.bidItems = new List<BidItemViewModel>(
(from item in repository.GetBidItemsBySectionId(section.SectionId)
select new BidItemViewModel(project, item)).ToList());
}
public SectionViewModel(Project project, Section section, List<BidItemViewModel> bidItemsForSection)
{
this.section = section;
this.bidItems = bidItemsForSection;
}
}
public class BidItemViewModel : BaseChangeNotify
{
private BidItem bidItem;
private List<SubItem> subItems;
public BidItemViewModel(Project project, BidItem bidItem, List<SubItem> subItems = null)
{
var repository = new ProjectRepository();
this.bidItem = bidItem;
if (subItems == null)
{
subItems = repository.GetSubItemsByBidItemId(bidItem.BidItemId);
}
this.subItems = subItems;
}
}
您可以在每个视图模型的一个构造函数中看到,我正在访问存储库以获取子对象。我想重写它,因为它表现不佳。可能有十几个sections
,每个{100} BidItems
。每个BidItem
可以有100 + SubItems
。因此,对于有5个部分的项目,我在应用程序启动期间将数据库命中50,000次(大约需要2.9秒)。
我已经重构了它,以便我只进行3次调用,一次是为项目提取所有部分,一次是项目中的所有BidItems
,另一次是项目中的所有SubItems
。现在我需要重建层次结构。
我最初尝试使用Lambda:
List<Section> projectSections =
repository.GetSectionsByProjectId(ProjectId).Where(section => section.SectionId != 0).ToList();
List<BidItem> bidItemCollection = repository.GetBidItemsByProjectId(ProjectId);
List<SubItem> subItemCollection = repository.GetSubItemsByProjectId(ProjectId);
// After the database calls so I can test actual reconstruction performance.
timer.Start();
foreach (var sectionViewModel in projectSections.Select(section => new SectionViewModel(project, section)))
{
Parallel.ForEach(bidItemCollection
.Where(bidItem => bidItem.SectionId == sectionViewModel.SectionId), bidItem =>
{
var bidItemViewModel = new BidItemViewModel(project, bidItem,
subItemCollection.Where(subItem => subItem.BidItemId == bidItem.BidItemId).ToList());
sectionViewModel.BidItems.Add(bidItemViewModel);
});
sectionViewModels.Add(sectionViewModel);
}
timer.Stop();
这很好用,但很慢。我的原始方法在启动期间需要2.9秒才能返回所有Sections,BidItems和SubItems。 Lambda花了2.3秒。然后我尝试了LINQ查询。
List<Section> projectSections =
repository.GetSectionsByProjectId(ProjectId).ToList();
List<BidItem> bidItemCollection = repository.GetBidItemsByProjectId(ProjectId);
List<SubItem> subItemCollection = repository.GetSubItemsByProjectId(ProjectId);
timer.Start();
sectionViewModels = new List<SectionViewModel>(
from section in projectSections
select new SectionViewModel(
project,
section,
bidItemCollection.Where(c => c.SectionId == section.SectionId)
.Select(
bidItem =>
new BidItemViewModel(project, bidItem,
new List<SubItem>(
subItemCollection.Where(subItem => subItem.BidItemId == bidItem.BidItemId))))
.ToList()));
timer.Stop();
返回速度最快,为0.3秒,但每个BidItems都包含一个空的SubItem集合。出于某种原因,我的SubItems没有像他们应该那样填充BidItem视图模型构造函数。我在subItemCollection.Where()
lambda中设置了一个断点,它永远不会被击中。
我真的很感谢我对LINQ做错的一些指导。我对LINQ有点新意,所以我知道我做错了,这是一个简单的修复。
所以看来问题是我的LINQ查询单元测试使用了错误的存储过程(就像我的Lambda一样)来获取SubItems导致零返回。我已经解决了这个问题,现在我找回了所有三种变体的匹配数字。
有趣的是现在的结果。第一种方法,500次击中数据库需要1.89秒。 Lambda需要2.3秒才能重建3个数据库查询。 LINQ需要0.70秒。对于Lambda和LINQ单元测试,我的数据库查询(使用Dapper)需要0.11秒。所以我现在有两个问题。
提前致谢!
乔纳森。
答案 0 :(得分:1)
projectSections.Select(section => new SectionViewModel(project, section))
from section in projectSections
select new SectionViewModel(
project,
section,
bidItemCollection.Where(...).ToList()
这两个调用不同的构造函数,因此执行时间不同。
只要逻辑和方法相同,两种写入方式都应该同时给出相同的结果。因为,编译器生成相同的IL。
由于我无法在您的机器上执行基准测试,因此我只会使用一般假设。
SectionViewModel.ctor(Project,Section)
和BidItemViewModel.ctor(Project,BidItem)
,因为他们会在数据库中执行更多查询。话虽如此,我会写下我的lambda如下://actually this is just your 3rd piece of code cleaned up
sectionViewModels = new List<SectionViewModel>(
projectSections.Select(
s => new SectionViewModel(project, s, bidItemCollection.Where(b => b.SectionId == s.SectionId).Select(
b => new BidItemViewModel(project, b, subItemCollection.Where(si => si.BidItemId == b.BidItemId))))));
另外,为了美观,我改变了以下的结构,以避免在Lambda中间有ToList
:
public class SectionViewModel
{
private readonly Section section;
private readonly List<BidItemViewModel> bidItems;
public SectionViewModel(Project project, Section section, IEnumerable<BidItemViewModel> bidItemsForSection)
{
this.section = section;
this.bidItems = bidItemsForSection.ToList();
}
}
public class BidItemViewModel
{
private BidItem bidItem;
private List<SubItem> subItems;
public BidItemViewModel(Project project, BidItem bidItem, IEnumerable<SubItem> subItems)
{
this.bidItem = bidItem;
this.subItems = subItems.ToList();
}
}