Cannot pass an object of type "WhereEnumerableIterator`1" to enter "System.Collections.Generic.ICollection`1

I have the following code (note that this is truncated to the corresponding part, the actual request is much more complicated):

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 a function within the update service program for an application to get any menu that has either changed itself or any of its descriptions since the last update service call.

Clarification: the function should return every menu that has changed since the last call. In addition, he needs to return each changed description of each changed menu. but he must leave the Description unchanged.

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 a new DateTime (2014, 12, 15), this is the structure that it should 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 () it looks 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; } 

Menus and descriptions were automatically created by the entity infrastructure as follows:

 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 included inside the ICollection<Description> defined by the entity infrastructure.

When I try to do this myself, I get a runtime error in the header:

 m => m.Descriptions = (ICollection<Description>)m.Descriptions.Where(...) 

Now I understand why this error occurs. Expression Description Where has not yet been evaluated, so what is supposed to be distinguished from ICollection<Description> is not yet IEnumerable<Description> , but whereEnumerableIterator is. Right now, I am throwing the Where expression into a list that immediately gets evaluated, and then discarded on ICollection<Description> .

 m => m.Descriptions = (ICollection<Description>)m.Descriptions.Where(...).ToList() 

However, this is just a workaround that kills the benefits of LINQ expression and is also just ugly. I could write an extension method WhereCollection<T>(...) calling Where<T> and returning ICollection<T> , but that would not change very much, I would have to throw inside, which either leads to the same error , or calls ToList() internally.

Is there an elegant solution to this problem without causing the Where evaluate before the LINQ statement is evaluated?

+5
source share
2 answers

"This is a function within the update service for the application to get any menu that has either or any description changed since the last update service call."

So ... would you not have a little complicated Where clause in this case instead of all this?

 result = GetAll() .Where(m => lastUpdate < m.LastModified || m.Descriptions.Any(d => lastUpdate < d.LastModified); 

The problem statement mainly describes the LINQ query .;)

0
source

Is there an elegant solution to this problem without forcing the evaluation of the Where clause before evaluating the LINQ statement?

The ForEach extension makes it not elegant and is probably a problem. There is a reason why ForEach not included in Linq. Linq uses functional "clean" methods, but ForEach uses side effects.

Your GetMenus method returns an IQueryable<Menu> . Thus, if your GetAll() also returns an IQueryable<> , then your ForEach is a performance issue. The reason is that when calling Linq methods in IQueryable<> for example dc.Customers.Where(c => c.Age >= 18) , when the Linq statement is converted to SQL WHERE , so only some clients are loaded from the database. If you write dc.Customers.ForEach(c =>...) and ForEach accepts IEnumerable instead of IQueryable (as in your case), then you query in memory from now on, and not in the database.

0
source

Source: https://habr.com/ru/post/979509/


All Articles