"You cannot save a class that does not contain a property that represents the item ID" when GlassCast(ing) a branch template item, why?
Asked Answered
I

2

5

I've been banging my head against this all day and I've run out of ideas. Basically because glass.mapper doesn't appear to support branch templates I'm creating a sitecore item using the standard DB methods. I then use GlassCast<T>() to give me a properly typed object.

This typed object appears to contain a id field however, when I try and save the object I get the error:

You cannot save a class that does not contain a property that represents the item ID

Here's my method:

private Glass.Mapper.Sc.ISitecoreService SitecoreService;

public T CreateFromBranch<T>(string parentPath, string itemName,
            Guid branchTemplateId) where T : class, ICmsItem
{

    Item parent = SitecoreService.Database.GetItem(parentPath);//SitecoreService.GetItem<Item>(parentPath);
    if (parent == null)
        throw new NullReferenceException("Cannot load item to act as parent from path: " + parentPath);

    BranchItem branch = SitecoreService.Database.GetItem(new ID(branchTemplateId));
    if (branch == null)
        throw new ApplicationException("Failed ot find template branch with the id:" + branchTemplateId);

    Item itemFromBranch = parent.Add(itemName, branch);                   
    if (itemFromBranch == null)
        throw new ApplicationException("Failed to create item from branch template. BranchId: " + branchTemplateId);


    itemFromBranch.Fields.ReadAll();


    T typedItem  = itemFromBranch.GlassCast<T>();
    //Id is accessible here and is the id of the item that is created!
    Guid id = typedItem.Id;
    typedItem.DisplayName = itemDisplayName;

    //Exception thrown here!
    SitecoreService.Save(typedItem);

    return typedItem;
}

Interface:

[SitecoreType(AutoMap = true)]
public interface ICmsItem
{
    //Id flagged here! Same in the concrete class
    [SitecoreId]
    Guid Id { get; }

    [SitecoreInfo(SitecoreInfoType.Name)]
    string Name { get; set; }

    [SitecoreInfo(SitecoreInfoType.DisplayName)]
    string DisplayName { get; set; }

    [SitecoreInfo(SitecoreInfoType.Path)]
    string Path { get; set; }

    [SitecoreInfo(SitecoreInfoType.TemplateId)]
    Guid TemplateId { get; set; }

    [SitecoreField(FieldName = "__Never publish")]
    bool NeverPublish { get; set; }

    [SitecoreField(FieldName = "__Icon")]
    string CmsIcon { get; set; }

    [SitecoreField(FieldName = "__Workflow")]
    Guid? Workflow { get; set; }

    [SitecoreField(FieldName = "__Workflow state")]
    Guid? WorkflowState { get; set; }

    [SitecoreField(FieldName = "__Sortorder")]
    int SortOrder { get; set; }
}

the item is created in sitecore. But it does not have the updated display name. If I alter the code so that it doesn't use glass mapper at all:

if (!string.IsNullOrWhiteSpace(itemDisplayName))
{
     itemFromBranch.EditField("__Display name", itemDisplayName);
}

It works!

Anybody got any idea what I'm doing wrong here?

Inandin answered 2/11, 2015 at 14:31 Comment(5)
Can you try adding a set modifier to the Id property to see if that makes any difference.Smyth
Good idea @Smyth but appears to make no difference. I've taken the interface totally out of the equation now and I'm typing it to a concrete class. Still same issueInandin
Sorry, no idea off the top of my head, everything looks ok. Personally, I would not cast to Glass just to update the field, your working sample without using Glass is fine IMO. Does returning the cast object works ok following on from this code?Smyth
I'm not just updating that field. I'm updating pretty much all of the fields. It's just a shortened example. Got there in the end, thanks for your help @SmythInandin
Nice, glad you managed to get to the bottom of it!Smyth
I
7

Got there in the end. The question doesn't actually contain the missing piece but I'm putting an answer in case anyone else is stuck in this cul-de-sac!

My issues was actually the concrete class. What I hadn't noticed was this class had other classes contained within it:

public class V2Development : ICmsItem
{
    .....
    [SitecoreField]
    public virtual IEnumerable<CallToActionButtonBase> CallToActionButtons { get; set; }
}

The CallToActionButtonBase type didn't have an ID field! So glass mapper was working its way down the whole dependency tree and trying to resolve all of the objects. Then when it hit this one, it failed, because it didn't have an id (could we have a more constructive error message next time please Mr GlassMapper? Like maybe say which object is missing the id?)

My fix was to add a layer of abstraction, so I have two objects one with children, one without out. I then create an instance of the object without children (an interface would of done a similar job but, TBH, this solution is swimming in interfaces and I'm trying to avoid using anymore).

Inandin answered 2/11, 2015 at 16:6 Comment(1)
submitted a thread with glass.mapper to try and get this error message more helpfulInandin
N
1

The same issue occurs if someone uses GlassMapper's Editable helper class with a model property which doesn't have an ID attribute associated with it. The simplest way to fix this problem is to declare an interface with ID property & inherit it(You can add it to your base type if you already have one).

[SitecoreType]
public interface ISitecoreItem
{
    [SitecoreId]
    Guid ID { get; set; }

    [SitecoreInfo(Type = SitecoreInfoType.TemplateId)]
    Guid TemplateID { get; set; }

    [SitecoreInfo(Type = SitecoreInfoType.TemplateName)]
    string TemplateName { get; set; }

    [SitecoreInfo(Type = SitecoreInfoType.Path)]
    string Path { get; set; }
}

Then inherit the above in Component/Page specific models.

public interface ISiteWide : ISitecoreItem
{
    //Component specific properties goes here 
}
Naissant answered 8/10, 2021 at 9:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.