Can't save custom part data in controller

Topics: General, Troubleshooting, Writing modules
Apr 7, 2013 at 7:56 PM
Edited Apr 7, 2013 at 7:56 PM
Hi, all!
I'am studying module developing... I create simple custom type and trying save that type in my controller, not in driver (but the driver exist too)

That's Controller:
[ValidateInput(false), Admin]
    public class PublicationController : Controller, IUpdateModel
    {
        private readonly IContentManager _contentManager;
        private readonly ITransactionManager _transactionManager;
        private readonly IPublicationService _publicationService;

        public PublicationController(
            IOrchardServices services,
            IContentManager contentManager,
            IShapeFactory shapeFactory,
            ITransactionManager transactionManager,
            IPublicationService publicationService)
        {
            Services = services;
            _contentManager = contentManager;
            Shape = shapeFactory;
            _transactionManager = transactionManager;
            _publicationService = publicationService;
            T = NullLocalizer.Instance;
        }

        dynamic Shape { get; set; }
        public IOrchardServices Services { get; set; }
        public Localizer T { get; set; }


        public ActionResult Create() {
            var publication = Services.ContentManager.New<PublicationPart>("Publication");
            if (publication == null)
                return HttpNotFound();
            dynamic model = Services.ContentManager.BuildEditor(publication);
            return View((object)model);
        }

        [HttpPost, ActionName("Create")]
        public ActionResult CreatePost()
        {
            var publication = Services.ContentManager.New<PublicationPart>("Publication");

            publication.As<PublicationPart>().MetaTitle = "blablabla";
            _contentManager.Create(publication);
            dynamic model = _contentManager.UpdateEditor(publication, this);
            
            if (!ModelState.IsValid)
            {
                _transactionManager.Cancel();
                return View((object)model);
            }

            _contentManager.Publish(publication.ContentItem);
            Services.Notifier.Information(T("Статья была создана успешно"));
            return View((object)model);
        }



        public ActionResult Index() {

            return View();
        }

        bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties)
        {
            return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
        }

        void IUpdateModel.AddModelError(string key, LocalizedString errorMessage)
        {
            ModelState.AddModelError(key, errorMessage.ToString());
        }
    }
The Driver:
public class PublicationDriver : ContentPartDriver<PublicationPart> {

        private readonly IPublicationService _publicationService;

        public PublicationDriver(IPublicationService publicationService) {
            _publicationService = publicationService;
        }

        protected override string Prefix
        {
            get { return "Publication"; }
        }

        protected override DriverResult Display(PublicationPart part, string displayType, dynamic shapeHelper) {
            return ContentShape("Parts_Publication", () => shapeHelper.Parts_Publication(
                PTitle: part.Title,
                PBody: part.Body));
        }

        protected override DriverResult Editor(PublicationPart part, dynamic shapeHelper) {
            return ContentShape("Parts_Publication_Edit",
                () => shapeHelper.EditorTemplate(
                    TemplateName: "Parts/Publication",
                    Model: part,
                    Prefix: Prefix));
        }

        protected override DriverResult Editor(PublicationPart part, IUpdateModel updater, dynamic shapeHelper) {
            updater.TryUpdateModel(part, Prefix, null, null);
            return Editor(part, shapeHelper);
        }

    }
When i save content item of my type in Admin dashboard - i have my custom type item saved - but not:
publication.As<PublicationPart>().MetaTitle = "blablabla";
It's empty in DB
Can you please help me?

Sorry for bad English.....
Coordinator
Apr 7, 2013 at 11:24 PM
Anything in app_data/logs?
Apr 8, 2013 at 6:52 AM
No, nothing...
Coordinator
Apr 8, 2013 at 7:18 AM
I don't see any obvious problem here. Maybe compare to a controller doing that sort of thing, maybe in the blog module?
Apr 8, 2013 at 7:24 AM
My controller is already from Blog example :(
Apr 8, 2013 at 5:41 PM
I am attach debuger in controller and it show that my publication.As<PublicationPart>().MetaTitle = "blablabla"; set to null in UpdateEditor method:
public dynamic UpdateEditor(IContent content, IUpdateModel updater, string groupId = "") {
            var context = new UpdateContentContext(content.ContentItem);

            Handlers.Invoke(handler => handler.Updating(context), Logger);

            var result = _contentDisplay.Value.UpdateEditor(content, updater, groupId);

            Handlers.Invoke(handler => handler.Updated(context), Logger);

            return result;
        }
in row:
var result = _contentDisplay.Value.UpdateEditor(content, updater, groupId);
Can anybody help?
Apr 9, 2013 at 6:45 PM
Perhaps model binding fails due to incorrect form element names?
Apr 9, 2013 at 9:03 PM
I double check model binding - all is ok
Now? i have new problem: i deleted controller and try to use driver - get NullReferenceException in Driver with my Custom Content Part
2013-04-09 23:52:10,587 [122] Orchard.ContentManagement.Drivers.Coordinators.ContentPartDriverCoordinator - NullReferenceException thrown from IContentPartDriver by Timbioz.Logic.Drivers.PublicationPartDriver
System.NullReferenceException: Object reference not set to an instance of an object.
   at Timbioz.Logic.ViewModels.PublicationViewModel.set_Anounce(String value) in f:\AzureWeb\EVO\src\Orchard.Web\Modules\Timbioz.Logic\ViewModels\PublicationViewModel.cs:line 52
   at Timbioz.Logic.ViewModels.PublicationViewModel.Map(PublicationPart source, PublicationViewModel target) in f:\AzureWeb\EVO\src\Orchard.Web\Modules\Timbioz.Logic\ViewModels\PublicationViewModel.cs:line 282
   at Timbioz.Logic.ViewModels.PublicationViewModel.Map(PublicationPart source) in f:\AzureWeb\EVO\src\Orchard.Web\Modules\Timbioz.Logic\ViewModels\PublicationViewModel.cs:line 278
   at Timbioz.Logic.Drivers.PublicationPartDriver.Editor(PublicationPart part, Object shapeHelper) in f:\AzureWeb\EVO\src\Orchard.Web\Modules\Timbioz.Logic\Drivers\PublicationPartDriver.cs:line 37
   at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2)
   at Orchard.ContentManagement.Drivers.ContentPartDriver`1.Orchard.ContentManagement.Drivers.IContentPartDriver.BuildEditor(BuildEditorContext context) in f:\AzureWeb\EVO\src\Orchard\ContentManagement\Drivers\ContentPartDriver.cs:line 43
   at Orchard.ContentManagement.Drivers.Coordinators.ContentPartDriverCoordinator.<>c__DisplayClassd.<BuildEditor>b__c(IContentPartDriver driver) in f:\AzureWeb\EVO\src\Orchard\ContentManagement\Drivers\Coordinators\ContentPartDriverCoordinator.cs:line 55
   at Orchard.InvokeExtensions.Invoke[TEvents](IEnumerable`1 events, Action`1 dispatch, ILogger logger) in f:\AzureWeb\EVO\src\Orchard\InvokeExtensions.cs:line 17
My PublicationPart:
using System;
using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;

namespace Timbioz.Logic.Models
{
public class PublicationPart : ContentPart<PublicationPartRecord>
    {
        public string Title
        {
            get { return Record.Title; }
            set { Record.Title = value; }
        }
        public string SubTitle
        {
            get { return Record.SubTitle; }
            set { Record.SubTitle = value; }
        }
        public string Anounce
        {
            get { return Record.Anounce; }
            set { Record.Anounce = value; }
        }
        public string ImagePath
        {
            get { return Record.ImagePath; }
            set { Record.ImagePath = value; }
        }
................................................................
}
public class PublicationPartRecord : ContentPartRecord
    {

        public virtual string Title { get; set; }

        public virtual string SubTitle { get; set; }

        public virtual string Anounce { get; set; }

        public virtual string ImagePath { get; set; }

       ............................................
}
}
Coordinator
Apr 9, 2013 at 11:19 PM
Did you change anything to the driver?
Apr 10, 2013 at 4:40 PM
There is my Driver:
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.Autoroute.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.ContentManagement.Drivers;
using Orchard.Core.Common.Drivers;
using Orchard.Mvc.Html;
using Timbioz.Logic.Models;
using Timbioz.Logic.Services;
using Timbioz.Logic.ViewModels;

namespace Timbioz.Logic.Drivers
{
    public class PublicationPartDriver : ContentPartDriver<PublicationPart> {

        private readonly IPublicationService _publicationService;

        public PublicationPartDriver(IPublicationService publicationService)
        {
            _publicationService = publicationService;
        }

        protected override string Prefix
        {
            get { return "Publication"; }
        }

        protected override DriverResult Display(PublicationPart part, string displayType, dynamic shapeHelper)
        {
            return ContentShape("Parts_Publication", () => shapeHelper.Parts_Publication(
                PTitle: part.Title,
                PBody: part.Body));
        }

        protected override DriverResult Editor(PublicationPart part, dynamic shapeHelper) {
            var viewModel = PublicationViewModel.Map(part);
            var newModel = new PublicationViewModel();
            return ContentShape("Parts_Publication_Edit",
                () => shapeHelper.EditorTemplate(
                    TemplateName: "Parts/Publication",
                    Model: viewModel ?? newModel,
                    Prefix: Prefix));
        }

        protected override DriverResult Editor(PublicationPart part, IUpdateModel updater, dynamic shapeHelper) {
            var viewModel = new PublicationViewModel();
            if(updater.TryUpdateModel(viewModel, Prefix, null, null)) {
                PublicationViewModel.Map(viewModel, part);
            }
            return Editor(part, shapeHelper);
        }

    }
}
Apr 10, 2013 at 4:44 PM
End ViewModel:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Orchard.Data;
using Timbioz.Logic.Models;

namespace Timbioz.Logic.ViewModels
{
    public class PublicationViewModel
    {

        public PublicationPart Publication { get; set; }

        [Display(Name = "Количество дополнительных блоков текста")]
        public int AdditionalBody { get; set; }

        public SelectList _bodySelectList;
        
        [Display(Name = "Количество дополнительных тегов Title для изображений")]
        public int AdditionalImageTitle { get; set; }

        public SelectList _imtSelectList;

        #region Model PublicationPart
        [Required]
        [Display(Name = "Заголовок статьи")]
        [StringLength(128, ErrorMessage = "Заголовок статьи не может превышать 128 символов")]
        public string Title
        {
            get { return Publication.Record.Title; }
            set { Publication.Record.Title = value; }
        }

        [Required]
        [Display(Name = "Подзаголовок")]
        [StringLength(200, ErrorMessage = "Текст Подзаголовка не может превышать 200 символов")]
        public string SubTitle
        {
            get { return Publication.Record.SubTitle; }
            set { Publication.Record.SubTitle = value; }
        }
................................................................ more similar code

public static PublicationViewModel Map(PublicationPart source)
        {
            return Map(source, new PublicationViewModel());
        }

        public static PublicationViewModel Map(PublicationPart source, PublicationViewModel target) {
            target.Anounce = source.Anounce;
            target.Body = source.Body;
            target.Body1 = source.Body1;
            target.Body2 = source.Body2;
            target.Body3 = source.Body3;
            target.BodyTitle1 = source.BodyTitle1;
            target.BodyTitle2 = source.BodyTitle2;
            target.BodyTitle3 = source.BodyTitle3;
           .............. more code

            return target;
        }

        public static void Map(PublicationViewModel source, PublicationPart target)
        {
            target.Anounce = source.Anounce;
            target.Body = source.Body;
            target.Body1 = source.Body1;
            target.Body2 = source.Body2;
            target.Body3 = source.Body3;
            target.BodyTitle1 = source.BodyTitle1;
       .............. more code
        }
    }
}
Thanks in advance
Apr 10, 2013 at 7:02 PM
You are newing up your view model, which contains properties using properties of a part as their backing storage.
Question is: where do you instantiate this PublicationPart? From the looks of it, it is null at the time you invoke TryUpdateModel from your driver. If it is, you should be able to tell by looking at your log files.
Coordinator
Apr 11, 2013 at 2:09 AM
Yup, I was going to say the same thing :) This is the suspicious part: public PublicationPart Publication { get; set; }
Apr 11, 2013 at 6:32 AM
Yes, that was a problem. After remove public PublicationPart Publication all is ok
Big Thanks Bertrand and Sipke!!