w3wp crash on driver display method

Topics: Troubleshooting, Writing modules
Jun 24, 2011 at 3:32 PM

I'm attempting to write a "related content" module that pulls in content tagged with a given tag.  I stumbled upon a blog that someone else had written a tutorial on doing exactly that, even down to having the same name as my module. (http://www.deepcode.co.uk/2011/06/real-world-orchard-cmspart-7finding.html)

The code seems to work fine, I can pull content just fine and it is displaying things correctly on the front-end, but when I try to view the content list in the admin, it blows up.  I got a notification from soluto (a 3rd party process/service monitor) that the IIS Worker Process crashed.  After some investigation, I've narrowed it down to the line in red below:

protected override DriverResult Display(
            RelatedContentPart part, string displayType, dynamic shapeHelper)
        {
            // Get current item
            int currentItemId = -1;
            currentItemId = TryGetCurrentContentId(-1);

            IEnumerable<TagsPart> parts =
                 _cms.Query<TagsPart, TagsPartRecord>()
                    .Where(tpr => tpr.Tags.Any(t => t.TagRecord.TagName == part.TagName))
                    .Join<CommonPartRecord>()
                //.Where(cpr => cpr.Id != currentItemId)
                    .OrderByDescending(cpr => cpr.PublishedUtc)
                    .Slice(10);

            var list = shapeHelper.List();
            list.AddRange(parts.Select(p => _cms.BuildDisplay(p, "Summary")));

            return ContentShape("Parts_RelatedContent", () => shapeHelper.Parts_RelatedContent(
                TagName: part.TagName,
                ContentItems: shapeHelper.List()));
        }

I put a breakpoint on the var list = line above it, and stepped through it.  when I step on the addrange line, it jumps back to the previous line (var list =).  I repeated this a few times, before hitting F5 and letting it cycle through, and eventually it throws a stackoverflow exception, which is what is crashing the w3wp.

So, I'm not sure what's suspect about that line...in the meantime, why is that better than just passing in the list of parts it found from the query instead?  What do I get from the BuildDisplay and shape helper stuff?

Jun 24, 2011 at 3:42 PM

Further debugging, I stepped into the line in red and it brought me to AddRange in shape.cs.

 

    public virtual Shape AddRange(IEnumerable<object> items, string position = DefaultPosition) {
            foreach (var item in items)
                Add(item, position);
            return this;
        }

I step into the foreach, and it goes:

foreach, items, in


And after that, it jumps back out to the var list = shapeHelper.List() line. So, there's something it doesn't like about the items collection, it seems.

 

Jun 24, 2011 at 3:48 PM

Ok, I lied, it seems to be happening on the front-end too.

Jun 24, 2011 at 4:41 PM

The List shape doesn't have an AddRange method - it's not a normal List<T>, it's a dynamic shape with clay behaviours and they only included Add. So you just need a loop.

What's happening with the strange debugging is that you're creating an infinite loop - your BuildDisplay call results in your driver getting called again, and so ad infinitum.

Finally - when you build your shape you're creating a new, empty List from shapeHelper, rather than passing in the list which you've added things to. Therefore nothing would be displaying anyway :)

Jun 24, 2011 at 4:47 PM
Edited Jun 24, 2011 at 4:49 PM

1.) Ok, I'll change this to a loop.

2.) If this creates an infinite loop, I wonder how the blogger I got this from got it to work.  I"ll have to re-read his post.

3.) Yes, I know that, I did that on purpose when I was trying to figure out why it happened, so at one point I commented those two lines out and just passed in an empty shapeHelper.List()

Can you give me an example on how to fix  #2?

Jun 24, 2011 at 4:53 PM

Actually, if I'm understanding you correctly, if I change it to a manual loop, that should take care of both issues (#1 & #2).

Jun 24, 2011 at 5:08 PM

I'm still a little confused.  I changed it to this:

 var list = shapeHelper.List();
            foreach (var tagsPart in parts) {
                list.Add(tagsPart);
            }
            //list.AddRange(parts.Select(p => _cms.BuildDisplay(p, "Summary")));

            return ContentShape("Parts_RelatedContent", () => shapeHelper.Parts_RelatedContent(
                TagName: part.TagName,
                ContentItems: parts));

Then my view errors saying that TagsPart doesn't contain a definition for "Title" on this line:

 <li><a class="news-icon" href="">@item.Title</a></li>
What exactly was that BuildDisplay do besides re-call the driver method? Why would that blogger recommend using this method? I imagine I'm missing something because I swear this worked yesterday,a nd I don't really know what I changed.  

Jun 24, 2011 at 5:19 PM

Well it depends on what content items you're rendering when you call BuildDisplay, but it's very easy to create these types of recursion. Believe me I've done it plenty; I'm building a whole system of many-to-many relationships between content, so I render all the child items exactly like this, and I was continually crashing IIS until I put in my own code to automatically check for infinite recursions and stop them!

