Making model binding work for a model with no default constructor

半腔热情 提交于 2020-01-03 15:53:50

问题


I’ve been trying to figure a way to have a model-binding go on with a model with a constructor with arguments.

the action:

 [HttpPost]
        public ActionResult Create(Company company, HttpPostedFileBase logo)
        {
            company.LogoFileName = SaveCompanyLogoImage(logo);
            var newCompany = _companyProvider.Create(company);
            return View("Index",newCompany);
        }

and the model

public  Company(CustomProfile customProfile)
        {
            DateCreated = DateTime.Now;
            CustomProfile = customProfile;
        }

I've done my research and seems I need to mess around with my ninjectControllerfactory:

  public class NinjectControllerFactory : DefaultControllerFactory
    {
        private readonly IKernel ninjectKernel;

        public NinjectControllerFactory()
        {
            ninjectKernel = new StandardKernel();
            AddBindings();
        }

        protected override IController GetControllerInstance(RequestContext requestContext,
                                                             Type controllerType)
        {
            return controllerType == null
                       ? null
                       : (IController) ninjectKernel.Get(controllerType);
        }

        private void AddBindings()
        {
            ninjectKernel.Bind<IAuthProvider>().To<FormsAuthProvider>();
            ninjectKernel.Bind<IMembershipProvider>().To<MembershipProvider>();
            ninjectKernel.Bind<ICustomProfileProvider>().To<CustomProfileProvider>();
            ninjectKernel.Bind<ICompanyProvider>().To<CompanyProvider>();
        }
    }

I also feel I need to modify my model binder but I'm not clear on the way forward:

 public class CustomProfileModelBinder : IModelBinder
{
    private const string sessionKey = "CustomProfile";

    #region IModelBinder Members

    public object BindModel(ControllerContext controllerContext,
                            ModelBindingContext bindingContext)
    {
        // get the Cart from the session 
        var customProfile = (CustomProfile) controllerContext.HttpContext.Session[sessionKey];
        // create the Cart if there wasn't one in the session data
        if (customProfile == null)
        {
            customProfile = new CustomProfile("default name");
            controllerContext.HttpContext.Session[sessionKey] = customProfile;
        }
        // return the cart
        return customProfile;
    }

    #endregion
}

Hope this explains my issue, I'm sorry if its a rather long winded question!

Thanks for any assistance


回答1:


In this case it seems that the parameter you need to create (CustomProfile) must be taken from the session. You could then use a specific model binder for the Company model that derives from the default model binder, changing only the way it creates an instance of the Company class (it will then populate the properties in the same way as the default one):

public class CompanyModelBinder: DefaultModelBinder
{
    private const string sessionKey = "CustomProfile";

    protected override object CreateModel(ControllerContext controllerContext,
                                         ModelBindingContext bindingContext,
                                         Type modelType)
    {
        if(modelType == typeOf(Company))
        {
            var customProfile = (CustomProfile) controllerContext.HttpContext.Session[sessionKey];
            // create the Cart if there wasn't one in the session data
            if (customProfile == null)
            {
                customProfile = new CustomProfile("default name");
                controllerContext.HttpContext.Session[sessionKey] = customProfile;
            }

            return new Company(customProfile);
        }
        else
        {
            //just in case this gets registered for any other type
            return base.CreateModel(controllerContext, bindingContext, modelType)
        }
    }
}

You will register this binder only for the Company type by adding this to the global.asax Application_Start method:

ModelBinders.Binders.Add(typeOf(Company), CompanyModelBinder);

Another option could be to create a dependency-aware model binder using the Ninject dependencies by inheriting from the DefaultModelBinder (As you are using Ninject, it knows how to build instances of concrete types without the need of registering them). However you would need to configure a custom method that builds the CustomProfile in Ninject, which I believe you could do using the ToMethod(). For this you would extract you would extract your configuration of your Ninject kernel outside the controller factory:

public static class NinjectBootStrapper{
    public static IKernel GetKernel()
    {
        IKernel  ninjectKernel = new StandardKernel();
        AddBindings(ninjectKernel);
    }

    private void AddBindings(IKernel ninjectKernel)
    {
        ninjectKernel.Bind<IAuthProvider>().To<FormsAuthProvider>();
        ninjectKernel.Bind<IMembershipProvider>().To<MembershipProvider>();
        ninjectKernel.Bind<ICustomProfileProvider>().To<CustomProfileProvider>();
        ninjectKernel.Bind<ICompanyProvider>().To<CompanyProvider>();
        ninjectKernel.Bind<CustomProfile>().ToMethod(context => /*try to get here the current session and the custom profile, or build a new instance */ );
    }
}

public class NinjectControllerFactory : DefaultControllerFactory
{
    private readonly IKernel ninjectKernel;

    public NinjectControllerFactory(IKernel kernel)
    {
        ninjectKernel = kernel;
    }

    protected override IController GetControllerInstance(RequestContext requestContext,
                                                         Type controllerType)
    {
        return controllerType == null
                   ? null
                   : (IController) ninjectKernel.Get(controllerType);
    }
}

In that case you would create this model binder:

public class NinjectModelBinder: DefaultModelBinder
{
    private readonly IKernel ninjectKernel;

    public NinjectModelBinder(IKernel kernel)
    {
        ninjectKernel = kernel;
    }

    protected override object CreateModel(ControllerContext controllerContext,
                                         ModelBindingContext bindingContext,
                                         Type modelType)
    {
        return ninjectKernel.Get(modelType) ?? base.CreateModel(controllerContext, bindingContext, modelType)
    }
}

And you would update the global.asax as:

IKernel kernel = NinjectBootStrapper.GetKernel();
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory(kernel));
ModelBinders.Binders.DefaultBinder = new NinjectModelBinder(kernel);


来源:https://stackoverflow.com/questions/13534524/making-model-binding-work-for-a-model-with-no-default-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!