MVC Model Binder for CSLA

MVC Model Binder for CSLA

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


rasupit posted on Wednesday, September 24, 2008

During the announcement of asp.net MVC preview 5, ScottGu explains how MVC handles object binding. The Model Binders topic has caught my attention to investigate further if this could be applied to CSLA objects.

There are examples that caught my attention where you could use a model as the parameter of controller action. With this option, the controller auto magically instantiate the object through Model Binder interface. The controller will also "bind" the form back to the object if a post action. This scenario allows you to have very minimal code in the action method:

[AcceptVerbs("POST")]
public object Create(Product product) {
try {
NorthwindDataContext northwind = new NorthwindDataContext();
northwind.Products.InsertOnSubmit(product);
northwind.SubmitChanges();

return Redirect("/ProductAdded");
}
catch (Exception ex) {
return View(product);
}
}

Notice the code above taken from ScottGu example is very clean. There is no create Product, no value assignment or rule validation (the binders also takes care of this, I'll get to that later).  All of these are handle automatically by model binder.

Out of the box, the default model binders could not work with CSLA objects. The default binder only support simple entity objects. CSLA uses factory method pattern which is not easily created just by calling Activator.CreateInstance(typeof(Product)).

However asp.net MVC provide interfaces where you can create custom binders through IModelBinder which you can use to define model binders at the controller level or as your default binder. Other method is to create custom binding attribute through CustomModelBinderAttribute which can be used to declare as parameter attribute.

I will start with the parameter attribute approach since this is more straight forward to implement.  The idea is to be able to tell model binder how to instantiate CSLA object.  For example we can annotate the Project parameter to use GetProject method by using Id form value as argument:

[AcceptVerbs("POST")]
public ActionResult Edit([CslaBind(FactoryMethod="GetProject", FactoryArguments="Id")] Project project)
{
if (!project.IsValid)
return View(project);

try
{
project.Save();
return RedirectToAction("Index");
}
catch (Exception)
{
TempData["message"] = "oops,problem...";
return View(project);
}
}

CslaBindAttribute implements an abstract CustomeModelBinderAttribute.  It overrides GetBinder method which return custom model binder that knows how to call factory method based on FactoryMethod property and knows how to get value from given argument in FactoryArguments. On the above example, this custom binder would get Id value and call GetProject method to instantiate the business object. This binder also updates the object with values changed by user (from Request.Form).  If there is any broken rule, it updates ModelState with broken rule messages.  Note, according to ScottGu ("Good news - we are planning on supporting IDataErrorInfo with the next MVC update (which will be the beta).") future release will support IDataErrorInfo therefore CSLA broken rules may be displayed automatically.  To simplify the implementation, CslaBindAttribute also implement IModelBinder which would function as custom model binder.

I have created a prove of concept that implement the above use case.  I will do some clean up first then share the code on my next post.

NEXT: I will write about the second approach which is implementing CslaModelBinder. This is a custom CSLA model binder which can be defined at controller level or application level.

Comments and thoughts are welcome,

Ricky Supit

EDIT:

Click here to read the second part of this post. 

Click here to dowload the source code.

nermin replied on Wednesday, September 24, 2008

Thanks Ricky,

This truly looks like an interesting solution. And btw actually this Binder concept existed for couple of years and can be found in first ASP.NET MVC Framework - Monorail.


Nermin
________________________________

From: rasupit [mailto:cslanet@lhotka.net]
Sent: Wed 9/24/2008 9:21 PM
To: Nermin Dibek
Subject: [CSLA .NET] MVC Model Binder for CSLA



During the announcement of asp.net MVC preview 5 , ScottGu explains how MVC handles object binding. The Model Binders topic has caught my attention to investigate further if this could be applied to CSLA objects.

There are examples that caught my attention where you could use a model as the parameter of controller action. With this option, the controller auto magically instantiate the object through Model Binder interface. The controller will also "bind" the form back to the object if a post action. This scenario allows you to have very minimal code in the action method:

[AcceptVerbs("POST")]
public object Create(Product product) {
try {
NorthwindDataContext northwind = new NorthwindDataContext();
northwind.Products.InsertOnSubmit(product);
northwind.SubmitChanges();

return Redirect("/ProductAdded");
}
catch (Exception ex) {
return View(product);
}
}


Notice the code above taken from ScottGu example is very clean. There is no create Product, no value assignment or rule validation (the binders also takes care of this, I'll get to that later). All of these are handle automatically by model binder.

Out of the box, the default model binders could not work with CSLA objects. The default binder only support simple entity objects. CSLA uses factory method pattern which is not easily created just by calling Activator.CreateInstance(typeof(Product)).

However asp.net MVC provide interfaces where you can create custom binders through IModelBinder which you can use to define model binders at the controller level or as your default binder. Other method is to create custom binding attribute through CustomModelBinderAttribute which can be used to declare as parameter attribute.

I will start with the parameter attribute approach since this is more straight forward to implement. The idea is to be able to tell model binder how to instantiate CSLA object. For example we can annotate the Project parameter to use GetProject method by using Id form value as argument:

[AcceptVerbs("POST")]
public ActionResult Edit([CslaBind(FactoryMethod="GetProject", FactoryArguments="Id")] Project project)
{
if (!project.IsValid)
return View(project);

try
{
project.Save();
return RedirectToAction("Index");
}
catch (Exception)
{
TempData["message"] = "oops,problem...";
return View(project);
}
}


CslaBindAttribute implements an abstract CustomeModelBinderAttribute. It overrides GetBinder method which return custom model binder that knows how to call factory method based on FactoryMethod property and knows how to get value from given argument in FactoryArguments. On the above example, this custom binder would get Id value and call GetProject method to instantiate the business object. This binder also updates the object with values changed by user (from Request.Form). If there is any broken rule, it updates ModelState with broken rule messages. Note, according to ScottGu ("Good news - we are planning on supporting IDataErrorInfo with the next MVC update (which will be the beta).") future release will support IDataErrorInfo therefore CSLA broken rules may be displayed automatically. To simplify the implementation, CslaBindAttribute also implement IModelBinder which would function as custom model binder.

I have created a prove of concept that implement the above use case. I will do some clean up first then share the code on my next post.

NEXT: I will write about the second approach which is implementing CslaModelBinder. This is a custom CSLA model binder which can be defined at controller level or application level.

Comments and thoughts are welcome,

Ricky Supit



rasupit replied on Sunday, September 28, 2008

Nermin,
Thanks for the info I haven't use the Monorail so it would be interesting to know how they implement model binder MR.  I'd guess implementing CSLA still post a unique challenge even on MR because it's not as straight forward as ActiveRecord.

mamboer replied on Wednesday, April 08, 2009

Nice post.

Copyright (c) Marimer LLC