What's happening is like this;

- You're displaying a content item. It has TagsPart, and it has RelatedContentPart.

- So the RelatedContentDriver gets called to create a shape for RelatedContentPart. The driver renders a list of content items (in summary mode). Each of those content items contains both TagsPart and RelatedContentPart.

- So the RelatedContentDriver gets called for the first item in that list. Again it renders a list of content items (in summary mode). Each of those content items contains ...

So you see here how the recursion is happening.

Firstly; you need to make sure in Placement.info that your RelatedContentPart won't display in Summary display type.

But this alone won't fix the problem - the recursion happens regardless of whether the child shapes display or not - you're still calling BuildDisplay so your driver is still recursing.

What you need is to use the "factory methods" feature of drivers, and this is code you'll see quite a lot in existing Orchard drivers:

protected override DriverResult Display(
            RelatedContentPart part, string displayType, dynamic shapeHelper)
        {
            // No processing here at all
            // ...

            // Start returning the ContentShape ...
            return ContentShape("Parts_RelatedContent", 

// This is the factory delegate
() => {
    // I've added curly braces so this is now a whole function,
    // where you can perform ALL processing. This code only
    // gets run if Placement decides the shape is going to be  
    // displayed.


    // Finally shapeHelper runs
    return shapeHelper.Parts_RelatedContent(
                TagName: part.TagName,
                ContentItems: shapeHelper.List());
}

// End of ContentShape call
);
            
        }

So with the above change, Placement can successfully switch off the child shape rendering, you not only avoid the recursion but gain performance, since you don't need to run any database calls or anything if the shape doesn't display.

Jun 24, 2011 at 5:19 PM

I'm not 100% sure it's correct that AddRange doesn't exist...I just reverted my code and this was the result:

protected override DriverResult Display(
            RelatedContentPart part, string displayType, dynamic shapeHelper)
        {
            IEnumerable<TagsPart> parts =
                 _cms.Query<TagsPart, TagsPartRecord>()
                    .Where(tpr => tpr.Tags.Any(t => t.TagRecord.TagName == part.TagName))
                    .Join<CommonPartRecord>()
                    .OrderByDescending(cpr => cpr.PublishedUtc)
                    .Slice(10);

            var list = shapeHelper.List();
            list.AddRange(parts.Select(p => _cms.BuildDisplay(p, "Summary")));

            return ContentShape("Parts_RelatedContent", () => shapeHelper.Parts_RelatedContent(
                TagName: part.TagName,
                ContentItems: list));
        }

This worked on the front-end...I think it's still hanging on the backend...

Jun 24, 2011 at 5:22 PM

Looks like we posted at the same time...I'll try this and post back when I have some info...

Jun 24, 2011 at 5:23 PM

I just saw your additional post. The above was in relation to "2.) If this creates an infinite loop, I wonder how the blogger I got this from got it to work.  I"ll have to re-read his post."

In answer to your other question; shapeHelper.List() creates a dynamic display shape into which you must also pass dynamic display shapes.

TagsPart isn't a dynamic display shape, nor is ContentItem. You have to call BuildDisplay to create that dynamic display shape; and that in turn calls all the part drivers, and then uses Placement to determine which shapes to actually display. The factory method is a kind of deferred execution, so you only have to build that if it's needed.

Jun 24, 2011 at 5:24 PM

P.S. Can you explain the summary type stuff? What does that mean? I lifted this code from somewhere else, and I don't quite understand "building in summary mode"

Jun 24, 2011 at 5:39 PM

Well it's just an arbitrary display type. A normal content page is in "Detail" mode, whereas most of the lists display in "Summary" mode. In Placement.info you can match by string against display type to control what shapes appear and where. It's perfectly reasonable to make up your own display type in cases like this, for instance if you wanted this display to function differently than Summary, which is used in search results, blog posts, and other lists.

Jun 24, 2011 at 5:42 PM

I found some documentation on placement info and matching display type...but nothing that says how to not display it.  Do I just do the following:

 

<Match DisplayType="Summary">
        <Place Parts_RelatedContent="none"/> 
    </Match>

Or somethign like that? What goes in the value of that attribute?

Jun 24, 2011 at 5:47 PM
Edited Jun 24, 2011 at 6:22 PM

I'm still not really understanding how this helps...if I set it to not build in summary mode, I assume the result is that when I view it in the admin panel content list, it won't build the related content part.  On the display side when I view the page, it will still render everything? 

Jun 24, 2011 at 5:51 PM

You can use "-" to hide the shape, it's a special value Orchard understands.

I think you're still not following the recursion; have a careful read back through what I've said; if you display the RelatedContentPart in Summary, then it will itself render more Summary items, it's an infinite loop.

Jun 24, 2011 at 6:25 PM

I'm still missing something, because it still seems to want to go into this recursive loop.  Here's what I have now:

