Click here to Skip to main content
15,888,351 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hello everyone,
Short Story :
C#
UP2Entities up2ent = new UP2Entities();
List<ViewAllItem> items = new List<ViewAllItem>();
           items = up2ent.ViewAllItems.ToList();

            lstYesTag = pnlTags.Children.OfType<Label>().Where(l1 => ((SolidColorBrush)l1.Background).Color == Colors.Green).Select(l => Convert.ToInt32(l.Tag)).ToList();
            lstNoTag = pnlTags.Children.OfType<Label>().Where(l1 => ((SolidColorBrush)l1.Background).Color == Colors.Red).Select(l => Convert.ToInt32(l.Tag)).ToList();
            if (lstYesTag.Count > 0) items = items.Where(r => up2ent.Items.Find(r.ItemID).ItemsTags.All(itg => lstYesTag.Contains(itg.TagID))).ToList();
            if (lstNoTag.Count > 0) items = items.Where(r => up2ent.Items.Find(r.ItemID).ItemsTags.All(itg => !lstNoTag.Contains(itg.TagID))).ToList();



when i run this code it takes sooo long that the debugger after 60 seconds breaks !!

Longer Version :
i have a view (ViewAllItems) that displays a list of item information
the view has a field ItemId
i have a table (Items) that also have a field ItemId and is related to table (ItemsTags) one-to-many

i need to filter the view base on tags the user inputs as two lists of integer
lstYesTag ==> the tag ids that the item need to have
lstNoTag ==> the tag ids that the need Not to have


after some searching i came to assume that the problem is in the way linq translates my query to SQL query
so i tried other forms of query by extending the Item by :

C#
public static class MyExtensions
   {
       public static bool IsTagsAll(this Item myItem , List<int> tagsList)
       {
           return   myItem.ItemsTags.Select(itg => itg.TagID).ToList().Intersect(tagsList).ToList().Count == tagsList.Count;

       }
}

and changed the code to :
if (lstYesTag.Count > 0) items = items.Where(r => up2ent.Items.Find(r.ItemID).IsTagsAll(lstYesTag)).ToList();


but still got the same result

i am new to entityframework ,
i used similar methods with linq to datasets before and it worked perfectly

i got some recommendations not to use Views , but the reason i used it was that the Query i used to get the data in the first place was too slow despite being fairly simple

any suggestions to achieve my goal with any other means ??

Thanks everyone

Update ::
i was told that the problem is my too many ToList() calls ,
so i made a simple test,
i minimized the ToList use , and tried the same logic once with linq to entities and another time with linq to dataset

the result time in first run :
in linq to entities was 13000 milliseconds
in linq to datasets was 95 milliseconds
second :
566
93
third
98
80
the testing code :
UP2 ds = new UP2();
       UP2TableAdapters.ItemsTableAdapter daItems = new UP2TableAdapters.ItemsTableAdapter();
       UP2TableAdapters.ItemsTagsTableAdapter daItemsTag = new UP2TableAdapters.ItemsTagsTableAdapter();
       UP2TableAdapters.TagsTableAdapter daTags = new UP2TableAdapters.TagsTableAdapter();
       UP2Entities up2Ent = new UP2Entities();
 private void btn_Click(object sender, RoutedEventArgs e)
       {
           populateEntity();

           populateDataset();


       }
       void populateEntity()
       {
           lst.Items.Clear();
           Stopwatch st = new Stopwatch();
           st.Start();
           List<Item> items = new List<Item>();
           items = up2Ent.Items.Take(500).ToList();
           prgrss.Maximum = up2Ent.Items.Count();
           foreach (Item i in items)
           {
               lst.Items.Add(i.Name + " : ");
               foreach (ItemsTag it in i.ItemsTags)
               {
                   lst.Items.Add("    " + it.Tag.Name);
               }
               lst.Items.Add(st.ElapsedMilliseconds.ToString());
               prgrss.Value += 1;


           }
           st.Stop();
           MessageBox.Show(st.ElapsedMilliseconds.ToString());
       }
       void populateDataset()
       {
           lst.Items.Clear();
           Stopwatch st = new Stopwatch();
           st.Start();
           List<UP2.ItemsRow> items = new List<UP2.ItemsRow>();
           items = ds.Items.Take(500).ToList();
           prgrss.Maximum = up2Ent.Items.Count();
           foreach (UP2.ItemsRow i in items)
           {
               lst.Items.Add(i.Name + " : ");
               foreach (UP2.ItemsTagsRow it in i.GetItemsTagsRows())
               {
                   lst.Items.Add("    " + it.TagsRow.Name);
               }
               lst.Items.Add(st.ElapsedMilliseconds.ToString());
               prgrss.Value += 1;


           }
           st.Stop();
           MessageBox.Show(st.ElapsedMilliseconds.ToString());
       }
Posted
Updated 28-Jun-14 5:48am
v3
Comments
johannesnestler 23-Jun-14 8:50am    
Didn't read any further after seeing all your ToList calls..., rethink your strategy and minimize enumeration (happens implicit during ToList calls), It seems you aren't aware of what is happening in the background (read about LINQ basics again?). So Intersect is not your problem it's all these ToList calls
Ahmad_kelany 23-Jun-14 9:27am    
Thanks ,
i will try to modify my code & edit my question accordingly
Ahmad_kelany 28-Jun-14 11:45am    
i ran some tests & updated my question,
would you kindly give it another look please ?
thanks
johannesnestler 30-Jun-14 8:45am    
I'd like to help you, but I can't write that code for you. My initial hint to LINQ basics was targeting the fact that a query only executes if it "has to" - that means by ANY kind of enumeration. This can be - a foreach loop, a explicit call to ToList and also Count!!!. So as example in your test you are setting a (Progressbar?) Maximum by calling Count - that means to enumerate the whole collection to count it! If this is LINQ to objects - you wouldn't notice much performace problems because enumerating a in memory list is quite fast, but since your are dealing with LINQ to Entities it means to query a database, transfer data across a network, ... So you can filter, select, subselect, join, whatever - but don't force an enumeration in between. That means most times you have to change "the style" you approach a problem with linq. Don't reley on "in memory" intertermediate results - better construct one big filtered, selected, projected,... query and execute it ONCE. So try to reformulate your complete query code to minimize intermediate results and to workarround intermediate enumeration - So no ToList, Count() (!= the "Count" property of lists!) calls if possible!
As example this 3 lines of code from your test - 2 times enumerating the same list, where once would have been enough. Why not get the Count from the items (List-) variable?
items = up2Ent.Items.Take(500).ToList();
prgrss.Maximum = up2Ent.Items.Count();
foreach (Item i in items)



You see - also your IsTagsAll extension method my need "refactoring" :-)
Ahmad_kelany 30-Jun-14 11:45am    
Thanks for your time & effort :)
i guess i will need to read more about Linq to entities behinde-the-scene operations to better right my queries.
thanks for pointing me to the right direction .
have a nice day

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900