orchardcmsorchard-modulesorchardcms-1.10

Multiple items created when importing a single content item in Orchard 1.10.1


I've got a problem with importing a certain content type. If I export a single item and then import it back not only does it not match and update the existing item, it even creates a second, totally empty item as well.

Screenshot of duplicate content item.

I'm using a custom identity property within a content part, which may be the problem, but I would've thought that even if it can't match an existing item it shouldn't be creating empty content items.

The handler for the part overrides the GetItemMetadata to add the identity:

protected override void GetItemMetadata(GetContentItemMetadataContext context)
{
    var part = context.ContentItem.As<ProductPart>();

    if (part != null)
    {
        context.Metadata.Identity.Add("PartNumber", part.PartNumber);
    }
}

The Importing and Exporting functions are defined as follows in the driver:

protected override void Importing(ProductPart part, ImportContentContext context)
{
    // Don't do anything if the tag is not specified.
    if (context.Data.Element(part.PartDefinition.Name) == null)
    {
        return;
    }

    context.ImportAttribute(part.PartDefinition.Name, "PartNumber", partNumber =>
        part.PartNumber = partNumber
    );
}

protected override void Exporting(ProductPart part, ExportContentContext context)
{
    context.Element(part.PartDefinition.Name).SetAttributeValue("PartNumber", part.PartNumber);
}

This is the exported XML I'm using for testing:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--Exported from Orchard-->
<Orchard>
  <Recipe>
    <ExportUtc>2016-07-26T14:03:45.9022599Z</ExportUtc>
  </Recipe>
  <Content>
    <Product Id="/PartNumber=BMF 20 20 30" Status="Published">
      <EnumerationField.Units Value="Metric" />
      <NumericField.InnerDiameter Value="0.19" />
      <NumericField.Length Value="0.185" />
      <NumericField.OuterDiameter Value="0.5" />
      <TaxonomyField.ProductRange Terms="/alias=taxonomies\/product-ranges\/oilite/Identifier=/Reference=oilite" />
      <TaxonomyField.ProductType Terms="/alias=taxonomies\/product-types\/plain/Identifier=/Reference=plain" />
      <CommonPart Owner="/User.UserName=admin" CreatedUtc="2016-07-26T14:03:27.8841625Z" PublishedUtc="2016-07-26T14:03:27.9597274Z" ModifiedUtc="2016-07-26T14:03:27.9547403Z" />
      <ProductPart PartNumber="BMF 20 20 30" />
    </Product>
  </Content>
</Orchard>

All of the properties other than PartNumber are defined as Fields, so PartNumber is the only thing that goes into its own table in the database.

The full definition for the part is as follows:

SchemaBuilder.CreateTable("ProductPartRecord", table => table
    .ContentPartRecord()
    .Column<string>("PartNumber", column => column
        .WithLength(50)
    )
);

ContentDefinitionManager.AlterPartDefinition("ProductPart", builder => builder
    .Attachable()
    .WithDescription("Turns a content type into a Product with a Part Number."));

ContentDefinitionManager.AlterPartDefinition("ProductPart", builder => builder
    .WithField("ProductRange", cfg => cfg
        .OfType("TaxonomyField")
        .WithDisplayName("Material/Range")
        .WithSetting("TaxonomyFieldSettings.Taxonomy", "Product Ranges")
        .WithSetting("TaxonomyFieldSettings.LeavesOnly", "True")
        .WithSetting("TaxonomyFieldSettings.Required", "True")
        .WithSetting("TaxonomyFieldSettings.SingleChoice", "True")
        .WithSetting("TaxonomyFieldSettings.Autocomplete", "False")
        .WithSetting("TaxonomyFieldSettings.AllowCustomTerms", "False")
    )
    .WithField("ProductType", cfg => cfg
        .OfType("TaxonomyField")
        .WithDisplayName("Type/Form")
        .WithSetting("TaxonomyFieldSettings.Taxonomy", "Product Types")
        .WithSetting("TaxonomyFieldSettings.LeavesOnly", "True")
        .WithSetting("TaxonomyFieldSettings.Required", "True")
        .WithSetting("TaxonomyFieldSettings.SingleChoice", "True")
        .WithSetting("TaxonomyFieldSettings.Autocomplete", "False")
        .WithSetting("TaxonomyFieldSettings.AllowCustomTerms", "False")
    )
    .WithField("Units", cfg => cfg
        .OfType("EnumerationField")
        .WithDisplayName("Units")
        .WithSetting("EnumerationFieldSettings.Required", "True")
        .WithSetting("EnumerationFieldSettings.Options", "Metric\r\nImperial")
        .WithSetting("EnumerationFieldSettings.ListMode", "Radiobutton")
    )
    .WithField("InnerDiameter", cfg => cfg
        .OfType("NumericField")
        .WithDisplayName("Inner Diameter")
        .WithSetting("NumericFieldSettings.Scale", "3")
    )
    .WithField("OuterDiameter", cfg => cfg
        .OfType("NumericField")
        .WithDisplayName("Outer Diameter")
        .WithSetting("NumericFieldSettings.Scale", "3")
    )
    .WithField("Length", cfg => cfg
        .OfType("NumericField")
        .WithDisplayName("Length")
        .WithSetting("NumericFieldSettings.Scale", "3")
    )
    .WithField("Thickness", cfg => cfg
        .OfType("NumericField")
        .WithDisplayName("Thickness")
        .WithSetting("NumericFieldSettings.Scale", "3")
    )
    .WithField("Width", cfg => cfg
        .OfType("NumericField")
        .WithDisplayName("Width")
        .WithSetting("NumericFieldSettings.Scale", "3")
    )
    .WithField("SurfaceStyle", cfg => cfg
        .OfType("EnumerationField")
        .WithDisplayName("Surface Style")
        .WithSetting("EnumerationFieldSettings.Options", "\r\nPlain\r\nSmooth\r\nHoles\r\nDiamond Pockets\r\nRound Pockets")
        .WithSetting("EnumerationFieldSettings.ListMode", "Dropdown")
    )
    .WithField("BackingMaterial", cfg => cfg
        .OfType("EnumerationField")
        .WithDisplayName("Backing Material")
        .WithSetting("EnumerationFieldSettings.ListMode", "Dropdown")
        .WithSetting("EnumerationFieldSettings.Options", "\r\nMild Steel\r\nBronze Backing")
    )
);

Any help would be greatly appreciated!


Solution

  • You need to implement an IIdentityResolverSelector for you PartNumber property. That will enable the import to properly understand your new identity. There are multiple examples of that in the code to show you the way.