protected override DriverResult Display(
            RelatedContentPart part, string displayType, dynamic shapeHelper)
        {
            return ContentShape("Parts_RelatedContent",
                                    () => {
                                        IEnumerable<TagsPart> parts =
                                             _cms.Query<TagsPart, TagsPartRecord>()
                                                .Where(tpr => tpr.Tags.Any(t => t.TagRecord.TagName == part.TagName))
                                                .Join<CommonPartRecord>()
                                                .OrderByDescending(cpr => cpr.PublishedUtc)
                                                .Slice(10);

                                        var list = shapeHelper.List();
                                        list.AddRange(parts.Select(p => _cms.BuildDisplay(p, "Summary")));

                                        return ContentShape("Parts_RelatedContent",
                                            () => shapeHelper.Parts_RelatedContent(
                                            TagName: part.TagName,
                                            ContentItems: list));
                                    }
                                ); //end of content shape call
        } 
 

And, for the placement.info

<Placement>
    <Place Parts_RelatedContent="Content:10"/>
    <Place Parts_RelatedContent_Edit="Content:7.5"/>

    <Match DisplayType="Summary">
        <Place Parts_RelatedContent="-"/>
    </Match>
</Placement>

If I understand right, the bolded line in the driver can still be included because the placement.info file above says "don't show the related content part if it's built in summary mode."  If so, something else is wrong cause it's still borking on me.

Coordinator
Jun 24, 2011 at 7:16 PM

Placement will determine where (if anywhere) your shape goes, but it will still get built. Hence the infinite recursion. That's an interesting problem. Seems like the display should be built in a more lazy way. Summoning Louis.

Jun 24, 2011 at 7:40 PM

After chatting with pete a little offline, I did some more debugging, and found that simply supressing the summary won't work because it looks like the parent shape gets rendered in summary mode.  So, I think I can change the children to render in "RelatedSummary" mode or something like that.  Now I just need to figure out how to get my content to show up, because it's showing up as blank on the front-end.

Jun 24, 2011 at 7:56 PM

By "make up your own displaytype" do you mean just passing a different string in (RelatedSummary, for instance) into the BuildDisplay function?  If so, I'm left with:

<Placement>
    <Place Parts_RelatedContent="Content:1"/>
    <Place Parts_RelatedContent_Edit="Content:7.5"/>

    <Match DisplayType="RelatedSummary">
        <Place Parts_RelatedContent="-"/>
    </Match>
</Placement>

And that results in the title of the content item being rendered, but no parts...

