Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>OK, let's try <a href="http://www.viddler.com/explore/mvcconf/videos/1/">putting this controller on a diet</a>.</p> <p>We start by fixing your repository and leaving it return null instead of some default post with ID = 0 which was kind of weird as you noticed:</p> <pre><code>public Post FindPost(int id, bool filterByPublished = false) { var query = db.Posts.Where(post =&gt; post.PostID == id); if (filterByPublished) query = query.Where(post =&gt; post.IsPublished == filterByPublished); return query.Select(post =&gt; post).SingleOrDefault(); } </code></pre> <p>then we could write a custom model binder for the <code>Post</code> model:</p> <pre><code>public class PostModelBinder : IModelBinder { private readonly IPostsRepository _repository; public PostModelBinder(IPostsRepository repository) { _repository = repository; } public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var postIdValue = controllerContext.Controller.ValueProvider.GetValue("postid"); int postId; if (postIdValue == null || !int.TryParse(postIdValue.AttemptedValue, out postId)) { return null; } return _repository.FindPost(postId, true); } } </code></pre> <p>which could be associated with the <code>Post</code> type in <code>Application_Start</code>:</p> <pre><code>var postsRepository = DependencyResolver.Current.GetService&lt;IPostsRepository&gt;(); ModelBinders.Binders.Add(typeof(Post), new PostModelBinder(postsRepository)); </code></pre> <p>and then a custom action filter that will take care to short-circuit the action in case the post is null or the post slug is different than the one passed as parameter:</p> <pre><code>public class PostActionFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { var postParameter = filterContext .ActionDescriptor .GetParameters() .Where(p =&gt; p.ParameterType == typeof(Post)) .FirstOrDefault(); if (postParameter == null) { return; } var post = (Post)filterContext.ActionParameters[postParameter.ParameterName]; if (post == null) { filterContext.Result = new RedirectResult(Url.Home()); } var slug = filterContext.Controller.ValueProvider.GetValue("slug"); if (slug != null &amp;&amp; post.Slug != slug.AttemptedValue) { filterContext.Result = new RedirectResult( Url.ShowPost(post.PostID, post.Slug), true ); } } } </code></pre> <p>If you are using ASP.NET MVC 3 this custom attribute could be registered as a global one in <code>Global.asax</code>:</p> <pre><code>public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new PostActionFilterAttribute()); ... } </code></pre> <p>and finally your controller action will become:</p> <pre><code>public ActionResult ShowPost(Post post) { postRepository.PostVisited(post); var viewModel = Mapper.Map&lt;Post, PostViewModel&gt;(post); return View(viewModel); } </code></pre> <p>or even bringing this a step further and introducing a custom mapper action filter:</p> <pre><code>[AutoMap(typeof(Post), typeof(PsotViewModel))] public ActionResult ShowPost(Post post) { postRepository.PostVisited(post); return View(post); } </code></pre> <p>The <code>AutoMap</code> action filter is pretty easy to implement. You would subscribe to the <code>OnActionExecuted</code> method and check whether the returned result from the controller action was a <code>ViewResultBase</code> and then extract the model from it, pass it to AutoMapper given the 2 types and substitute the model with the view model.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload