I have the following code (please note that this is stripped down to the relevant part, the actual query is a lot more complex):
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
This is an function within an update service routine for an App to get any menu, which either itself or any of its descriptions has changed since the update service was last called.
Clarification: The function needs to return every Menu, which has changed since the last call. Additionally, it needs to return every changed Description of every changed Menu. but it must leave out the unchanged Descriptions.
As an Example:
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) }
}
};
Now, when I call the update function with new DateTime(2014, 12, 15), this is the structure it needs to return:
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> {}
}
}
With ForEach() looking like this:
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> action) {
... // Parameter check
foreach (T item in source) {
action(item);
}
return source;
}
Menu and Description were automatically created by entity framework like this:
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; }
...
}
Unfortunately the Where
function returns an IEnumerabley<Description>
, which cannot be cast internally to the ICollection<Description>
defined by entity framework.
When I try to cast it myself like this I get the runtime error within the title:
m => m.Descriptions = (ICollection<Description>)m.Descriptions.Where(...)
Now, I do understand as to why this error is thrown. The Description's Where
expression has not yet been evaluated, so what is supposed to being cast to ICollection<Description>
is not an IEnumerable<Description>
yet, but a WhereEnumerableIterator. Right now I'm casting the Where
expression to a list, which gets evaluated immediately and then cast to ICollection<Description>
.
m => m.Descriptions = (ICollection<Description>)m.Descriptions.Where(...).ToList()
However, this is merely a workaround killing the benefits of the LINQ expression and besides, plain ugly. I could write an extension method WhereCollection<T>(...)
calling Where<T>
and returning an ICollection<T>
but that wouldn't change much, I'd have to do the cast internally which either results in the same error or calls ToList()
internally.
Is there an elegant solution to this problem, without forcing the Where
expression to evaluate before the LINQ statement gets evaluated?
"This is an function within an update service routine for an App to get any menu, which either itself or any of its descriptions has changed since the update service was last called."
So... wouldn't you have a slightly complex Where
clause in this case instead of all that?
result = GetAll()
.Where(m => lastUpdate < m.LastModified ||
m.Descriptions.Any(d => lastUpdate < d.LastModified);
Your problem statement basically described the LINQ query. ;)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With