Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save bennage/474263 to your computer and use it in GitHub Desktop.
Save bennage/474263 to your computer and use it in GitHub Desktop.
open System
open System.ComponentModel
open System.Web.Mvc
open Microsoft.FSharp.Reflection
// inspired by http://www.atrevido.net/blog/CommentView,guid,d5591bdc-add1-4fbc-b1d9-0c25359433a6.aspx
type MyModelBinder() =
let rec makeDefaultRecord ty =
let defval ty =
if FSharpType.IsRecord ty then makeDefaultRecord ty
else
match ty.GetConstructor(Type.EmptyTypes) with
null -> null
| c -> c.Invoke null
let vals = FSharpType.GetRecordFields ty |> Array.map (fun x -> defval x.PropertyType)
FSharpValue.MakeRecord(ty, vals)
let recordPropteries(bc:ModelBindingContext) =
ComponentModel.TypeDescriptor.GetProperties(bc.ModelType)
|> Seq.cast<ComponentModel.PropertyDescriptor> // BCLFail
|> Seq.filter(fun p -> bc.PropertyFilter.Invoke(p.Name))
let propertyValue(cc,bc,pd, binder:IModelBinder) =
let o = binder.BindModel(cc,bc)
if bc.ModelMetadata.ConvertEmptyStringToNull && Object.Equals(o,String.Empty) then
null
else
o
let set(bc:ModelBindingContext, pd:PropertyDescriptor, value) =
let flags = Reflection.BindingFlags.Instance ||| Reflection.BindingFlags.NonPublic
let field = bc.ModelType.GetField(pd.Name + "@", flags)
field.SetValue (bc.Model, value)
let bind(cc, bc:ModelBindingContext, pd:PropertyDescriptor) =
let context = new ModelBindingContext()
context.ModelMetadata <- bc.PropertyMetadata.[pd.Name]
context.ModelName <- pd.Name
context.ModelState <- bc.ModelState
context.ValueProvider <- bc.ValueProvider
let binder = ModelBinders.Binders.GetBinder(pd.PropertyType)
let value = propertyValue(cc,context,pd,binder)
set(bc,pd,value)
interface IModelBinder with
member x.BindModel(cc,bc) =
let t = bc.ModelType
if not(FSharpType.IsRecord t) then raise (InvalidOperationException("oops, not a record"))
let model = makeDefaultRecord(t)
let acc = Func<obj>( fun()-> model )
bc.ModelMetadata <- ModelMetadataProviders.Current.GetMetadataForType( acc , t)
for prop in recordPropteries(bc) do
bind(cc,bc,prop)
bc.Model
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment