Field not saving or loading with the DB

Topics: Writing modules
Dec 22, 2011 at 11:51 PM

So I've been working on doing a custom modification of the advanced menu module so that menu items could be hidden based on a simple permission check. This didn't really seem like it would be too hard. First stem I just need to add a field to the AdvancedMenuItemPart so that a user can enter in the name of the permission that they want associated with that specific menu item. So I modified the record.

    public class AdvancedMenuItemPartRecord : ContentPartRecord
    {
        public virtual string Text { getset; }
        public virtual string Url { getset; }
        public virtual string Position { getset; }
        public virtual string MenuName { getset; }
        public virtual string SubTitle { getset; }
        public virtual string Classes { getset; }
        public virtual string PermissionNeeded { getset; }
        public virtual bool DisplayText { getset; }
        public virtual bool DisplayHref { getset; }
        public virtual int RelatedContentId { getset; }
    }

No problem. Then I modified the part.

    public class AdvancedMenuItemPart : ContentPart<AdvancedMenuItemPartRecord>
    {
        [StringLength(255)]
        public string Text
        {
            get { return Record.Text; }
            set { Record.Text = value; }
        }
 
        [Required]
        public string Position
        {
            get { return Record.Position; }
            set { Record.Position = value; }
        }
 
        public string Url
        {
            get { return Record.Url; }
            set { Record.Url = value; }
        }
 
        /// <summary>
        /// Name of the corresponding menu
        /// </summary>
        public string MenuName
        {
            get { return Record.MenuName; }
            set { Record.MenuName = value; }
        }
 
        /// <summary>
        /// Name of the corresponding menu
        /// </summary>
        public string SubTitle
        {
            get { return Record.SubTitle; }
            set { Record.SubTitle = value; }
        }
 
        /// <summary>
        /// Additional CSS classes to add to this menu item
        /// </summary>
        public string Classes
        {
            get { return Record.Classes; }
            set { Record.Classes = value; }
        }
 
 
        /// <summary>
        /// Display menu name
        /// </summary>
        public bool DisplayText
        {
            get { return Record.DisplayText; }
            set { Record.DisplayText = value; }
        }
 
        /// <summary>
        /// Should display menu item as link
        /// </summary>
        public bool DisplayHref
        {
            get { return Record.DisplayHref; }
            set { Record.DisplayHref = value; }
        }
 
        public string PermissionNeeded
        {
            get { return Record.PermissionNeeded; }
            set { Record.PermissionNeeded = value; }
        }
 
        public int RelatedItemId
        {
            get { return Record.RelatedContentId; }
        }
 
        public IContent RelatedItem { getset; }
 
        public RouteValueDictionary RouteValues { getset; }
    }


The driver is simple nothing to do here.

    public class AdvancedMenuItemPartDriver : ContentPartDriver<AdvancedMenuItemPart>
    {
        private readonly INotifier _notifier;
        private readonly IAdvancedMenuService _service;
        private readonly INavigationManager _nav;
        private const string TemplateName = "Parts/Menu.AdvancedItem";
 
        public Localizer T { getset; }
 
        public AdvancedMenuItemPartDriver(INotifier notifier, IAdvancedMenuService service, INavigationManager nav)
        {
            _notifier = notifier;
            _service = service;
            _nav = nav;
            T = NullLocalizer.Instance;
        }
 
        protected override DriverResult Editor(AdvancedMenuItemPart part, dynamic shapeHelper)
        {
            if(string.IsNullOrWhiteSpace(part.Position))
                part.Position = PositionUtility.GetNext(_nav.BuildMenu(part.MenuName ?? "main"));
            return ContentShape("Parts_Menu_AdvancedItem",
                    () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: part, Prefix: Prefix));
        }
 
        protected override DriverResult Editor(AdvancedMenuItemPart part, IUpdateModel updater, dynamic shapeHelper)
        {
            if (updater.TryUpdateModel(part, Prefix, nullnull))
            {
                _notifier.Information(T("Menu edited successfully"));
            }
            else
            {
                _notifier.Error(T("Error during menu update!"));
            }
            _service.TriggerSignal();
            return Editor(part, shapeHelper);
        }
    }

And can't forget that we need to add a bit to the view.

    <div>
        @Html.LabelFor(m => m.PermissionNeeded, @T("Permission needed to view"))
        @Html.EditorFor(m => m.PermissionNeeded)
        @Html.ValidationMessageFor(m => m.PermissionNeeded)
        <span class="hint">
            @T("A user will see this menu item only if they have the specified permission")
        </span>
    </div>

