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

Topics: Customizing Orchard
Nov 27, 2012 at 8: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. 

Coordinator
Nov 27, 2012 at 11:20 PM

You probably misspelled it in your migration.

Nov 27, 2012 at 11:30 PM
Edited Nov 27, 2012 at 11: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>
    {
        [Required]
        public string Sku
        {
            get { return Record.Sku; }
            set { Record.Sku = value; }
        }

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

ContentDefinitionManager.AlterPartDefinition("ProductPart",
                builder => builder.Attachable());

ContentDefinitionManager.AlterTypeDefinition("ProductType",
                type => type
                    .WithPart("BodyPart")
                    .WithPart("ProductPart")
            );
Coordinator
Nov 28, 2012 at 1:08 AM

Show the code that creates the item.

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

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

Nov 28, 2012 at 5: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'. 

Coordinator
Nov 28, 2012 at 10: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 2: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 };
                context.Builder.Weld(part);
            }
I.e. Content part must have a ContentPartDriver to be created at the runtime. Otherwise it's just a pack of metadata.