<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:TrackMoves /> <w:TrackFormatting /> <w:PunctuationKerning /> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:DoNotPromoteQF /> <w:LidThemeOther>EN-US</w:LidThemeOther> <w:LidThemeAsian>X-NONE</w:LidThemeAsian> <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> <w:Compatibility> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:SplitPgBreakAndParaMark /> <w:DontVertAlignCellWithSp /> <w:DontBreakConstrainedForcedTables /> <w:DontVertAlignInTxbx /> <w:Word11KerningPairs /> <w:CachedColBalance /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> <m:mathPr> <m:mathFont m:val="Cambria Math" /> <m:brkBin m:val="before" /> <m:brkBinSub m:val="--" /> <m:smallFrac m:val="off" /> <m:dispDef /> <m:lMargin m:val="0" /> <m:rMargin m:val="0" /> <m:defJc m:val="centerGroup" /> <m:wrapIndent m:val="1440" /> <m:intLim m:val="subSup" /> <m:naryLim m:val="undOvr" /> </m:mathPr></w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="true" DefSemiHidden="true" DefQFormat="false" DefPriority="99" LatentStyleCount="267"> <w:LsdException Locked="false" Priority="0" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Normal" /> <w:LsdException Locked="false" Priority="9" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="heading 1" /> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 2" /> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 3" /> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 4" /> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 5" /> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 6" /> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 7" /> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 8" /> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 9" /> <w:LsdException Locked="false" Priority="39" Name="toc 1" /> <w:LsdException Locked="false" Priority="39" Name="toc 2" /> <w:LsdException Locked="false" Priority="39" Name="toc 3" /> <w:LsdException Locked="false" Priority="39" Name="toc 4" /> <w:LsdException Locked="false" Priority="39" Name="toc 5" /> <w:LsdException Locked="false" Priority="39" Name="toc 6" /> <w:LsdException Locked="false" Priority="39" Name="toc 7" /> <w:LsdException Locked="false" Priority="39" Name="toc 8" /> <w:LsdException Locked="false" Priority="39" Name="toc 9" /> <w:LsdException Locked="false" Priority="35" QFormat="true" Name="caption" /> <w:LsdException Locked="false" Priority="10" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Title" /> <w:LsdException Locked="false" Priority="1" Name="Default Paragraph Font" /> <w:LsdException Locked="false" Priority="11" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Subtitle" /> <w:LsdException Locked="false" Priority="22" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Strong" /> <w:LsdException Locked="false" Priority="20" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Emphasis" /> <w:LsdException Locked="false" Priority="59" SemiHidden="false" UnhideWhenUsed="false" Name="Table Grid" /> <w:LsdException Locked="false" UnhideWhenUsed="false" Name="Placeholder Text" /> <w:LsdException Locked="false" Priority="1" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="No Spacing" /> <w:LsdException Locked="false" Priority="60" SemiHidden="false" UnhideWhenUsed="false" Name="Light Shading" /> <w:LsdException Locked="false" Priority="61" SemiHidden="false" UnhideWhenUsed="false" Name="Light List" /> <w:LsdException Locked="false" Priority="62" SemiHidden="false" UnhideWhenUsed="false" Name="Light Grid" /> <w:LsdException Locked="false" Priority="63" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 1" /> <w:LsdException Locked="false" Priority="64" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 2" /> <w:LsdException Locked="false" Priority="65" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 1" /> <w:LsdException Locked="false" Priority="66" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 2" /> <w:LsdException Locked="false" Priority="67" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 1" /> <w:LsdException Locked="false" Priority="68" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 2" /> <w:LsdException Locked="false" Priority="69" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 3" /> <w:LsdException Locked="false" Priority="70" SemiHidden="false" UnhideWhenUsed="false" Name="Dark List" /> <w:LsdException Locked="false" Priority="71" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Shading" /> <w:LsdException Locked="false" Priority="72" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful List" /> <w:LsdException Locked="false" Priority="73" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Grid" /> <w:LsdException Locked="false" Priority="60" SemiHidden="false" UnhideWhenUsed="false" Name="Light Shading Accent 1" /> <w:LsdException Locked="false" Priority="61" SemiHidden="false" UnhideWhenUsed="false" Name="Light List Accent 1" /> <w:LsdException Locked="false" Priority="62" SemiHidden="false" UnhideWhenUsed="false" Name="Light Grid Accent 1" /> <w:LsdException Locked="false" Priority="63" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 1 Accent 1" /> <w:LsdException Locked="false" Priority="64" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 2 Accent 1" /> <w:LsdException Locked="false" Priority="65" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 1 Accent 1" /> <w:LsdException Locked="false" UnhideWhenUsed="false" Name="Revision" /> <w:LsdException Locked="false" Priority="34" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="List Paragraph" /> <w:LsdException Locked="false" Priority="29" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Quote" /> <w:LsdException Locked="false" Priority="30" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Intense Quote" /> <w:LsdException Locked="false" Priority="66" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 2 Accent 1" /> <w:LsdException Locked="false" Priority="67" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 1 Accent 1" /> <w:LsdException Locked="false" Priority="68" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 2 Accent 1" /> <w:LsdException Locked="false" Priority="69" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 3 Accent 1" /> <w:LsdException Locked="false" Priority="70" SemiHidden="false" UnhideWhenUsed="false" Name="Dark List Accent 1" /> <w:LsdException Locked="false" Priority="71" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Shading Accent 1" /> <w:LsdException Locked="false" Priority="72" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful List Accent 1" /> <w:LsdException Locked="false" Priority="73" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Grid Accent 1" /> <w:LsdException Locked="false" Priority="60" SemiHidden="false" UnhideWhenUsed="false" Name="Light Shading Accent 2" /> <w:LsdException Locked="false" Priority="61" SemiHidden="false" UnhideWhenUsed="false" Name="Light List Accent 2" /> <w:LsdException Locked="false" Priority="62" SemiHidden="false" UnhideWhenUsed="false" Name="Light Grid Accent 2" /> <w:LsdException Locked="false" Priority="63" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 1 Accent 2" /> <w:LsdException Locked="false" Priority="64" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 2 Accent 2" /> <w:LsdException Locked="false" Priority="65" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 1 Accent 2" /> <w:LsdException Locked="false" Priority="66" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 2 Accent 2" /> <w:LsdException Locked="false" Priority="67" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 1 Accent 2" /> <w:LsdException Locked="false" Priority="68" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 2 Accent 2" /> <w:LsdException Locked="false" Priority="69" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 3 Accent 2" /> <w:LsdException Locked="false" Priority="70" SemiHidden="false" UnhideWhenUsed="false" Name="Dark List Accent 2" /> <w:LsdException Locked="false" Priority="71" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Shading Accent 2" /> <w:LsdException Locked="false" Priority="72" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful List Accent 2" /> <w:LsdException Locked="false" Priority="73" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Grid Accent 2" /> <w:LsdException Locked="false" Priority="60" SemiHidden="false" UnhideWhenUsed="false" Name="Light Shading Accent 3" /> <w:LsdException Locked="false" Priority="61" SemiHidden="false" UnhideWhenUsed="false" Name="Light List Accent 3" /> <w:LsdException Locked="false" Priority="62" SemiHidden="false" UnhideWhenUsed="false" Name="Light Grid Accent 3" /> <w:LsdException Locked="false" Priority="63" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 1 Accent 3" /> <w:LsdException Locked="false" Priority="64" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 2 Accent 3" /> <w:LsdException Locked="false" Priority="65" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 1 Accent 3" /> <w:LsdException Locked="false" Priority="66" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 2 Accent 3" /> <w:LsdException Locked="false" Priority="67" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 1 Accent 3" /> <w:LsdException Locked="false" Priority="68" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 2 Accent 3" /> <w:LsdException Locked="false" Priority="69" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 3 Accent 3" /> <w:LsdException Locked="false" Priority="70" SemiHidden="false" UnhideWhenUsed="false" Name="Dark List Accent 3" /> <w:LsdException Locked="false" Priority="71" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Shading Accent 3" /> <w:LsdException Locked="false" Priority="72" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful List Accent 3" /> <w:LsdException Locked="false" Priority="73" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Grid Accent 3" /> <w:LsdException Locked="false" Priority="60" SemiHidden="false" UnhideWhenUsed="false" Name="Light Shading Accent 4" /> <w:LsdException Locked="false" Priority="61" SemiHidden="false" UnhideWhenUsed="false" Name="Light List Accent 4" /> <w:LsdException Locked="false" Priority="62" SemiHidden="false" UnhideWhenUsed="false" Name="Light Grid Accent 4" /> <w:LsdException Locked="false" Priority="63" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 1 Accent 4" /> <w:LsdException Locked="false" Priority="64" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 2 Accent 4" /> <w:LsdException Locked="false" Priority="65" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 1 Accent 4" /> <w:LsdException Locked="false" Priority="66" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 2 Accent 4" /> <w:LsdException Locked="false" Priority="67" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 1 Accent 4" /> <w:LsdException Locked="false" Priority="68" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 2 Accent 4" /> <w:LsdException Locked="false" Priority="69" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 3 Accent 4" /> <w:LsdException Locked="false" Priority="70" SemiHidden="false" UnhideWhenUsed="false" Name="Dark List Accent 4" /> <w:LsdException Locked="false" Priority="71" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Shading Accent 4" /> <w:LsdException Locked="false" Priority="72" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful List Accent 4" /> <w:LsdException Locked="false" Priority="73" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Grid Accent 4" /> <w:LsdException Locked="false" Priority="60" SemiHidden="false" UnhideWhenUsed="false" Name="Light Shading Accent 5" /> <w:LsdException Locked="false" Priority="61" SemiHidden="false" UnhideWhenUsed="false" Name="Light List Accent 5" /> <w:LsdException Locked="false" Priority="62" SemiHidden="false" UnhideWhenUsed="false" Name="Light Grid Accent 5" /> <w:LsdException Locked="false" Priority="63" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 1 Accent 5" /> <w:LsdException Locked="false" Priority="64" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 2 Accent 5" /> <w:LsdException Locked="false" Priority="65" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 1 Accent 5" /> <w:LsdException Locked="false" Priority="66" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 2 Accent 5" /> <w:LsdException Locked="false" Priority="67" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 1 Accent 5" /> <w:LsdException Locked="false" Priority="68" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 2 Accent 5" /> <w:LsdException Locked="false" Priority="69" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 3 Accent 5" /> <w:LsdException Locked="false" Priority="70" SemiHidden="false" UnhideWhenUsed="false" Name="Dark List Accent 5" /> <w:LsdException Locked="false" Priority="71" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Shading Accent 5" /> <w:LsdException Locked="false" Priority="72" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful List Accent 5" /> <w:LsdException Locked="false" Priority="73" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Grid Accent 5" /> <w:LsdException Locked="false" Priority="60" SemiHidden="false" UnhideWhenUsed="false" Name="Light Shading Accent 6" /> <w:LsdException Locked="false" Priority="61" SemiHidden="false" UnhideWhenUsed="false" Name="Light List Accent 6" /> <w:LsdException Locked="false" Priority="62" SemiHidden="false" UnhideWhenUsed="false" Name="Light Grid Accent 6" /> <w:LsdException Locked="false" Priority="63" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 1 Accent 6" /> <w:LsdException Locked="false" Priority="64" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Shading 2 Accent 6" /> <w:LsdException Locked="false" Priority="65" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 1 Accent 6" /> <w:LsdException Locked="false" Priority="66" SemiHidden="false" UnhideWhenUsed="false" Name="Medium List 2 Accent 6" /> <w:LsdException Locked="false" Priority="67" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 1 Accent 6" /> <w:LsdException Locked="false" Priority="68" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 2 Accent 6" /> <w:LsdException Locked="false" Priority="69" SemiHidden="false" UnhideWhenUsed="false" Name="Medium Grid 3 Accent 6" /> <w:LsdException Locked="false" Priority="70" SemiHidden="false" UnhideWhenUsed="false" Name="Dark List Accent 6" /> <w:LsdException Locked="false" Priority="71" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Shading Accent 6" /> <w:LsdException Locked="false" Priority="72" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful List Accent 6" /> <w:LsdException Locked="false" Priority="73" SemiHidden="false" UnhideWhenUsed="false" Name="Colorful Grid Accent 6" /> <w:LsdException Locked="false" Priority="19" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Subtle Emphasis" /> <w:LsdException Locked="false" Priority="21" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Intense Emphasis" /> <w:LsdException Locked="false" Priority="31" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Subtle Reference" /> <w:LsdException Locked="false" Priority="32" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Intense Reference" /> <w:LsdException Locked="false" Priority="33" SemiHidden="false" UnhideWhenUsed="false" QFormat="true" Name="Book Title" /> <w:LsdException Locked="false" Priority="37" Name="Bibliography" /> <w:LsdException Locked="false" Priority="39" QFormat="true" Name="TOC Heading" /> </w:LatentStyles> </xml><![endif]--><!--[if gte mso 10]> <mce:style><! /* Style Definitions */ table.MsoNormalTable {mso-style-name:"Table Normal"; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-qformat:yes; mso-style-parent:""; mso-padding-alt:0in 5.4pt 0in 5.4pt; mso-para-margin:0in; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} --> <!--[endif] -->

