Bookmark and Share BitCoin Donate: 13t8gAWVpHP2ddxMp88d1NFpZjnFJC6UwK

Dictionary model binder for MVC 3

This code was written by the guy at Siphon 9 and it's awesome, it is a bit broken though so be aware. It seems like a full bind and validate happens on each subproperty's bind which causes all your validators to fire multiple times. The original code can be found on github at Dictionary binder original source

Have fun

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.ComponentModel;

using log4net;

namespace XYZ.Core.Mvc{
	///
	/// This binder allows us to bind to Dictionaries for XYZ Views
	/// SOURCE: https://github.com/loune/MVCStuff/blob/master/Extensions/DefaultDictionaryBinder.cs	
	/// ASP.NET MVC Default Dictionary Binder
	///
	public class DefaultDictionaryBinder : DefaultModelBinder
	{
		private static ILog log = LogManager.GetLogger("DefaultDictionaryBinder");

		IModelBinder nextBinder;

		///
		/// Create an instance of DefaultDictionaryBinder.
		///
		public DefaultDictionaryBinder() : this(null)
		{
		}

		///
		/// Create an instance of DefaultDictionaryBinder.
		///
		///The next model binder to chain call. If null, by default, the DefaultModelBinder is called.
		public DefaultDictionaryBinder(IModelBinder nextBinder)
		{
			this.nextBinder = nextBinder;
		}

		private IEnumerable GetValueProviderKeys(ControllerContext context)
		{
//#if !ASPNETMVC1
			List keys = new List();
			keys.AddRange(context.HttpContext.Request.Form.Keys.Cast());
			keys.AddRange(((IDictionary)context.RouteData.Values).Keys.Cast());
			keys.AddRange(context.HttpContext.Request.QueryString.Keys.Cast());
			keys.AddRange(context.HttpContext.Request.Files.Keys.Cast());
			return keys;
//#else
//			return bindingContext.ValueProvider.Keys;
//#endif
		}

		private object ConvertType(string stringValue, Type type)
		{
			return TypeDescriptor.GetConverter(type).ConvertFrom(stringValue);
		}

		public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
		{
			//if (bindingContext == null) throw new ArgumentNullException("bindingContext");

			Type modelType = bindingContext.ModelType;
			Type idictType = modelType.GetInterface("System.Collections.Generic.IDictionary`2");

			if (idictType != null)
			{
				object result = null;
				System.Collections.Hashtable alreadyBindedHash = new System.Collections.Hashtable();

				Type ga = idictType.GetGenericArguments();
				IModelBinder valueBinder = Binders.GetBinder(ga1);
				log.Debug("ENTER::DICT::BIND");
				foreach (string key in GetValueProviderKeys(controllerContext))
				{
					if (key.StartsWith(bindingContext.ModelName + "", StringComparison.InvariantCultureIgnoreCase))
					{
						int endbracket = key.IndexOf("", bindingContext.ModelName.Length + 1);
						if (endbracket == -1)
							continue;

						object dictKey;
						try
						{
							dictKey = ConvertType(key.Substring(bindingContext.ModelName.Length + 1, endbracket - bindingContext.ModelName.Length - 1), ga0);
						}
						catch (NotSupportedException)
						{
							continue;
						}

						ModelBindingContext innerBindingContext = new ModelBindingContext()
						{
//#if ASPNETMVC1
//							Model = null,
//							ModelType = ga1,
//#else
							ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => null, ga1),
//#endif
							ModelName = key.Substring(0, endbracket + 1),
							ModelState = bindingContext.ModelState,
							PropertyFilter = bindingContext.PropertyFilter,
							ValueProvider = bindingContext.ValueProvider
						};

						log.Debug("BINDMODEL::" + modelType.AsString());
						
						if (!alreadyBindedHash.ContainsKey(modelType.AsString()))
						{
							alreadyBindedHash.Add(modelType.AsString(), true);
							object newPropertyValue = valueBinder.BindModel(controllerContext, innerBindingContext);

							if (result == null)
							{
								log.Debug("CREATEMODEL::" + modelType.AsString());
								result = CreateModel(controllerContext, bindingContext, modelType);
							}

							if (!(bool)idictType.GetMethod("ContainsKey").Invoke(result, new object { dictKey }))
								idictType.GetProperty("Item").SetValue(result, newPropertyValue, new object { dictKey });
						}
					}
				}
				log.Debug("EXIT::DICT::BIND");

				return result;
			}

			if (nextBinder != null)
			{
				return nextBinder.BindModel(controllerContext, bindingContext);
			}

			return base.BindModel(controllerContext, bindingContext);
		}
	}

}