This project is read-only.

Parts, updating behaviour, and also alternative to ViewBag when using Shapes

Topics: Writing modules
Jul 30, 2012 at 2:08 PM


I'm writing my first module, and although I've made some progress, I'm a bit unsure/stumped on a few things..

I'm using the Projections module as a reference. This uses ViewModels to present data to the view via Shapes. I had previously set my system up to directly access the ContentPart, instead of a ViewModel, but I've come to a point where I might need a viewmodel after all.

The problem arose when I wanted to build a drop down list in the EditorTemplate View from a webservice call. So I thought, in my MVC noobness, I'd just call the function from Razor. Not happening. The MVC way would be to put the results of the service call in a ViewBag. So I read up on Orchard and ViewBags. Everyone said "don't do that, put it in the Shape instead".

The problem is that I'm not wanting to bloat my driver code, converting from ContentPart to ViewModel and back again. This also raises another question, when I change a value in a ContentPart, is this automatically persisted?

If I have to build a seperate ViewModel, then so be it, but if someone has more of a clue than me (it wouldn't be hard lol) could maybe suggest an alternative that wont result in an extra 60+ lines of code, I'm listening intently..

Disclaimer: I've not much of a clue about MVC in general, this is an all in one learning curve lol..

Cheers in advance.

Jul 30, 2012 at 4:13 PM

Yes, in Orchard it's the recommended way to use Shapes as the model for your views - it's way more versatile. However, ViewBag can also be used as the model for your views if you really want to. It's just not recommended practice within Orchard. Perhaps because that's a bit like using global variables instead of properties and local variables. Using shapes have a lot of advantages over ViewBag, so it's good practice to learn to use them - you'll benefit from it.

Using ViewModel classes from driver code is perfectly legal if it's what you need. If you're concerned about bloating your drivers with model mapping code, either move the mapping code to another class, or take advantage of Automapper.

No, when you change a property in a ContentPart, it is not persisted. Only property values on Record classes are persisted automatically (provided that there is a StorageFilter registered for the repository of that Record entity). Most ContentPart implementations, however, derive from ContentPart<TRecord>, and implement wrapping properties that set the value on the Record.

Jul 30, 2012 at 4:25 PM

Hey, Thanks for that, it makes perfect sense now. I'll look at moving my conversion code to some utility class (good idea anyways I think)..

Thanks for the info on persistance as well, all my parts derive from their records, so I think I should be ok there. In fact I think I reference the record implicitly on my conversions, so don't think I'll have a problem there.

What I don't get (yet) is the driver/editor/controller relationship in the back office. I'm guessing the driver handles just the template, and then for create/edit/delete, the controller is used. As I say, I'm just picking through the projections (and blog) code, to see how it's done, but all this dynamic goodness is leaving me rather stumped. I'm sure I'll get used to it though.

Are you the Skywalker Software Development blogger? If you are, your tutorial is helping me muchly, I'd actually be lost without it at this point, although I'm finding that I need to extend it somewhat, as I'm handling a relational music catalog (Releases, Tracks, Artists, Featured Artists etc), and I've got quite a bit to port from my old WebForms project. If you've got any hints on relational (1-n, 1-n-1), I'd love to see some posts about that.

Thanks for the advice again, really set me straight there.. :)

Jul 30, 2012 at 5:49 PM
Edited Jul 30, 2012 at 5:53 PM

That would be me :) Glad I could help.

As for the driver/editor/controller relationship, it goes more or less like this:

  • The Contents module has an AdminController with Edit and Create actions. Edit accepts the ID of a content item
  • The Edit action loads the requested content item by id, and then invokes a method on the ContentManager called BuildEditor. BuildEditor basically selects all the Parts attached to the content item and invokes the Editor method on all drivers for each content part. The result is a shape, which holds the entire tree of shapes.
  • The Create action does the same, except it first news up a content item based on the specified content type.
  • When posting back, the content item is either loaded or created. Then the UpdateEditor on the ContentManager is invoked. This time, each overloaded Editor method of the drivers are invoked (the one that takes an IUpdater argument, which is actually just the AdminController that invoked UpdateEditor). Again a shape tree is returned used to display the screen.

So you're right about that: the drivers handle just the content parts. Controllers handle the entire HTTP request, potentially using the ContentManager to execute drivers (another example using ContentManager is the Blogs module).

Handling relational entities / conten items is actually very easy. If you for example have an Artist content type and a Track content type, you could create a TrackPart class that has a property of type ArtistPart. If that property is called Artist, simply define the TrackPartRecord table to hold a column called Artist_Id. Orchard / NHibernate will do the mapping.

Also be sure to checkout the Taxonomies and Projections modules to define relations between contents. Using Taxonomies, you could for example create a taxonomy called Release, holding a number of terms (which are the actual releases, which are simply content items of type Release). If you then define the Artist content type and attach a Taxonomy field, you can attach multiple Releases to an Artist. All this, and much more, is possible without writing a single line of code.

Thinking about it, this may be a nice subject to write a tutorial about some time (first need to find some time to update the current Webshop posts). We'll see :)

Jul 30, 2012 at 5:58 PM
Edited Jul 30, 2012 at 5:59 PM

Thanks for the brilliant info there, really helpful :)

I'll check out the Taxonomies module for sure, it may be a little bit different than the way I considered, at this time I'm just going by the relations in an EF kinda way, as I built the original code from mapping the database first.  Sounds like I'm on the right road though.

Another consideration I had was the inclusion of search functionality. I think my current way of writing a self contained system may hamper my abilities to search my content, don't really know about that to be honest.. what considerations should I have here??

Jul 30, 2012 at 6:40 PM

Actually, you can let your content parts participate in the indexing phase simply by implementing a ContentHandler and implementing the OnIndexing method. I haven't done this myself though, but there should be enough samples in the source code, as well as threads on this site on how to do it.

Jul 30, 2012 at 7:01 PM

Many thanks again, I think I'm all set now (for the moment anyways lol).