By “make up your own displaytype” I think you mean just pass in a different string to the BuildDisplay method? I’m doing that, and the error goes away, but I no longer get any content on

Coordinator
Jun 24, 2011 at 7:59 PM

Louis confirms this is probably the way to go.

Jun 27, 2011 at 6:17 PM
Edited Jun 27, 2011 at 7:33 PM

I've been trying to get this to work for a few days now, and I'm running into a roadblock.  I removed all my custom code from my driver and view and just had it displaying a "Hello World" message.  I wanted to start fresh and make sure nothing else was getting in the way.  When I solve this, I can start adding pieces back in to see if it fixes the recursion issue.  For now, here's what I'm seeing:

Here's my driver code:

 

protected override DriverResult Display(
            RelatedContentPart part, string displayType, dynamic shapeHelper)
        {
            return BuildRelatedContentDisplay(part, displayType, shapeHelper);

            return ContentShape("Parts_RelatedContent",
                    () => BuildRelatedContentDisplay(part, displayType, shapeHelper)
                );
        }

        
private ContentShapeResult BuildRelatedContentDisplay(RelatedContentPart part, string displayType, dynamic shapeHelper)
        {
            var list = shapeHelper.List();

            return ContentShape("Parts_RelatedContent",
                        () => shapeHelper.Parts_RelatedContent(
                                ContentItems: list) // end shapeHelper call 
                ); // end contentShape call
        }

 

