I have asp.net MVC web application. It is the list of some entities where a user could pick up some of them and views details and so on.
Most of the actions of this application have some Entity
as a parameter. For example:
[Authorize]
public ActionResult Details(Entity entity)
{
...
return View();
}
I keep this entity in session and fill this parameter in the model binder by id from URL. It looks like:
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var httpContext = controllerContext.HttpContext;
var values = httpContext.Request.RequestContext.RouteData.Values;
if (!values.ContainsKey("id")) return null;
var id = values["id"].ToString();
var obj = httpContext.Session["Entities"];
var list = (List<Entity>) obj;
return list?.SingleOrDefault(x => x.Id == id);
}
But time to time it is possible case that the user tries to access to action when session object is empty. For example, a user gets URL by email to action and omits code that puts Entity in session.
What is the best fix for this case?
1. Implement getting entity in the binder:
var id = values["id"].ToString();
var obj = httpContext.Session["Entities"];
var list = (List<Entity>) obj;
var entity = list?.SingleOrDefault(x => x.Id == id);
if(null==entity){
entity = repository.GetEntity(id);
//set entity in session
}
return entity;
This way looks nasty because of I have to implement extra filling logic in the binder. Also, I can't inject repository in the binder. But this way is simple and easy to implement.
2. Remove the binder and add some get method:
Remove binder at all and change all action with Entity in the next way:
[Authorize]
public ActionResult Details()
{
Entity entity = GetEntity();
...
return View();
}
private Entity GetEntity()
{
var id = ... //get id from http request.
var list = (List<Entity>)Session["Entities"];
var entity = list?.SingleOrDefault(x => x.Id == id);
if(null==entity){
entity = repository.GetEntity(id);
//set entity in session
}
return entity;
}
I can implement injection of repository now, the code is safe but looks ugly.
Which solution is best? Is it possible that I have missed any another implementation?
This way looks nasty because of I have to implement extra filling logic in the binder. Also, I can't inject repository in the binder.
You are correct that dependency injection cannot be properly done in a model binder. But you could use a factory:
public class MyModelBinder : DefaultModelBinder
{
private readonly Func<IRepository> repositoryProvider;
public MyModelBinder(Func<IRepository> repositoryProvider)
{
this.repositoryProvider = repositoryProvider;
}
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var id = ... //get id from http request.
var list = (List<Entity>)Session["Entities"];
var entity = list?.SingleOrDefault(x => x.Id == id);
if (null == entity)
{
IRepository repository = this.repositoryProvider();
entity = repository.GetEntity(id);
//set entity in session
}
return entity;
}
}
and then when registering your binder:
ModelBinders.Binders[typeof(Entity)] = new MyModelBinder(
() => DependencyResolver.Current.GetService<IRepository>()
);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.