【发布时间】:2015-02-11 07:20:42
【问题描述】:
我有以下代码(请注意,这是精简到相关部分,实际查询要复杂得多):
public IQueryable<Menu> GetMenus(DateTime lastUpdate) {
...
result = GetAll().Where(m => lastUpdate < m.LastModified)
.ForEach(m => m.Descriptions = m.Descriptions
.Where(d => lastUpdate < d.LastModified));
...
enter code here
这是更新服务例程中的一个函数,用于获取任何菜单,自上次调用更新服务以来,该菜单本身或其任何描述已更改。
澄清:该函数需要返回自上次调用以来已更改的每个菜单。此外,它需要返回每个更改的菜单的每个更改的描述。但它必须省略未更改的描述。
举例:
Menu menuA = new Menu() {
LastModified = new DateTime(2014, 12, 24),
Descriptions = new List<Description>() {
new Description() { LastModified = new DateTime(2014, 12, 24) },
new Description() { LastModified = new DateTime(2014, 12, 01) }
}
};
Menu menuB = new Menu() {
LastModified = new DateTime(2014, 12, 20),
Descriptions = new List<Description>() {
new Description() { LastModified = new DateTime(2014, 12, 01) }
}
};
现在,当我用 new DateTime(2014, 12, 15) 调用更新函数时,这是它需要返回的结构:
List<Menu>: {
menuA: {
LastModified: DateTime(2014, 12, 24),
Descriptions: List<Description> {
Description: {
LastModified: DateTime(2014, 12, 24),
}
}
},
menuB: {
LastModified: DateTime(2014, 12, 20),
Descriptions: List<Description> {}
}
}
ForEach() 看起来像这样:
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> action) {
... // Parameter check
foreach (T item in source) {
action(item);
}
return source;
}
菜单和描述是由实体框架自动创建的,如下所示:
public partial class Menu {
...
public System.DateTime LastModified { get; set; }
public virtual ICollection<Description> Descriptions { get; set; }
...
}
public partial class Description {
...
public System.DateTime LastModified { get; set; }
public virtual Menu Menu { get; set; }
...
}
不幸的是,Where 函数返回一个IEnumerabley<Description>,它不能在内部强制转换为实体框架定义的ICollection<Description>。
当我尝试像这样自己投射时,标题中出现运行时错误:
m => m.Descriptions = (ICollection<Description>)m.Descriptions.Where(...)
现在,我明白为什么会抛出这个错误了。描述的Where 表达式尚未被评估,因此应该转换为ICollection<Description> 的不是IEnumerable<Description>,而是一个WhereEnumerableIterator。现在我正在将Where 表达式转换为一个列表,该列表会立即被评估,然后转换为ICollection<Description>。
m => m.Descriptions = (ICollection<Description>)m.Descriptions.Where(...).ToList()
但是,这仅仅是一种解决方法,扼杀了 LINQ 表达式的好处,而且还很丑陋。我可以编写一个扩展方法WhereCollection<T>(...) 调用Where<T> 并返回一个ICollection<T> 但这不会有太大变化,我必须在内部进行强制转换,这要么导致相同的错误,要么在内部调用ToList()。
对于这个问题,是否有一个优雅的解决方案,而不是在评估 LINQ 语句之前强制 Where 表达式进行评估?
【问题讨论】:
-
你的
ForEach方法是什么,它显然会返回一些东西? -
有道理。你知道
Menu.Descriptions属性是否被Entity Framework懒加载了吗? -
如果我理解正确,virtual 关键字声明延迟加载,所以是的,
Menu.Descriptions是延迟加载的。
标签: c# linq ienumerable icollection