ContentManager.Query using same objects across different queries

Topics: Customizing Orchard
Nov 14, 2014 at 6:23 AM
In a driver, I have a content manager query that goes out and gets some existing content types. then I inspect the objects and then process them for display based on set criteria by the parent widget content type/part. This works great if I only use 1 widget on a page.

However, if I put two widgets with slightly different parameters, I get some shapes that are effectively clones of the first query to run. For example, my test case has 1 shape on the right, two shapes on the left. Whichever object overlaps between the two queries, it's clear that the same object is being used.

It appears that content manager is caching the query from the first result set and utilizing it for the second query run. To provide a different view on the scenario: the server-side data fetch may in fact retrieve the same number of objects for run1 and run2 but additional logic in the driver may change the display the characteristics so that I can have 2 different sets of shapes. This fails at the moment because of the reused objects.

How can I avoid this situation?
Developer
Nov 14, 2014 at 6:39 AM
Can you expand on what exactly you do when you 'process them for display'? Are you setting properties on the returned content items, or the created shapes based on these content items?
Nov 14, 2014 at 6:58 PM
Edited Nov 15, 2014 at 1:04 AM
Yes.

Here's the details:
  • Custom Type with stereotype=Widget, this is designed to pull together an aggregate list of another content type
  • Type has a few properties that surface in the widget edit page
  • Corresponding custom part to allow custom driver to manage widget
In the Display method for the driver:
  • Create a query to retrieve data based on parameters
  • Inspect items and adjust properties on the objects before handing them off
// Step 1: get data based on properties on the items themselves
var query = this.contentManager.Query(VersionOptions.Published, Solutions.Promotions.Constants.ContentTypeName)
                                       .Where<MarketingPromotionRecord>(x => ((x.StartDate == null || x.StartDate <= DateTime.Now.Date) && (x.EndDate == null || x.EndDate >= DateTime.Now.Date)))
                                       .OrderBy(x => x.Priority)
                                       .OrderByDescending(x => x.EndDate)

// Step 2: look at properties on widget and use those values to further filter 
 var activeItems = query.List().Where(x => {
  // NOTE: InstanceID is a simple property setup with Guid.NewGuid() to help validate uniqueness as I was trying to figure out why I am seeing duplicate renderings
  System.Diagnostics.Debug.WriteLine("Instance ID: " + x.As<MarketingPromotionPart>().InstanceID);

  return // true or false based on property set - left out for simplicity

});

// Step 3: adjust remaining objects based on widget data
var promotionParts = activeItems.Select(x => x.As<MarketingPromotionPart>());
foreach (MarketingPromotionPart p in promotionParts) {
  // Set properties on individual parts,,,
}

// Step 4: Prep and handoff
var list = shapeHelper.List ();
list.AddRange(promotionParts.Select(x => this.contentManager.BuildDisplay(x, "SummaryList")));
return shapeHelper.Parts_MarketingPromotions_Current(ContentItems: list);
      
When I include only 1 widget of this type on a page, this works great. When I include 2 or more, I see that objects are being reused. For example, assume that I put two widgets in a layout, one in aside1 and the other in aside2. If I set different properties for each widget, I was expecting to see... (simplified example)

Widget 1
  • item 1 with blue
  • item 2 with red
Widget 2
  • item 1 with yellow
Instead, I see that Item1 is the same in both cases. It just depends on which query runs first to determine if Item 1 comes back as blue or yellow in both cases.

It's fairly easy to see that my widgets are picking up the same item1. This is not what I intended. SQL traces show that 2 distinct queries for the data are made, so somewhere/somehow my two collections are being mishandled during the display step.

Am I doing something wrong or do I need to set some property to allow operations on 2 different collections?

--- Afternoon Update ---

It appears this problem is related to the concept of storing all items in the session cache. If I comment out line 223 in DefaultContentManger.cs, multiple queries on the same page work as expected.
// store in session prior to loading to avoid some problems with simple circular dependencies
// session.Store(contentItem);   <<<<--- commented out to validate problem exists with cached data
            
This appears to clearly be a bug in the logic. A custom query for a content item shouldn't be stored in the session and allowed to "short-circuit" other object creation steps as evidenced by code blocks 143-145 and line 215. This explains why the same object was being used in different widgets even though SQL is clearly providing new result sets.

The question becomes... what is the right fix? is there a way for my contentmanager.queries to not store themselves in the cache?
Nov 20, 2014 at 7:14 PM
For the curious - I've opened a bug to cover this issue. https://orchard.codeplex.com/workitem/21078