You'll see there are two return statements in the Display method.  When the code runs the first return, my "Hello World" message displays properly.  when I comment that line out and use the second return statement with the deferred delegate, it only shows the title of the module and fails to display my content (the "Hello World" message).

Why does the deferred method not display my view, and the straight up return call does?

Jun 27, 2011 at 6:51 PM

When using the deferred method, an error gets logged:

2011-06-27 13:48:20,690 [31] Orchard.ContentManagement.Drivers.Coordinators.ContentPartDriverCoordinator - RuntimeBinderException thrown from IContentPartDriver by Laughlin.RelatedContent.Drivers.RelatedContentDriver
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Orchard.ContentManagement.Drivers.ContentShapeResult' does not contain a definition for 'Metadata'
   at CallSite.Target(Closure , CallSite , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
   at Orchard.ContentManagement.Drivers.ContentPartDriver`1.AddAlternates(Object shape) in C:\Projects\git\mysite\src\Orchard\ContentManagement\Drivers\ContentPartDriver.cs:line 86
   at Orchard.ContentManagement.Drivers.ContentPartDriver`1.<>c__DisplayClass13.<ContentShapeImplementation>b__12(BuildShapeContext ctx) in C:\Projects\git\mysite\src\Orchard\ContentManagement\Drivers\ContentPartDriver.cs:line 82
   at Orchard.ContentManagement.Drivers.ContentShapeResult.ApplyImplementation(BuildShapeContext context, String displayType) in C:\Projects\git\mysite\src\Orchard\ContentManagement\Drivers\ContentShapeResult.cs:line 37
   at Orchard.ContentManagement.Drivers.ContentShapeResult.Apply(BuildDisplayContext context) in C:\Projects\git\mysite\src\Orchard\ContentManagement\Drivers\ContentShapeResult.cs:line 21
   at Orchard.ContentManagement.Drivers.Coordinators.ContentPartDriverCoordinator.<>c__DisplayClassa.<BuildDisplay>b__9(IContentPartDriver driver) in C:\Projects\git\mysite\src\Orchard\ContentManagement\Drivers\Coordinators\ContentPartDriverCoordinator.cs:line 47
   at Orchard.InvokeExtensions.Invoke[TEvents](IEnumerable`1 events, Action`1 dispatch, ILogger logger) in C:\Projects\git\mysite\src\Orchard\InvokeExtensions.cs:line 19

Coordinator
Jun 27, 2011 at 7:16 PM

Oh but the Lambda that you pass in shouldn't return a shape *result*, it should return a shape.

Jun 27, 2011 at 7:58 PM

Alright fellas, many thanks for your patient responses while you watched me step on my own feet.  Here is the final working setup, just for reference purposes:

View:

@{
    IEnumerable<object> latest = Model.ContentItems;
}
<div id="featured-articles-widget" style="width:100%;">

@if (latest == null || latest.Count() < 1) {
<p>@T("No related content.")</p>
}
else {
    <h3 class="gradient-header">
		@Model.TagName Articles</h3>
    <ul class="content-items">
    @foreach (dynamic item in latest)
    {
        <li><a class="news-icon" href="">@item.Title</a></li>
    }
    </ul>
}
<a href="#" class="more-link">View More</a>
</div>


Driver:

protected override DriverResult Display(
            RelatedContentPart part, string displayType, dynamic shapeHelper) {
                return ContentShape("Parts_RelatedContent",
                        () => {

                            IEnumerable<TagsPart> parts =
                                             _cms.Query<TagsPart, TagsPartRecord>()
                                                .Where(tpr => tpr.Tags.Any(t => t.TagRecord.TagName == part.TagName))
                                                .Join<CommonPartRecord>()
                                                .OrderByDescending(cpr => cpr.PublishedUtc)
                                                .Slice(10);

                            var list = shapeHelper.List();
                            list.AddRange(parts.Select(p => _cms.BuildDisplay(p, "RelatedSummary")));

                            // Finally shapeHelper runs
                            return shapeHelper.Parts_RelatedContent(
                                        TagName: part.TagName,
                                        ContentItems: list);
                        }
                    ); // end of contentshape call
        }

Placement:

<Placement>
    <Place Parts_RelatedContent="Content:1"/>
    <Place Parts_RelatedContent_Edit="Content:7.5"/>

    <Match DisplayType="Summary">
        <Place Parts_RelatedContent="Content:1"/>
    </Match>
    <Match DisplayType="RelatedSummary">
        <Place Parts_RelatedContent="-"/>
    </Match>
</Placement>



Jun 27, 2011 at 8:30 PM

Just a comment on this; you're building that RelatedSummary display for each item, and then basically throwing it away in the view - only accessing item.Title (actually I'm not sure where that comes from or if it'll even be there).

Why not just let the List shape render the items (with @Display(Model.ContentItems)) and then customise the Parts_RoutePart template, so you get both the title and url (which currently you don't seem to have at all).

Jun 27, 2011 at 9:16 PM

I think I'll eventually look into that. I wanted to get the basics down first...everything is currently rendering correctly as is, but when I finish this, I'll look into adding what your recommend as extra polish.

Jun 28, 2011 at 1:30 PM

I looked at this article and saw it was using ItemDisplayLink, but I couldn't find a way to put a custom class on the link that gets generated.  I also did some looking at some of the other Orchard modules in the spirit of trying the @Display(Model.ContentItems) suggestion, but after reading some posts on this forum, it seems like, at least if I'm understanding it right, customizing the header of a list like this can be a pain (maybe this isn't the case).

That being said, I'm not sure what the difference is between writing my own little loop over the content, or using Display(Model.ContentItems) and then customizing a template that does the same thing.  Can someone explain to me the benefits? I'm open to it, I just want to understand it first.

Coordinator
Jun 28, 2011 at 5:45 PM

The difference is that you are able to use local zones and placement, alternates and wrappers. That also gives you extensibility: if you extend those types later, you can simply change placement to take the changes into account instead of having to touch all templates that use it. It's a choice.

Aug 15, 2011 at 9:51 PM

Now that I'm further along with my knowledge of Orchard, I wanted to revisit this functionality so that I'm doing things in a more Orchard-friendly way.  I've got a new module I have created and everything is working to the point where it renders my list out, and now I need to customize both the header/footer (where the <ul> tag gets rendered, as well as the content itself (where the <li> gets rendered).  Bertrand, are you aware of a module that does something like this that I could base it off of? I'm having trouble getting a handle on what alternates I need to create to be able to customize my list...

Coordinator
Aug 15, 2011 at 10:13 PM

Have you tried using the Shape Tracing module to list all available alternate ?

Aug 16, 2011 at 12:52 PM

Sebastien,

Thanks for responding.  I have used shape tracing, and I seem to be able to get at the <li> item, but not the header...can you advise? In the meantime, I'm looking at your "relatedcontent" module to see how yo handle it...

Aug 16, 2011 at 1:42 PM
Edited Aug 16, 2011 at 1:57 PM

For example, using shape tracing on your recent content widget, I found I could setup an alternate for it by putting a file named "Widget-RecentContent.cshtml" in my theme.  Since that calls Display(Model.ContentItems) the UL/LI get rendered in that call.  I need to be able to directly control the <ul> to add some stuff, and also the <li> to add some extra stuff.  Is this possible using alternates? I know that there are often more alternates available than what shape tracing offers, but I haven't been able to figure out what to override, and reading some posts on this forum seem to indicate it's not exactly straight-forward.  If I'm wrong, please advise...

** UPDATE **

I technically could style the ul appropriately with css and not have to mess with it.  More specifically, what I'm trying to do is this:

I have an image field on my content items.  My part queries for content items and displays them in a list. This is working.  What I want to do is customize the list items to display that image along with the title/link and maybe some other stuff from the content item next to each link in the list, so instead of the default rendering:

<ul>
<li><a href='/foo'>My link</a></li>
</ul>

I customize it to:
<ul>
<li><img src='my-icon.png' /> <a href='/foo'>My link</a> (2011-08-16)</li>
</ul>

I found this post: http://weblogs.asp.net/bleroy/archive/2011/03/27/taking-over-list-rendering-in-orchard.aspx

I'll take a look at this in the meantime while I await your response...

Aug 16, 2011 at 2:09 PM

More info, I'm also rendering these items out in "FeaturedSummary" display type.  Shape tracing gives me the following as an alternate when I select one of the items in my list:

    ~/Themes/MyTheme/Views/Parts.Common.Body.Summary.cshtml with a displaytype of "FeaturedContent"

Here's my driver method in case it helps:

protected override DriverResult Display(FeaturedContentPart part, string displayType, dynamic shapeHelper)
        {
            return ContentShape("Parts_FeaturedContent_List",
                                () =>
                                {
                                    var items = _cms.Query(VersionOptions.Published, "Article")
                                                       .Join<RoutePartRecord>()
                                                       .Join<CommonPartRecord>()
                                                       .Join<FeaturedContentPartRecord>()
                                                       .Where(fcr => fcr.IsFeatured)
                                                       .OrderByDescending<CommonPartRecord, DateTime?>(record => record.PublishedUtc)
                                                       .Slice(5);

                                    var list = shapeHelper.List();
                                    list.AddRange(items.Select(p => _cms.BuildDisplay(p, "FeaturedSummary")));

                                    // we only want to render this shape if this page is set to show the featured content shape
                                    if (part.IsVisible)
                                    {
                                        // Render the shape
                                        //var shape = shapeHelper.Parts_FeaturedContent(ContentItems: list);

                                        // Push it to the correct zone 
                                        _work.GetContext().Layout.Zones["Content"].Add(list, "1");
                                    }
                                    return new DriverResult();
                                } // end of delayed function
                );
        }

Coordinator
Aug 16, 2011 at 5:58 PM

A quick note to correct what you say, there never are more alternate than the ones displayed by shape tracing. But there are modules your might need to enable to have more ones.

Aug 16, 2011 at 6:04 PM
Edited Aug 16, 2011 at 6:06 PM

Ahh, I didn't know that. My bad, I thought there were others that were available.  So it seems like what I"m trying to do is not really possible...at least the ways I tried to do it.  Unfortunately, I will have to resort to rendering the shape HTML myself instead of using the list shape. 

P.S. On a side note, related to my task (but not necessarily the original post) if I add an imagefield to a content type, and then create a part that displays a list of those content types in its shape, how can I access that field? I thought I'd be able to do something like:

foreach (var item in contentItems)
                {
                    // item.As<ContentPart>().Fields
                }

I read somehwere that there was an easy way to do this since 1.1, but I can't seem to find this in shape tracing or in the debugger...I also read that fields are attached to a part? I was able to attach my imagefield to an "article" content type...so not sure what that means...

Coordinator
Aug 16, 2011 at 6:13 PM

Yes, couldn't find the documentation section but here is the idea. First you need to cast a Content Item to dynamic. Then you browse the Parts and Fields. All those properties are also displayed automatically in Shape Tracing's Model tab.

dynamic contentItem = Model.ContentItem; // or whatever property is your content item
var value = contentItem.YOURPART.YOURFIELD.YOURPROPERTY;

Aug 16, 2011 at 6:17 PM

Ok, I can work with that, but what if my Field isn't added to a part, what if it's added to a ContentType like "Page" or "Article?"  Does that just use "ContentPart" then?

Coordinator
Aug 16, 2011 at 6:19 PM

Ah ah ;) It's always added to a part. It happens that fields attached to a content type, are actually added to a default part which has the name of the content type. So for instance contentitem.Article.Teaser.Value. You can see it also in the Shape Tracing. Open the Model tab, open a content item of any type, you will see it has a content part with the same name as the type.

Aug 16, 2011 at 6:21 PM

I knew it had to be added to some default part...should have known it would be the same name as the content type...thanks for the prompt responses...

Oct 4, 2011 at 4:24 PM

hi

in which driver i can add this code?