Dictionary as ContentPart Property

Topics: Writing modules
Jan 15, 2012 at 3:02 PM

Hello.

I'm writing a module and I wished to have a Dictionary as a Property for a Content part, like this:

 

...
        /// <summary>
        /// A dictionary representing settings for a slide.
        /// </summary>
        public Dictionary<string, string> Content
        {
            get { return Tools.SetDefaultSettingsIfNeeded(Tools.XmlToDictionary(Record.Content), Record); }
            set { Record.Content = Tools.DictionaryToXml(value); }
        }
...

 

Those XmlToDictionary() and DictionaryToXml() methods are simple ones and do as stated in their names. I use them because in a ContentPartRecord I wish to store that Dictionary property as XML in string form:

 

    public class SlidePartRecord : ContentPartRecord
    {
...
        public virtual string Content { get; set; }
...
    }

 

Let's think about it like I may have different Dictionary entries for each content item, ofc i know which content item has what Dictionary entries and their keys.

I'm trying to edit those from under Dashboard and trying made an editor template for this content part look like this:

 

@model SlidePart
@using TTS.AwkwardShowcase
@using TTS.AwkwardShowcase.Models
@using TTS.AwkwardShowcase.Enums
@using Orchard.ContentManagement;
<fieldset>
    <legend>@T("��������� ������.")</legend>
    <div>
        <span>Id : @Model.Id</span>
    </div>
    <div>
        <span>��� : @Model.Type</span>
    </div>
    <div>
        @Html.LabelFor(m => m.ShowcaseName, @T("��� Showcase"))
        @Html.EditorFor(m => m.ShowcaseName)
        @Html.ValidationMessageFor(m => m.ShowcaseName, "*")
        <span class="hint">
            @T("� ������ showcase ��������� �����.")
        </span>
    </div>
    <div>
        @Html.LabelFor(m => m.Position, @T("������� ������"))
        @Html.EditorFor(m => m.Position)
        @Html.ValidationMessageFor(m => m.Position, "*")
    </div>
        @switch (Model.Type)    
        {
            case SlideType.HTML:
                break;
            case SlideType.Image:
 
                <div>
                    @Html.LabelFor(m => m.Content["uri"], @T("Uri ��������"))
                    @Html.EditorFor(m => m.Content["uri"])
                    @Html.ValidationMessageFor(m => m.Content["uri"], "*")
                    <span class="hint">
                        @T("����� ����� ���� ��� ���������� ��� � �������������.")
                    </span>
                </div>
                <div>
                    @Html.LabelFor(m => m.Content["alt"], @T("���������� �����"))
                    @Html.EditorFor(m => m.Content["alt"])
                    @Html.ValidationMessageFor(m => m.Content["alt"], "*")
                    <span class="hint">
                        @T("������������ ���� �� ������� ��������� ��������.")
                    </span>
                </div>
  
                   break;
            case SlideType.Video:
                   break;
            case SlideType.AudioAndImage:
                   break;
        }

        <div>
            @Html.LabelFor(m => m.Caption, @T("��������� ������"))
            @Html.EditorFor(m => m.Caption)
            <span class="hint">
                @T("����� �������������.")
            </span>
        </div>


</fieldset>

Problem is when trying to update like stated above, in a ContentPartDriver TryUpdateModel fails.

if to comment these statements:

...
@Html.LabelFor(m => m.Content["uri"], @T("Uri ��������")) @Html.EditorFor(m => m.Content["uri"]) @Html.ValidationMessageFor(m => m.Content["uri"], "*") ... @Html.LabelFor(m => m.Content["alt"], @T("���������� �����")) @Html.EditorFor(m => m.Content["alt"]) @Html.ValidationMessageFor(m => m.Content["alt"], "*")
...

TryUpdatemodel works fine, for example Position property is updated well.

The question is: Is there any way to store and edit Dictionary<string, string> properties for ContentPart? Would appriciate if you could point me a right dirrection for digging. Thank you in advance.

Jan 15, 2012 at 4:25 PM

I can see what you're trying to do and why you're trying to do it ... the bad news is this really isn't a recommended way to do things, which is why it's not working. Technically it might be possible to do with a custom ModelBinder, but I'd strongly advise against it; there are better ways to skin this cat!

Firstly; I wrote a set of modules called Media Garden which is designed to handle multiple types of content in an intelligent way which looks to be basically what you're doing here, so you might want to consider using/extending the work I've already done rather than reinventing this wheel. The project website is http://orchardmediagarden.codeplex.com.

If you don't want to use that (and out of the box I realise Media Garden is more involved than what most people need, at least until my next major release which should seriously wow people...) then there is still a much better approach to take. Basically you can keep your dictionary, but extract individual viewmodels from it, and push out a separate editor shape for each viewmodel instead of trying to lump everything together. It's still not the totally optimum way to do things in Orchard, really you should be implementing a completely separate ContentPart for each type of media using proper database fields and then build different Content Types to implement the different behaviours (you'd be thankful for this later on when e.g. you realise you need to query or sort on one of the values hidden away in your dictionary...) - But this way at least should be cleaner, as well as more flexible and extendible than having a switch statement in the view, and it'll work.

Jan 15, 2012 at 4:51 PM
Edited Jan 15, 2012 at 4:51 PM

My first though WAS to make separate content parts etc., but then I desided to make it other way, because i think Dashboard becomes a bit messy and harder to manage with all that tons of uncataloged content parts, types, modules, settings, content items and other... Regarding to your answear I guess I have to implement my first idea. Anyway, thank you for straighting things out.

Jan 15, 2012 at 5:22 PM

I find having separate content types makes things easier to manage, not the other way around (and of course you'll very rarely see lists of content parts, it's only when you're setting things up, and usually I build my content types in migrations instead of in the UI). You can still put all these parts in one single module/feature ... you don't have to provide any settings ... and of course you can hide admin features away from end users with permissions so they see a simpler view as needed. Personally I prefer having all those options and the ability to hide them, than simply not having those options ;) And there are generally very good reasons why Orchard is geared this way, it's taken me a long time working with Orchard to fully understand why things are how they are, but it really is usually best to follow the existing examples and conventions, especially if you want to release your module(s) to other users.

Jan 15, 2012 at 5:35 PM

Agreed. When in Rome... ;)