问题
I have created a custom Action Filter and am binding it with Ninject's BindFilter method:
public class ExtendModelAttribute : FilterAttribute {}
public class ExtendModelFilter : IActionFilter
{
private IKernel kernel;
public ExtendModelFilter(Func<IKernel> kernel)
{
this.kernel = kernel;
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// TODO:
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
}
}
I am binding my filter like so:
kernel.BindFilter<ExtendModelFilter>(FilterScope.Action, 0).WhenActionMethodHas<ExtendModelAttribute>();
Everything so far is working great. What I want to accomplish is the following:
[ExtendModel]
public ActionResult Index()
{
return View(new IndexModel());
}
What I want the ExtendModel filter to do is determine what type of Model is being used and then find the correct dependency:
public interface IModelExtender<TModel> {
void Extend(TModel model);
}
public class IndexModelExtender : IModelExtender<IndexModel> {
public void Extend(IndexModel model)
{
// extend the model here with anything extra that is required
}
}
I'm not sure how to write the code in the action filter to get an instance of IModelExtender:
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// TODO:
// I Need to look at the filterContext Model to determine which
// type of IModelExtender<T> to create:
// For Example, when [ExtendModel] is applied on the Index() method above
// then I need it to resolve to IndexModelExtender
}
Maybe what I want to do isnt' even possible? Since I don't know at compile time what T is, is there any way to do what I want?
EDIT
Here is an example of what a ModelExtender might do:
public class IndexModelExtender : IModelExtender<IndexModel>
{
public IndexModelExtender(IFooRepository fooRepository, IBarRepository barRepository)
{
// ...
}
public void Extend(IndexModel model)
{
model.SelectList1 = new SelectList(fooRepository.GetFoos(), "Description", "Id");
model.SelectList2 = new SelectList(barRepository.GetBars(), "Description", "Id");
}
}
I want to avoid code like this in my controller:
public ActionResult Index()
{
var model = new IndexModel();
// populate select lists here
return View(model);
}
[HttpPost]
public ActionResult Index(IndexModel model)
{
if(!ModelState.IsValid ) {
// populate the same stuff here
}
}
回答1:
You will have to use reflection because the model type is known only at runtime and your extender is generic:
public class ExtendModelFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext filterContext)
{
object model = null;
if (filterContext.Result is ViewResultBase)
{
model = ((ViewResultBase)filterContext.Result).Model;
}
else if (filterContext.Result is JsonResult)
{
model = ((JsonResult)filterContext.Result).Data;
}
// TODO: you could continue with the else if here to take
// into account some other action results that have the notion of model
// like for example some custom action results that you might have written
if (model == null)
{
// we have no model => nothing to extend
return;
}
var extenderType = typeof(IModelExtender<>).MakeGenericType(model.GetType());
var extender = DependencyResolver.Current.GetService(extenderType);
var extend = extenderType.GetMethod("Extend");
extend.Invoke(extender, new[] { model });
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
}
}
You will also notice that I have refactored the custom action filter in order to use the current dependency resolver and make it NInject agnostic. You could of course keep the IKernel
dependency if you prefer.
来源:https://stackoverflow.com/questions/9381557/ninject-in-an-action-filter