Finally add a method to the migrations file.

        public int UpdateFrom6() {
            SchemaBuilder.AlterTable("AdvancedMenuItemPartRecord", table => table.AddColumn<string>("PermissionNeeded", c => c.WithLength(1000).Nullable()));
 
            return 7;
        }

Done. But when I test the data doesn't get persisted. It says it saves correctly but the data I enter in for that one specific field is not persisted. No entries appear in the corresponding table! Also when i manually change the data in that table it doesn't show up when the record gets loaded. I checked that the data is getting updated on the part appropriately so the problem in somewhere between the database and the part but I don't know how to get into that process exactly since it should be all automatic and convention driven. The funny thing is that if I don't do the migration then the site breaks saying that it can't find the column. But once the column is there it won't use the thing! I've spent several hours on this problem but don't feel like I've made any progress, anyone have any idea why this could be happening?

    public class AdvancedMenuItemPartRecord : ContentPartRecord
    {
        public virtual string Text { getset; }
        public virtual string Url { getset; }
        public virtual string Position { getset; }
        public virtual string MenuName { getset; }
        public virtual string SubTitle { getset; }
        public virtual string Classes { getset; }
        //public virtual string PermissionNeeded { get; set; }
        public virtual bool DisplayText { getset; }
        public virtual bool DisplayHref { getset; }
        public virtual int RelatedContentId { getset; }
    }
Coordinator
Dec 23, 2011 at 12:09 AM

What does your handler look like?

Dec 23, 2011 at 2:08 AM

I didn't change it at all from the piotr had. But it looks like a mostly standard handler with regard to persistence. I could up load a zip if that would help.

    public class AdvancedMenuItemPartHandler : ContentHandler {
        public AdvancedMenuItemPartHandler(IRepository<AdvancedMenuItemPartRecord> repository)
        {
            Filters.Add(StorageFilter.For(repository));
            OnLoaded<AdvancedMenuItemPart>((ctx, part) => {
                                               var relatedItem = ctx.ContentManager.Get(part.Record.RelatedContentId);
                                               if (relatedItem != null) {
                                                   part.RelatedItem = relatedItem;
                                                   part.RouteValues = ctx.ContentManager.GetItemMetadata(relatedItem).DisplayRouteValues;
                                               }
                                           });
            OnRemoved<AdvancedMenuItemPart>((ctx, part) => repository.Delete(part.Record));
        }
    }
Coordinator
Dec 23, 2011 at 9:50 AM

Not sure. You seem to have, for some reason, a bad mapping here. Nothing seems obviously wrong to me here.

Dec 23, 2011 at 7:09 PM
Edited Dec 23, 2011 at 7:30 PM

So something is going wrong inside nHibernate? How do I troubleshoot something like that?

Coordinator
Dec 23, 2011 at 8:18 PM

I would start by removing the mappings file from app_data/sites/default, see if it makes a difference.

Dec 23, 2011 at 8:31 PM

Yeah I tried deleting that but the problem persisted.

Coordinator
Dec 23, 2011 at 8:34 PM

And you restarted IIS?

Dec 23, 2011 at 8:45 PM

Yeah all the usual suspects. I rebuilt everything, restarted IIS, deleted the mapping files. I even checked the Orchard.Web bin directory for stale .dlls (still not sure how those get in there sometimes.)

Coordinator
Dec 23, 2011 at 9:00 PM

Yeah, I don't see anything obviously wrong in there, so what you could do is try to add another property with a completely different name. You know, vary things one at a time. I've seen weird stuff happen with columns conflicting with reserved names at the hibernate level (not that yours looks like anything that could be reserved, but maybe worth trying).

Dec 23, 2011 at 9:23 PM

Oh sorry. I actually tried that already too. Changed it from permissionRequired (original name) to permissionNeeded. I guess I could try that again with something completely different like foo.

Coordinator
Dec 23, 2011 at 11:29 PM

Yes, that would be a good thing to try.

Dec 26, 2011 at 5:22 PM

well for some reason foo worked. Maybe starting a field with permission triggers some kind of special convention in nHibernate?

Coordinator
Dec 26, 2011 at 8:29 PM

It sounds likely, yes. Oh well. At least we have a good workaround now. Sorry it took so long to find that was the problem.

Dec 26, 2011 at 11:18 PM

The permission thing doesn't seem to be it. Naming the property ToView didn't work but oddly enough naming the property simply Permission worked fine. I'm really curious as to what's actually going on here but for now I'm just happy that I have a name that more or less fits what the property is and actually saves to the database. Thanks for the help.