Noah Blumenthal's Blog

April 23, 2009

Advanced databinding: determining (sub) type at runtime

Filed under: DataBinding — noahblu @ 3:07 pm

This is something I haven’t seen anyone talk about:

I’m working on a “coupon” site — merchants post offers on the site for visitors to see.  There are a few different types of offers: Coupons, Rebates, Free Stuff, and Deals.  In terms of code, these all implement an IOffer interface (and they all are children of OfferBase).

The site has a single page for posting offers — the merchant chooses the offer type (Coupon, Rebate, Free Stuff, or Deal) and the form accomodates for the necessary information, swapping out certain input elements for others.

I wanted a way to post the form to a single Action and have a databinder determine the exact type at runtime.  And it was actually pretty easy.  I’m still working on some specific details, so I’ll let you know if there are any caveats, but as far as I can see this works pretty well.

Here’s what I did:

First I created an Action that takes an IOffer parameter

public ActionResult Create(IOffer offer)
{

}

Now the MVC framework is going to have to bind the posted data to an IOffer, so I created an IOfferModelBinder.

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
IOffer offer;
ValueProviderResult offerType;
bindingContext.ValueProvider.TryGetValue(“OfferType”, out offerType);
if (offerType == null)
return null;

// Instantiate correct offer type based on selection
string assemblyName = typeof(IOffer).Assembly.FullName;
string typeName = string.Concat(offerType.AttemptedValue, “Offer”);
string fullTypeName = “Picselle.BunchOfSavings.DomainModel.” + typeName;
var obj = Activator.CreateInstance(assemblyName, fullTypeName);
offer = obj.Unwrap() as IOffer;

// Bind everything else
bindingContext.Model = offer;
bindingContext.ModelType = offer.GetType();
var bindResult = ModelBinders.Binders.DefaultBinder.BindModel(controllerContext, bindingContext);

return offer;
}

I didn’t want to limit myself to the current offer types, so I figured that we’ll use a pretty standard naming convention to name our classes (this is how it’s done now)– [offer_type_name]Offer (e.g. CouponOffer or RebateOffer).  So the code uses the Activator to create an object of the type the merchant wants by adding “Offer” to the end of the posted deal type string (which is the deal type selected by the merchant).  Now I can add a BuyOneGetOneFreeOffer later and not have to change anything…

Pretty straightforward, and powerful…

Create a free website or blog at WordPress.com.