CslaModelBinder not binding child list

CslaModelBinder not binding child list

Old forum URL: forums.lhotka.net/forums/t/12826.aspx


aramka posted on Wednesday, February 04, 2015

I have the following business classes

 

        public class EditableRoot:Csla.BusinessBase<EditableRoot>
    {
        public string Name { get; private set; }
        public int Id { get; private set; }

        public static EditableRoot New() {
            return DataPortal.Create<EditableRoot>();
        }

        public static readonly PropertyInfo<EditableChildList> ChildListProperty = RegisterProperty<EditableChildList>(c => c.ChildList, RelationshipTypes.Child);
        public EditableChildList ChildList
        {
            get { return GetProperty(ChildListProperty); }
            private set { SetProperty(ChildListProperty, value); }
        }
        protected override void DataPortal_Create()
        {
            ChildList = EditableChildList.New();
        }

    }
    public class EditableChildList : Csla.BusinessListBase<EditableChildList,EditableChild>
    {
        public static EditableChildList New() { return DataPortal.CreateChild<EditableChildList>(); }
    }
    public class EditableChild : Csla.BusinessBase<EditableChild>
    {
        public static readonly PropertyInfo<string> AssignedByProperty = RegisterProperty<string>(c => c.AssignedBy);
        public string AssignedBy
        {
            get { return GetProperty(AssignedByProperty); }
            private set { LoadProperty(AssignedByProperty, value); }
        }
        public static readonly PropertyInfo<int> DocTypeIDProperty = RegisterProperty<int>(c => c.DocTypeID);
        public int DocTypeID
        {
            get { return GetProperty(DocTypeIDProperty); }
            set { SetProperty(DocTypeIDProperty, value); }
        }
        public static EditableChild New(int docTypeId) { return DataPortal.CreateChild<EditableChild>(docTypeId); }
        void Child_Create(int docTypeId)
        {
            DocTypeID = docTypeId;
            AssignedBy = "AssignedBy" + docTypeId;
        }
       
    }

And I have controller

 

public class ComplexTypeController : Csla.Web.Mvc.Controller, Csla.Web.Mvc.IModelCreator
    {
        //
        // GET: /ComplexType/
        public ActionResult Create()
        {
            EditableRoot type = EditableRoot.New();
            ViewData.Model = type;
            return View();
        }

        [HttpPost]
        public ActionResult Create(EditableRoot complexType, FormCollection collection, string submit)
        {
            if (submit != "Create")
            {
                Random rand = new Random();

                complexType.ChildList.Add(EditableChild.New(rand.Next()));
               
            }
            ViewData.Model = complexType;
            return View();
        }

        public object CreateModel(Type modelType)
        {
            if (modelType == typeof(EditableRoot))
                return EditableRoot.New();
            else if (modelType == typeof(EditableChildList))
                return EditableChildList.New();
            else if (modelType == typeof(EditableChild))
                return EditableChild.New(0);
            else
                return Activator.CreateInstance(modelType);
        }
    }

And I have view

 

@model EditableRoot

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>
@using (Html.BeginForm())
{
    @Html.DisplayFor(m => m.Name);
    @Html.HiddenFor(m => m.Id);
    <table>
        <thead>
            <tr>
                <th>Child Type Name</th>
                <td>Child Type Id</td>
            </tr>
        </thead>
        <tbody>
            @for (int i = 0; i < Model.ChildList.Count(); i++)
            {
                <tr>
                    <td>
                        @Html.TextBoxFor(a => Model.ChildListIdea.AssignedBy)
                    </td>
                    <td>
                        @Html.TextBoxFor(a => Model.ChildListIdea.DocTypeID)
                    </td>
                </tr>
            }
        </tbody>
    </table>
   <input name="submit" type="submit" id="submit" value="Create" />
    <input name="submit" type="submit" id="process" value="Add Child" />                       
}

When I add an EditableChild by clicking the "Add Child" button then click the "Create" button the ChildList property of the EditableRoot object in the public ActionResult Create(EditableRoot complexType, FormCollection collection, string submit) call is not bound.

In other words the child list EditableRoot.ChildList is not bound, no items in the list, even though the html in the view follows the conventions for binding lists of complex types. And when I view the actual html in the browser the rows emitted for the items in EditableRoot.ChildList are present and correctly named.

However, I got the CslaModelBinder from github and put it into my project and wired up the mvc default modelbinder to use it. Then I changed the CslaModelBinder method

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

to look like this

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                //if (typeof(Csla.Core.IEditableCollection).IsAssignableFrom((bindingContext.ModelType)))
                //    return BindCslaCollection(controllerContext, bindingContext);

                var suppress = bindingContext.Model as Csla.Core.ICheckRules;
                if (suppress != null)
                    suppress.SuppressRuleChecking();
                var result = base.BindModel(controllerContext, bindingContext);
                return result;
            }

Everything worked. The EditableRoot.ChildList property is bound, the expected items are in the list.

Ultimately my modification of the CslaModelBinder method

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

comments out the handling of binding Csla IEditableCollection and so the method ultimately calls the BindModel method on the base class, DefaultModelBinder. Which works.

But, if I use my modified CslaModelBinder will I run into problems elsewhere?

If DefaultModelBinder can handle binding of Csla.Core.IEditableCollection types then why

if (typeof(Csla.Core.IEditableCollection).IsAssignableFrom((bindingContext.ModelType)))
                    return BindCslaCollection(controllerContext, bindingContext);

???

JonnyBee replied on Monday, February 09, 2015

Hi, 

IIRC the default model binder is able to create the list of objects - however the problem is that all items in the list will have IsNew = true.

IE: The metadata properties is not correct in the list.

You could also have (potentially) other properties on the list object that are not included in the view/postback - so how do you handle those properties?  

aramka replied on Tuesday, February 10, 2015

I understand the metastate of the objects in the child list will not have the correct state using the default model binder.

But the problem is that the csla binder is not binding the data from the post to my child list. The child list always has 0 items, even though the data from the post is named correctly according to the mvc convention of u

@for (int i = 0; i < Model.ChildList.Count(); i++)
            {
                <tr>
                    <td>
                        @Html.TextBoxFor(a => Model.ChildListIdea.PropertyName1)
                    </td>
                    <td>
                        @Html.TextBoxFor(a => Model.ChildListIdea.PropertyName2)
                    </td>
                </tr>
            }

 

The problem is that the CslaModelBinder doesnt even add the items to my child list. The default model binder does add the items to my list under the same circumstances. Why does the default model binder add the items and the CslaModelBinder does not?

Copyright (c) Marimer LLC