Code Contracts with new MVC 3 ViewBag - asp.net-mvc-3

public class HomeController : Controller
{
public ActionResult Index()
{
// Warning 19 CodeContracts: Possibly calling a method on a null reference 'Website.Controllers.HomeController.<Index>o__SiteContainer0.<>p__Site2.Target' HomeController.cs
if (ViewBag != null)
{
ViewBag.Message = "Be Immortal";
}
return View();
}
public ActionResult About()
{
return View();
}
}
Wracking my brain trying to figure out how to satisfy [X] Implicit Non-Null Obligations for the ViewBag in MVC 3. Has anyone come up with a way to make code contracts jive with the new dynamic ViewBag type?
I'd preferably like to be able to wrap the ViewBag in a base controller as ViewBagSafe etc.
I do realize this is not really a problem with the project since ViewBag will never be null but I would like to leave code-contracts on with default null-checking for future slip-ups on my part (and still be able to compile without warnings so that I can easily identify my own contract-breaking coding).

I tracked down the problem. It has to do with the initialization logic of dynamic member lookups and the static caching fields emitted by the C# compiler. I had to teach cccheck about these and add some contracts to the caching classes in System.Core.dll. The next release should no longer issue these warnings on dynamically access members. Thanks for bringing it up.

Related

View and controller in asp.net mvc3 - controller should match a view one to one?

I have a very typical situation in any application, where i have the following functionality:
create new record
edit existing record
so other irrelevant actions
IMO, creating and editing should be served by the same view, but different actions. But it appears that I have to have the action name match the view name....would you use partial views for this? I would rather not complicate this scenario - which is very simple and appears in virtually every web app.
Action can return a view with a diferent name this way:
public ActionResult OneName()
{
return View("OtherName");
}
If you don't specify the view name (View("") then the view will be the view with the action name
Partial views are an excellent answer. I'd suggest you look at how the MvcScaffold NuGet package does it. See here or get the package in Visual Studio.
I'd simply use the same action altogether and use the ID to determine if this is a new record or updating an existing one:
/Forum/Post/Edit/0 create a new record
/Forum/Post/Edit/10457 update a record with ID 10457
However, since you insist on using different actions, why not simply create 2 actions, both returning the same view?
public class PostController : Controller
{
public ActionResult Create(Post post)
{
// work your magic...
return View("Edit", post);
}
public ActionResult Update(Post post)
{
// work your magic...
return View("Edit", post);
}
}
If this doesn't work in your scenario, you're pretty much left with partial views.

Weird MVC Issue

I have this code and I can't understand why it works this way
I have a model and view which is arbitrary and a very simple (but weird) controller
Here is my controller:
public partial class RouteController : Controller
{
[HttpGet]
public virtual ActionResult Create()
{
Create create = new Create();
return View("Create", create);
}
[HttpPost]
public virtual ActionResult Create(Create route)
{
return Create();
}
}
The first create method loads the view as normal. When the view posts back it runs the 2nd action which runs the first (as expected). The wierd part is the view is (re-)loaded with my previously entered data with errors (if any). I dont understand this because my model is empty. I was expecting it to post back with the same form as if it was loaded for the first time but with errors possibly.
Please explain.
That's the normal behavior of HTML helpers and it is by design. They first look at values contained in the ModelState and after that in the actual model. If you intend to modify some values on the model in a POST action you need to remove them from modelstate first:
For example:
[HttpPost]
public virtual ActionResult Create(Create route)
{
ModelState.Remove("SomeProperty");
route.SomeProperty = "some new value";
return View(route);
}
If you intend to completely modify everything as in your example you could clear the modelstate entirely:
[HttpPost]
public virtual ActionResult Create(Create route)
{
ModelState.Clear();
return Create();
}
Another possibility is to write your own TextBoxFor, HiddenFor, CheckBoxFor, ... helpers that will use the value in the model and not the one in the model state. Or yet another (non-recommended) possibility:
<input type="text" name="SomeProperty" value="#Model.SomeProperty" />
Of course in this case client validation among other things provided by the standard helpers won't work.

