Problem with custom content part when a new content item it's a part of is created

Topics: Customizing Orchard
Nov 27, 2012 at 7:57 PM

I've created two custom content parts that are part of a content type. When I create a content item of this type and view the Parts member of this object, I see that one of the custom content parts is listed as being of type 'MyModule.Models.CustomPart1', while the other custom content part is of type 'Orchard.ContentManagement.ContentPart'. When I try to retrieve the second content part from the content item through ContentItem.As<CustomPart2>(), it returns null. I'm not sure why one of the custom content part works, while the other doesn't. 

Nov 27, 2012 at 10:20 PM

You probably misspelled it in your migration.

Nov 27, 2012 at 10:30 PM
Edited Nov 27, 2012 at 10:35 PM

I'm using the 'typeof(ContentPart).Name' notation throughout. I tried adding a content part that is relatively simple from the documentation, such as shown below. I create a content item of 'ProductType' and when I check the Parts member, I again see the type of ProductPart as Orchard.ContentManagement.ContentPart.
I get null when I call ProductContentItem.As<ProductPart>().

using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;

namespace DocumentationImporter.Models
    public class ProductPartRecord : ContentPartRecord
        public virtual string Sku { get; set; }
        public virtual float Price { get; set; }

    public class ProductPart : ContentPart<ProductPartRecord>
        public string Sku
            get { return Record.Sku; }
            set { Record.Sku = value; }

        public float Price
            get { return Record.Price; }
            set { Record.Price = value; }
SchemaBuilder.CreateTable("ProductPartRecord", table => table
                .Column("Sku", DbType.String)
                .Column("Price", DbType.Single)

                builder => builder.Attachable());

                type => type
Nov 28, 2012 at 12:08 AM

Show the code that creates the item.

Nov 28, 2012 at 12:33 AM
Edited Nov 28, 2012 at 6:26 AM
var productItem = Services.ContentManager.New("ProductType");
var productPart = productItem.As<ProductPart>(); //null
var bodyPart = productItem.As<BodyPart>(); //..Core.Common.Models.BodyPart object
Nov 28, 2012 at 6:21 AM

Right, in your migration, the type is called "ProductType", and then you're asking content manager for "Product".

Nov 28, 2012 at 4:52 PM

Sorry, I incorrectly typed 'Product' as the content type parameter in my previous comment. I actually use 'ProductType' in my code. As I understand it, Orchard looks for content part classes deriving from the ContentPart class and having the same name as the parts comprising a content type. If it finds a match, then it instantiates it; if it doesn't find a match, then it instantiates a generic ContentPart class. In my case, it seems that the latter is occurring and I have no idea as to why. 

For my custom content part, I have defined a CustomContentPartRecord class and a CustomContentPart class deriving from ContentPart<CustomContentPartRecord>. I have created a 'CustomContentPartRecord' table for the data entities and a 'CustomContentPart' part in Migrations.cs. When I attach this part to a content type and view the parts of a content item instantiated from that content type, I see the type of what should be the CustomContentPart object as simply Orchard.ContentManagement.ContentPart. Within this object, I check the PartDefinition member and see that the Name is set to 'CustomContentPart'. 

Nov 28, 2012 at 9:47 PM

No idea. I'd continue looking for differences with a part that works: the handler, etc. If you can still not find it, step through the New call in a debugger.

Dec 12, 2012 at 1:15 PM

I've take a pack of hours today troubleshooting this issue. 

The reason why this takes place is the code in ContentPartDriverCoordinator class:

var partInfos = _drivers.SelectMany(cpp => cpp.GetPartInfo()).ToList();

            foreach (var typePartDefinition in contentTypeDefinition.Parts) {
                var partName = typePartDefinition.PartDefinition.Name;
                var partInfo = partInfos.FirstOrDefault(pi => pi.PartName == partName);
                var part = partInfo != null 
                    ? partInfo.Factory(typePartDefinition) 
                    : new ContentPart { TypePartDefinition = typePartDefinition };
I.e. Content part must have a ContentPartDriver to be created at the runtime. Otherwise it's just a pack of metadata.