.net 在WPF中缓存大量实体
我正在用WPF写一份申请书。我正在使用EntityFramework5,不知道您是否可以给我一些关于如何处理以下情况的建议 我基本上只有三张桌子: 项目{ID,Name} 属性{ID,名称,类型} AttributeValue{ItemID,AttributeID,Value} 我们的客户希望为他的物品添加属性,这似乎效果很好。现在,他可以添加属性并为其项指定值。第一个问题:你认为这是一个好的设计吗 现在的问题是如何在WPF数据网格中显示项,并将所有属性显示为列。这就是我们现在所拥有的: 我们以编程方式生成列:.net 在WPF中缓存大量实体,.net,wpf,entity-framework,caching,.net,Wpf,Entity Framework,Caching,我正在用WPF写一份申请书。我正在使用EntityFramework5,不知道您是否可以给我一些关于如何处理以下情况的建议 我基本上只有三张桌子: 项目{ID,Name} 属性{ID,名称,类型} AttributeValue{ItemID,AttributeID,Value} 我们的客户希望为他的物品添加属性,这似乎效果很好。现在,他可以添加属性并为其项指定值。第一个问题:你认为这是一个好的设计吗 现在的问题是如何在WPF数据网格中显示项,并将所有属性显示为列。这就是我们现在所拥有的: 我们以
foreach (var attribute in db.Attributes)
{
datagrid.Columns.Add(new DataGridTextColumn
{
Header = attribute.Name,
Binding = new Binding { Path = new PropertyPath(string.Format("[{0}]", attribute.ID)) }
});
}
这个很好用。现在的问题是如何在Items类中实现索引器。我们有一个选项,它在处理大量数据时速度非常慢(20000个项目,400个属性,每个项目有100个值):
就像我说的,这是可行的,但会变慢。我没有总是查询AttributeValue,而是在显示之前缓存所有内容,如下所示:
var items = db.Items.AsNoTracking().ToArray();
CachedValues.Values = new Dictionary<int, Dictionary<int, AttributeValue>>(items.Length);
foreach (var item in items)
{
var attributevalues = db.AttributeValue.AsNoTracking().Where(w => w.ArtikelID == item.ID).ToArray();
CachedValues.Values[item.ID] = new Dictionary<int, AttributeValue>(attributevalues.Length);
foreach (var value in attributevalues)
{
CachedValues.Values[item.ID][value.AttributeID] = value;
}
}
很明显,初始化缓存需要一些时间(15秒),但是速度要快得多。按datagrids中的属性对项目进行排序只需一秒钟。用另一种方法,它会持续很久
我对解决方案不满意,你有什么建议吗?我愿意接受任何形式的批评(我知道这两种批评都不是好的解决办法)
谢谢
托马斯
编辑
要使第一个问题更清楚,请举一个小例子:
项目:项目1、项目2、项目3,。。。
属性:宽度、高度、速度。。。
属性值:(项目1,宽度,100),(项目1,高度,200),(项目2,宽度,100),(项目3,高度,200),(项目3,速度,40)
所以这是一个经典的多对多关系。属性可能出现在0-many项中,而项可能具有0-many属性。回答第一个问题:为什么属性和属性值不合并?AttributeValue可以有更多属性吗?我想这是一个可以有多个属性的项目 项目{ID,Name} 属性{ID,ItemID,Name,Type,Value}
我想给你们写一个问题的答案,但当我深入研究你们的问题时,我开始不确定你们在做什么。您正在尝试在数据库中创建动态表吗* 以下是我的回答:
您需要找出性能瓶颈所在。是检索数据还是显示数据 15秒对于显示的数据来说似乎太多了。这不应该超过1到2秒,IMHO 这里有几个提示: DataGrid是一个很重的控件,可以做很多事情。我建议您将ListView与GridView结合使用,并根据您必须具备的任何需求对您进行自定义 默认情况下,ListView是虚拟化的。在4.0中,如果应用分组虚拟化,将禁用。在4.5中,可以应用分组虚拟化 使用SQL事件探查器优化数据库查询。查看执行了哪些查询。如果执行大量查询,可能会导致性能下降。在EF(with.Include)中进行快速加载有助于加快速度。记住正确的索引
要评论拉迪斯拉夫·姆卡的评论,这并不是全部真相。WPF控件不支持数据虚拟化(仅当显示数据时才获取数据)。它们只支持所有数据都存在的普通虚拟化,但只呈现项目的可视部分。两者之间有很大的区别。有一些解决方案试图在WPF中实现数据虚拟化 您的数据模型是一个非常通用的解决方案,有支持和反对它的观点,但是如果没有更多的背景,很难判断,所以我将跳过问题的这一部分 我可能已经实现了一个与您类似的缓存,但是做了一些轻微的修改
- 我不会使用全局静态缓存,而是每个实体一个字典,只缓存该实体的属性值。这样,如果实体超出范围,您就不必关心从全局缓存中删除条目,因为缓存会与实体一起被垃圾收集
- 我不会预先填充字典,而是首先尝试从缓存中获取值,如果不存在,则搜索属性值列表并根据需要更新缓存。如果应用程序表现良好,并且不能一次访问所有值(例如,因为在任何时候网格中只有一些数据可见,而其他行不可见),则这可能会在较长的时间内无缝地分布缓存填充过程,并使用户看不到它
最后一个想法-20000件物品,每个物品有400个属性?哪个用户应该能够处理一个一次最多显示8000000个值的应用程序?也许你也应该考虑重新设计用户界面和交互逻辑——没有人需要并且可以同时处理这么多信息。嘿,只是一个简短的说明,一步一步地抓取每一件东西要快得多,而不是使用虚拟化控件来执行每一个属性的子查询。(或者在WPF中是如何调用的)。这意味着不要一次加载所有数据,而是加载例如100行,并在用户向下滚动时加载下一个100行。这就像没有页面列表的分页。WPF应该对此有一些支持。我正在使用DataGrid的虚拟化功能:(EnableColumnVirtualization=“True”,EnableRowVirtualization=“True”)在我看来,它们不应该被合并。我在我编辑的帖子中更详细地解释了它。我将查看ListVIEW和SQL Primerer.Exchange。我们今天改变了设计,并使用分页来向用户显示这些项目的一小部分。我将考虑缓存机制的想法。
var items = db.Items.AsNoTracking().ToArray();
CachedValues.Values = new Dictionary<int, Dictionary<int, AttributeValue>>(items.Length);
foreach (var item in items)
{
var attributevalues = db.AttributeValue.AsNoTracking().Where(w => w.ArtikelID == item.ID).ToArray();
CachedValues.Values[item.ID] = new Dictionary<int, AttributeValue>(attributevalues.Length);
foreach (var value in attributevalues)
{
CachedValues.Values[item.ID][value.AttributeID] = value;
}
}
public static class CachedValues
{
public static Dictionary<int, Dictionary<int, ArtikelAttribut>> Values;
}
public AttributeValue this[int i]
{
get
{
AttributeValue val = null;
CachedValues.Values[ID].TryGetValue(i, out val);
return val;
}
}