Trying to understand a design issue/philophy with contentparts

Topics: Customizing Orchard
Nov 10, 2014 at 9:30 AM
Edited Nov 10, 2014 at 9:32 AM
Hi,

I've been exploring building custom content types. In the available demos, I always see that a custom content type is effectively backed by a db table. How this is done is fairly straightforward. However, I realized the other day that I really don't need a custom table. I'm simply using built-parts and they natively store their own stuff perfectly fine.

Thus, I've been trying to effectively create what I can do via the UI (create a type, add a Title part, add a Body part). In "downgrading"/switching from a db-backed type, I removed the content handler that manages the IRepository and all references to the ContentPartRecord. However, when I try to create a new item, the system throws an exception because the insert statement fails because there is no table for the record.

Here's a rundown of what I have (in shorthand)..

Model:
public class MyPart : ContentPart {
    public string Title {
      get {
        return this.As<TitlePart>().Title;
      }
      set {
        this.As<TitlePart>().Title = value;
      }
    }
}
Driver: pretty much the "standard" demo one w/ a Display and 2 Editor methods
Handler: nothing (removed once the record was removed)


I'm a little stumped. How can I tell the system to just use it's internal structure? If I create a simple type through the ui with just a Title and Body part, there's no issue. What am I overlooking?

Also, aside from the management of a schema, is there good source for pros/cons on why a design should (or should not) use a custom contentpartrecord backed by a table?

-Maurice
Developer
Nov 10, 2014 at 9:43 AM
You should only use a content part with a backing record if you want to use its values when querying. Other than that, it is recommended to leverage InfoSet storage of the content item.

Please show us how you are currently defining your content type and parts by showing your migration code, and at which point you see the exception.
Nov 10, 2014 at 3:26 PM
Your example above assumes that the ContentType you will attach MyPart to will also ALWAYS have the TitlePart attached to it. Try adding the TitlePart to your ContentType and see if it works after that.

Note that If this is the only property that MyPart has then it's redundant with the TitlePart and you should use the TitlePart instead. If you need specific functionality for the Title on your ContentType you can use Shape Alterntates to override views or you can create new Handlers/Drivers for the TitlePart in your module.
Nov 10, 2014 at 3:33 PM
Edited Nov 10, 2014 at 4:31 PM
Actually, I think it's a very bad idea to reference another Part in your own Part like this. So even if Title is not the only property in MyPart I would highly suggest you either
  1. Attach the TitlePart to your ContentType and remove the Title property from MyPart
  2. Make MyPart inherit from ITitleAspect, leave the Title property but don't reference TitlePart. Store the Title in the Infoset and/or MyPartRecord if you have one. You can use Retrieve(...) and Store(...) to accomplish that easily (see BodyPart.cs for a good example of using Retrieve/Store)
Nov 10, 2014 at 9:18 PM
Edited Nov 10, 2014 at 9:18 PM
Thanks for the feedback.

First, I was able to get this work after cleaning everything out, letting IIS reset, and closing down VS. Prior to each deployment, I clean out the site (i.e. new db) to ensure I start with a known data state but I rarely close down VS. My suspicion is that maybe VS was holding onto something and once I shutdown for the night, the old stuff was released. I was clearly getting a sql exception about a missing table corresponding to the original record class. Dunno - very frustrating and quite embarrassing to come in the next morning after posting a question to see it just work.

I think the broader question of which persistence techniques should be utilized is well worth discussing in the "Writing a Content Part" help page. It took me quite a while to realized that I didn't need to write my own schema and that I could use the built-in types and fields quite effectively as-is. It seems there are at least 2 (or more) different major implementation styles and it has been super difficult finding the info as it's spread all over the place.

Let me illustrate what I was trying to do:
  • Create a simple part (mypart) that contains 2 custom fields - start and end date. The part will reference the 2 dates, body and text.
  • Part is added to a custom type.
  • The content type will contain mypart, titlepart, body, and comment.
  • Avoid creating a custom table
The idea was to have the item only display between the dates provided. I figured this would be a good starter learning lesson.

When I started, I followed the demo and as I mentioned it was straightforward. Then it dawned on me that what I wanted to create didn't really need its own table. My prototypes via the UI clearly worked without Orchard building a custom table under the hood. So I tried to find an example of something that contained a part without a backing record. I started looking at the BlogPostPart and found the methodology using this.As<TitlePart>().Title.

The content type is defined in the migration code as:
ContentDefinitionManager.AlterPartDefinition(typeof(MyPart).Name,
                                             builder => builder.Attachable()
                                .WithField("StartDate", field => field.OfType("DateTimeField")
                                    .WithDisplayName("Start Date")
                                    .WithSetting("DateTimeFieldSettings.Display", "DateAndTime"))
                                .WithField("EndDate", field => field.OfType("DateTimeField")
                                    .WithDisplayName("End Date")
                                    .WithSetting("DateTimeFieldSettings.Display", "DateAndTime"))
                                .WithField("Image", field => field.OfType("MediaLibraryPickerField")
                                    .WithDisplayName("Promotional Image"))

ContentDefinitionManager.AlterTypeDefinition("MyPart",
                                             builder => builder.WithPart("MyPart")
                                             .WithPart("TitlePart")
                                             .WithPart("BodyPart")
                                             .WithPart("CommonPart")
                                             .DisplayedAs("My Custom Type")
                                             .WithSetting("ContentTypeSettings.Creatable", "True")
                                             .WithSetting("ContentTypeSettings.Draftable","True"));
As I understand the functionality today, if I don't need a query against the properties (i.e. get me all items with startdate > somedate) then the persistence model that doesn't use a backing contentpartrecord is ok. Is there any other reason why to use or not use a backing record?

Why is referencing another part a bad idea? The BlogPart model does this and served as my reference. In the long run, the idea is to add additional fields so that the rendering logic can adjusted. This logic will depend on values within the other parts. Is there a better way to access this data?

Thanks once again,
-Maurice