T4MVC not generating some Actions for one Controller

I have a situation where T4MVC is generating everything properly (meaning intellisense shows all areas/controllers/actions and everything compiles), but when I run the code, I get a T4MVC was called incorrectly runtime error.
I've investigated the generated files and discovered that for one controller in my project, only actions in the base class are getting the overridden stub actions generated. For other controllers, all actions are being generated. They all have the same set up, described below.
I have a BaseController class that has some shared code (and inherits from Controller). In the Controllers directory (root of project) I have a number of controllers, all which inherit from BaseController.
I then have several Areas. In each Area, I have the same controllers, each inheriting from the controller of the same name in the root Controllers directory.
Running T4MVC (version 2.6.54), everything works fine except for one controller. The odd thing is that intellisense works for the controller, but chokes when the actual action is referenced (in an ActionLink() call).
I manually added one action in particular into the generated code and there was no error.
So my question is, what would cause T4MVC to not generate all code for a controller? The missing actions are all public virtual ActionResult and the actions themselves work fine. The problem controller has the same issue in all Areas.
Some abbreviated code.
/Controllers/BaseController.cs
namespace MyProject.Controllers
{
public abstract partial class BaseController : Controller
{
protected ISession session;
public BaseController()
{
}
// other shared methods/actions
}
}
/Controllers/ActivitiesController.cs (this is the problem controller)
namespace MyProject.Controllers
{
public partial class ActivitiesController : BaseController
{
// for resolving concurrency exceptions
private Activity userValues;
private Activity databaseValues;
public ActivitiesController() : base()
{
ViewBag.ControllerName = "Activities";
}
// this action is causing the problem used like
<li>#Html.ActionLink("Activities", MVC.Areas.Module1.Activities.Index())</li> in a view
public virtual ActionResult Index()
{
return View();
}
}
}
/Areas/Module1/Controllers/ActivitiesController.cs. This is the whole class
namespace MyProject.Areas.Module1.Controllers
{
public partial class ActivitiesController : MyProject.Controllers.ActivitiesController
{
public ActivitiesController() : base()
{
base.currentModule = Modules.Module1;
}
}
}
In case anyone else comes across this I had a similar issue and resulting run-time error message but in a bit different scenario. It was in the RedirectToAction statement at end of a ActionResult method:
RedirectToAction(Edit(id));
The error went away after correcting it to:
RedirectToAction(MVC.[action name].Edit(id));
The error message isn't very intuitive and the suggestion to re-run the custom tool doesn't really help.
Did you make sure to re-run T4MVC to generate based on the latest (Right click .tt file / run custom tool)?
If that's not the problem, I may need to look at a sample app that has the problem to see what's going on.

MVC3 Routing Issues - How to re-use a View for all Controller Methods?

I'm trying to implement a common controller in MVC3 to return various JSON feeds, example -
public class AjaxController : Controller
{
public ActionResult Feed1()
{
ViewBag.Json = LogicFacade.GetFeed1Json();
return View();
}
public ActionResult Feed2()
{
ViewBag.Json = LogicFacade.GetFeed2Json();
return View();
}
}
This class has 30+ methods in it, the problem is this requires implementing an IDENTICAL View for each of the Controller's methods (sigh) that writes out ViewBag.Json.
I'm assuming this is a routing issue but I'm struggling with that. The following didn't work -
Tried setting ViewBag.Json then using RedirectToAction() but that seems to reset ViewBag.Json.
Note JsonResult is not appropriate for my needs, I'm using a different JSON serialiser.
So the objective here is to maintain one View file but keep this class with seperate methods that are called by routing, and not a crappy switch statement implementation.
Any help appreciated.
Use the same view and just specify the name. You can store in the controller's view folder, if only used by one controller, or in the Shared view folder if used by more than one.
return View("SharedJsonView");
Another, perhaps better, solution would be to create your own result -- maybe deriving from JsonResult, maybe directly from ActionResult -- that creates the JSON response that you need. Look at the source code for JsonResult on http://www.codeplex.com/aspnet for ideas on how to do it.

MVC Routes based on POST parameters

We have an a PHP application that we are converting to MVC. The goal is to have the application remain identical in terms of URLs and HTML (SEO and the like + PHP site is still being worked on). We have a booking process made of 3 views and in the current PHP site, all these view post back to the same URL, sending a hidden field to differentiate which page/step in the booking process is being sent back (data between pages is stored in state as the query is built up).
To replicate this in MVC, we could have a single action method that all 3 pages post to, with a single binder that only populates a portion of the model depending on which page it was posted from, and the controller looks at the model and decides what stage is next in the booking process. Or if this is possible (and this is my question), set up a route that can read the POST parameters and based on the values of the POST parameters, route to a differen action method.
As far as i understand there is no support for this in MVC routing as it stands (but i would love to be wrong on this), so where would i need to look at extending MVC in order to support this? (i think multiple action methods is cleaner somehow).
Your help would be much appreciated.
I have come upon two solutions, one devised by someone I work with and then another more elegant solution by me!
The first solution was to specify a class that extends MVcRouteHandler for the specified route. This route handler could examine the route in Form of the HttpContext, read the Form data and then update the RouteData in the RequestContext.
MapRoute(routes,
"Book",
"{locale}/book",
new { controller = "Reservation", action = "Index" }).RouteHandler = new ReservationRouteHandler();
The ReservationRouteHandler looks like this:
public class ReservationRouteHandler: MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var request = requestContext.HttpContext.Request;
// First attempt to match one of the posted tab types
var action = ReservationNavigationHandler.GetActionFromPostData(request);
requestContext.RouteData.Values["action"] = action.ActionName;
requestContext.RouteData.Values["viewStage"] = action.ViewStage;
return base.GetHttpHandler(requestContext);
}
The NavigationHandler actually does the job of looking in the form data but you get the idea.
This solution works, however, it feels a bit clunky and from looking at the controller class you would never know this was happening and wouldn't realise why en-gb/book would point to different methods, not to mention that this doesn't really feel that reusable.
A better solution is to have overloaded methods on the controller i.e. they are all called book in this case and then define your own custome ActionMethodSelectorAttribute. This is what the HttpPost Attribute derives from.
public class FormPostFilterAttribute : ActionMethodSelectorAttribute
{
private readonly string _elementId;
private readonly string _requiredValue;
public FormPostFilterAttribute(string elementId, string requiredValue)
{
_elementId = elementId;
_requiredValue = requiredValue;
}
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
{
if (string.IsNullOrEmpty(controllerContext.HttpContext.Request.Form[_elementId]))
{
return false;
}
if (controllerContext.HttpContext.Request.Form[_elementId] != _requiredValue)
{
return false;
}
return true;
}
}
MVC calls this class when it tries to resolve the correct action method on a controller given a URL. We then declare the action methods as follows:
public ActionResult Book(HotelSummaryPostData hotelSummary)
{
return View("CustomerDetails");
}
[FormFieldFilter("stepID", "1")]
public ActionResult Book(YourDetailsPostData yourDetails, RequestedViewPostData requestedView)
{
return View(requestedView.RequestedView);
}
[FormFieldFilter("stepID", "2")]
public ActionResult Book(RoomDetailsPostData roomDetails, RequestedViewPostData requestedView)
{
return View(requestedView.RequestedView);
}
[HttpGet]
public ActionResult Book()
{
return View();
}
We have to define the hidden field stepID on the different pages so that when the forms on these pages post back to the common URL the SelectorAttributes correctly determines which action method to invoke. I was suprised that it correctly selects an action method when an identically named method exists with not attribute set, but also glad.
I haven't looked into whether you can stack these method selectors, i imagine that you can though which would make this a pretty damn cool feature in MVC.
I hope this answer is of some use to somebody other than me. :)

Resources