Model binding XML in Orchard Module

Topics: Customizing Orchard, General, Writing modules
Jul 9, 2011 at 1:47 PM
Edited Jul 12, 2011 at 8:59 AM

I would like to implement XML Model binding in my Module's controller.

I have implemented pretty much as per article in http://lostechies.com/jimmybogard/2011/06/24/model-binding-xml-in-asp-net-mvc-3/ but have however run into an issue as the ModelBinderProvider needs to be initialized in the Global.asax file for this to work. Is there another way to wire this up in the module?

protected void Application_Start()
{
ModelBinderProviders.BinderProviders
.Add(new XmlModelBinderProvider());
}

 

Thanking you in anticipation.

Jul 13, 2011 at 7:10 AM

Hi all, anybody with any ideas how this can be accomplished?

Dec 2, 2011 at 9:03 PM

I can also second the request. Is it possible to register a custom model binder in Orchard?

Coordinator
Dec 2, 2011 at 9:17 PM

Sure, it's MVC. Just don't do it in Application_Start. I've done it lazily around the site where I needed it, once, for example (and once it's added, it sticks, of course).

Dec 8, 2011 at 8:03 PM

Betrand if you want to share you how did this lazily I would appreciate it. I do have one solution I will share, in your custom module create a class that implements IOrchardShellEvents like so:

    public class RegisterHubspotModelBinder : IOrchardShellEvents
    {
        public void Activated()
        {
            ModelBinderProviders.BinderProviders.Add(new MyBinderProvider());
        }

        public void Terminating()
        {
            
        }
    }

Behind the scenes when Orchard spins up its Shell it will automatically execute this code for you.

Coordinator
Dec 8, 2011 at 10:09 PM

Sure, I just did this in my controller's constructor... It might be super wrong but it worked:

           if (!Binders.ContainsKey(typeof(DateTime))) {
                Binders.Add(typeof(DateTime), new DateAndTimeModelBinder {
                    Day = "day",
                    Month = "month",
                    Year = "year"
                });
            }

Dec 8, 2011 at 10:21 PM

 

http://orchard.codeplex.com/discussions/278838

Dec 27, 2011 at 4:35 AM
Edited May 2, 2012 at 5:34 PM

I have done it like this (using version 1.3.9), basically treating custom model binders in Orchard very similarly to Routes: 

1. /MVC/ShoesCustomModelBinder.cs

 

using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.Mvc.Routes;
using ShoeDepot.Models; 

namespace ShoeDepot.MVC
{
    public class ShoeTypesCustomModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (controllerContext.RouteData.Values.ContainsKey("shoeType"))
            {
                return ShoeTypes.GetInstance(controllerContext.RouteData.Values["shoeType"].ToString());
            }

            return ShoeTypes.Default;
        }
    }
}

 

 

 

2. /ModelBinders.cs

 

using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.Mvc.Routes;

namespace ShoeDepot
{
    public class ModelBinders : IModelBinderProvider
    {
        IEnumerable<ModelBinderDescriptor> IModelBinderProvider.GetModelBinders()
        {
            return new[] {
                new ModelBinderDescriptor{
                    Type = typeof(ShoeTypes)
                    , ModelBinder = new ShoeTypesCustomModelBinder()
                }
            };
        }

    }
}

 

3. /Controllers/ShoeController.cs

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Transactions;
using Orchard.Themes;

namespace ShoeDepot.Controllers
{
    [Themed]
    public class ShoeController : Controller
    {
        public ActionResult Index(int shoeId, ShoeType shoeType)
        {
                ...
                return View("Index", m); 
        }
}

 

 

4. The routes look like this (in /Routes.cs): 

 

using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.Mvc.Routes;

namespace ShoeDepot
{
    public class Routes : IRouteProvider
    {
        public const int DEFAULT_ROUTE_PRIORITY = 15;
        private readonly IRouteConstraint _shoeProfile_PageTypeConstraint = new FromValuesListConstraint(
		ShoeTypes.Sandals.ToString()
		, ShoeTypes.DressShoes.ToString()
		, ShoeTypes.TennisShoes.ToString()
	);

        public void GetRoutes(ICollection<RouteDescriptor> routes)
        {
            foreach (var routeDescriptor in GetRoutes())
                routes.Add(routeDescriptor);
        }


        public IEnumerable<RouteDescriptor> GetRoutes()
        {
            return new[] {
                new RouteDescriptor {
                    Name = "Shoe-Canonical"
                    , Priority = DEFAULT_ROUTE_PRIORITY + 5
                    , Route = new Route(
                        "shoes/{shoeId}/{shoeType}" // URL with parameters
                        , new RouteValueDictionary {
                            {"area", "shoedepot"}
                            , {"controller", "Shoe"}
                            , {"action", "Index"}
                            , {"shoeType", UrlParameter.Optional}
                        } // Parameter defaults
                        , new RouteValueDictionary{
                            {"shoeType", _shoeProfile_PageTypeConstraint}
                        } // constraints: valid values for a url param
                        , new RouteValueDictionary{{"area", "shoedepot"}} // dataTokens:values passed to handler, but not used to determine 
                        , new MvcRouteHandler()
                    )
                }
            };
            
        }
    }    
}

 


 

 

 

I've converted my actual code into a contrived scenario about shoes so there might be some minor typos. ShoeTypes.cs defines a class that implements the type-safe-enum design pattern. 

 

If you have a custom model binder, or have created them before in asp.net MVC apps, all of the above is already familiar to you. The only new part is step #2. In that step, by implementing IModelBinderProvider, you are declaring the the mappings between Model types and the respective model binders you want to use for those types. Orchard picks them up the same way it picks up the Routes in Routes.cs.