ASP.NET Web Forms Extensibility: Model Binding Value Providers
ASP.NET 4.5 introduced model binding: basically, it is a way for databound controls - Repeater, GridView, ListView, etc - to be fed, not from a datasource control - ObjectDataSource, EntityDataSource, SqlDataSource, etc -, but from a method in the page. This method needs to return a collection, and may have parameters. The problem is: how these parameters get their values? The answer is: through a model binding value provider.
A model binding value provider is a class that implements IValueProvider, and normally is injected through a ValueProviderSourceAttribute-derived attribute. ASP.NET includes some implementations:
-
ControlAttribute: gets a value from a control;
-
CookieAttribute: gets a value from a cookie;
-
FormAttribute: gets a value from a submitted form field;
-
QueryStringAttribute: gets a value from the query string;
-
RouteDataAttribute: gets a value from the current route;
-
SessionAttribute: gets a value from the session;
- UserProfileAttribute: gets a value from the user's profile;
-
ViewStateAttribute: gets a value from a control's view state.
If we want, say, to return a value from the Common Service Locator, it's pretty easy. First, an attribute:
[Serializable]
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class ServiceLocatorAttribute : ValueProviderSourceAttribute
{
private readonly Type serviceType;
private readonly String key;
public ServiceLocatorAttribute(Type serviceType, String key = null)
{
this.serviceType = serviceType;
this.key = key;
}
public override IValueProvider GetValueProvider(ModelBindingExecutionContext modelBindingExecutionContext)
{
return new ServiceLocatorValueProvider(this.serviceType, this.key);
}
}
And now the actual value provider:
public sealed class ServiceLocatorValueProvider : IValueProvider
{
private readonly Type serviceType;
private readonly String key;
public ServiceLocatorValueProvider(Type serviceType, String key)
{
this.serviceType = serviceType;
this.key = key;
}
public Boolean ContainsPrefix(String prefix)
{
return true;
}
public ValueProviderResult GetValue(String key)
{
return new ValueProviderResult(ServiceLocator.Current.GetInstance(this.serviceType, this.key), null, CultureInfo.CurrentCulture);
}
}
You can even have the ServiceLocatorAttribute implement the IValueProvider interface, I just separated it because conceptually they are different things.
Finally, here's how we would use it:
public IQueryable<SomeEntity> GetItems([ServiceLocator(typeof(MyComponent), "SomeKey")] MyComponent cmp)
{
//do something
}
Pretty sleek, don't you think?