Best asp.net-mvc questions in December 2011

Multiple developers using single web.config with different settings

9 votes

I'm creating an ASP.Net MVC web app. There are multiple developers on the team that need to have different settings in the web.config file. These settings are for the database connection and a local linux virtual machine that needs to be accessed. There are other things that we will need to add in the future. What is a methodology that can be used for each developer to have their own custom settings in the web.config without the fear of having their local settings being committed to source control?

The deployment configurations will not work because they are running the site through the local development VS web server. We will use the deployment configurations for deploying to different stages in our environment.

Most sections in the config XML file allow you to use a configSource attribute. We used this attribute to put commonly dev-modified sections into a separate file. Each of these commonly modified sections would have a seperate *.dev.config and *.prd.config files. The *.dev.config files were ignored by the source control (git).

The production deployment would then set the configSource attribute to use the *.prd.config files. Again this means you need to keep two sets of configuration up to date. I started working on a solution to keep the *.prd.config and *.dev.config files in sync at the element level, but just never got the time to finish it.

Dynamically Insert Pagebreak

9 votes

My team working in a project using asp.net mvc3(c#). Based on the project requirement,we need to implement page break like Microsoft Word. I need to save the pagebreak and the page size may be a4, letter, legal, etc.

Is it possible to control the page size of the content in the ckeditor and insert page breaks shown inside the editor when it crosses a certain height or size, the same way it works in MS Word>

Is there is any alternative solution?

Alternative solution I found after searching the google for solving pagebreak by using RichTextBoxSilverlight

some of the features

  • RichTextBox fully supports paging and printing. You can edit documents in either Print Layout or Draft view; much like Microsoft Word. Print Layout supports continuous page flow when scrolling and even supports facing multiple pages horizontally.

  • RichTextBox supports importing and exporting RTF, Html, and plain text. Load existing rich text or Html into the C1RichTextBox control, edit the document, and then export it back to RTF or Html.

  • Edit and format text containing multiple fonts, decorations, colors, tables, images, lists, and more.

  • RichTextBoxToolbar includes the following commands: Paste, Cut, Copy, Undo, Redo, Font Family, Font Size, Grow Font, Shrink Font, Bold, Italic, Underline, Change Case, Subscript, Superscript, Text Color, Text Highlight Color, Align Left, Align Center, Align Right, Justify, Bullets, Numbering, Text Wrapping, Border Thickness, Border Color, Paragraph Color, Margin, Padding, Insert Image, Insert Symbol, Insert Hyperlink, Remove Hyperlink, Find and Replace, Spell Check, and additional commands for inserting/editing Tables.

  • RichTextBox for inserting and editing images. Users can easily upload images from their computer to the editor or point to an image's url on the web. Users can also select, resize and drag images on the document surface.

  • RichTextBox supports page zooming in both print layout and draft views.

  • RichTextBox content can be exported to PDF format.

  • Edit data in the RichTextBox with confidence. You now have the ability to easily undo and redo your changes with the click of a button.

Is it possible to call the Razor Compiler Programmatically from a Controller method?

7 votes

I am using ASP .NET MVC 3 and I have an interesting problem to solve that I am hoping for some advice on.

I have a page that has a number of divs inside it. The contents of each div changes over time and so currently I have a timer for each div running that makes a $.ajax request to the server which returns a PartialViewResult with the updated contents of the div. The partial view is quite complex and references other views.

The problem with this approach is that it does not scale very well. It could be that each user has a lot of these timers running and with a lot of users the server is constantly being hit. I would rather, therefore, make a single request to the server that returns, potentially, multiple div contents so it would be:

div1 { some html }
div2 { some html }

...

Then on the client I could put each bit of HTML into the correct position on the page.

I thought that what I could do is return JSON from the server but my problem is - how do I get the HTML? At the moment the razor compiler will run and turn my partial view cshtml files into HTML but if I am returning JSON, is it possible to programmatically call the razor compiler?

I found Razor Engine here: http://razorengine.codeplex.com/ that seems to do what I want but is it possible to do it with just vanilla ASP NET MVC?

Or, given the problem, is there a better way that I could achieve my goal?

Thanks for any help!

Create an Action that returns a new PartialView that renders all of those PartialViews. e.g. an action:

public PartialViewResult AggregatedAction(args)
{
    return PartialView();
}

with a view that contains:

@Html.Action("IndividualAction1", null)
@Html.Action("IndividualAction2", null)
@Html.Action("IndividualAction3", null)

See http://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx for more details.

That way there is only one request and the rendering engine is being called from the right place, i.e. the view.

Then with the result, you can search for the various divs and replace the html in the client.

$('div#id1').html('div#id1',$(data));
$('div#id2').html('div#id2',$(data));

If the structure of your page allows it, you should use: http://api.jquery.com/load/ (as @Jorge says) to replace all of the html with one line.

$('div#targetDiv').load('Controller\AggregatedAction', anyData);

Cannot Return Custom HTTP Error Details Remotely

6 votes

This is a strange one. I'm running MVC 3 and have a custom action result that wraps exceptions and returns a message along with the standard HTTP error.

public class ExceptionResult : ActionResult
{
    private readonly Exception _exception;

    public ExceptionResult(Exception exception)
    {
        _exception = exception;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        var response = context.HttpContext.Response;
        response.ClearHeaders();
        response.Cache.SetNoStore();
        response.ContentType = ContentType.Json;

        var baseEx = _exception as BaseException ?? new ServerException(_exception);

        var result = baseEx.GetResult();

        var json = result.ToJSON();
        response.Write(json);
        response.StatusCode = (int)result.Status.Code;
    }
}

When I run this locally I get exactly what I expect:

HTTP/1.1 400 Bad Request
Cache-Control: no-store
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Date: Thu, 01 Dec 2011 19:00:03 GMT
Content-Length: 81

{"error":"invalid_request","error_description":"Parameter grant_type is missing"}

But when I try to connect from a different machine I get the standard IIS error message instead:

HTTP/1.1 400 Bad Request
Cache-Control: no-store
Content-Type: text/html
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Date: Thu, 01 Dec 2011 19:02:33 GMT
Content-Length: 11

Bad Request

UPDATE

There must be some http module somewhere in the IIS pipeline that is swallowing the response and rewriting the content. I wrote a module to log the request and response and it's returning exactly what I expect however what actually makes it to the browser is wrong.

2011-12-02 15:39:00,518 - ======== Request ========
2011-12-02 15:39:00,518 - GET /oauth/2/token HTTP/1.1
2011-12-02 15:39:00,519 - Cache-Control: max-age=0
2011-12-02 15:39:00,519 - Connection: keep-alive
2011-12-02 15:39:00,519 - Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
2011-12-02 15:39:00,519 - Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
2011-12-02 15:39:00,519 - Accept-Encoding: gzip,deflate,sdch
2011-12-02 15:39:00,519 - Accept-Language: en-US,en;q=0.8
2011-12-02 15:39:00,519 - Host: micah-pc:8095
2011-12-02 15:39:00,519 - User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2
2011-12-02 15:39:00,519 - =========================
2011-12-02 15:39:00,519 - OAuth exception occurred.
BoomTown.OAuth.OAuthException: Parameter grant_type is missing
   at BoomTown.OAuth.Request.TokenRequest.GetRequestValidator() in C:\code\BoomTown\Api\BoomTown.OAuth\Request\TokenRequest.cs:line 19
   at BoomTown.OAuth.Request.OAuthRequestBase.Validate() in C:\code\BoomTown\Api\BoomTown.OAuth\Request\OAuthRequestBase.cs:line 33
   at BoomTown.OAuth.Request.OAuthRequestBase..ctor(HttpRequestBase request, IOAuthServiceLocator serviceLocator) in C:\code\BoomTown\Api\BoomTown.OAuth\Request\OAuthRequestBase.cs:line 28
   at BoomTown.OAuth.Request.TokenRequest..ctor(HttpRequestBase request, IOAuthServiceLocator serviceLocator) in C:\code\BoomTown\Api\BoomTown.OAuth\Request\TokenRequest.cs:line 13
   at BoomTown.Api.Web.Controllers.OAuth.V2.OAuthController.Token() in C:\code\BoomTown\Api\BoomTown.Api.Web\Controllers\OAuth\V2\OAuthController.cs:line 26
2011-12-02 15:39:00,520 - ======= Response =======
2011-12-02 15:39:00,520 - HTTP/1.1 400 Bad Request
2011-12-02 15:39:00,520 - Cache-Control: no-store
2011-12-02 15:39:00,520 - X-AspNet-Version: 4.0.30319
2011-12-02 15:39:00,520 - Content-Type: application/json; charset=utf-8
2011-12-02 15:39:00,520 - {"error":"invalid_request","error_description":"Parameter grant_type is missing"}

SOLUTION

Thanks to a little sleuthing I was able to figure it out. I setup IIS tracing which confirmed my suspicions that it was related to the customerrormodule which was intercepting my requests and overwriting my error messages. I kept monkeying with the

<system.web>
  <customErrors />
<system.web>

settings but to no avail. I was on the right track, but since it's IIS 7 that I'm running I needed to change the correct web.config section like this:

  <system.webServer>
    <httpErrors errorMode="Detailed" />
  </system.webServer>

Now all my custom JSON messages come through perfectly. Big thanks to Jason Finneyfrock for the tag team on this one.

In your web.config, do you have httpErrors defined to only be DetailedLocalOnly? I'm not sure whether or not the content would be removed in this situation.

http://www.iis.net/ConfigReference/system.webServer/httpErrors

MVC Render Speedup

6 votes

I just hooked up the mvc-mini-profiler (thanks SO!) on my site and was looking around to see how well I've done up to this point (it's my first major bout with linq to entities and mvc). So far everything is looking good, however I'm always looking for ways to improve response times. At this point it looks like the only major boost I could get would be from reducing the time it takes to render the individual views on each of my pages.

profiler screeny

You can see from my screeny that the rendering of the Blog view is the longest running task. I know that 30ms is already really quick, but I'm betting there are still some tricks I can pull to get these numbers even lower.

So the question is this: How can I reduce view render times? I know that caching of dynamic views into something like the HttpRuntime.Cache can help, but I'm even seeing several ms durations for static view rendering. What techniques do you use to lower the render times of your views?

I suggest 2 things (if you don't have it done yet)...

  1. Remove unused ViewEngines. So if your project uses only the razor view engine, do this in the global.asax on Application_Start();

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new RazorViewEngine());
    

    or

    ViewEngines.Engines.Add(new WebFormViewEngine());
    

    if you use the WebFormsViewEngine only

  2. The biggest improvment is to use the OutputCacheAttribute to cache the html. I dont think your Blog changes on every Request ;)

    public class BlogController : Controller
    {
        [OutputCache]
        public ActionResult Index()
        {
           // do something here
           return View();
        }
    }
    

You can set the cache-duration and more. Check out: MSDN - OutputCacheAttribute.

Requests for static files are hitting the managed code in ASP.NET MVC3

5 votes

Creating custom IHttpModules, I have realized that the requests for static files (e.g.: .css and .js files) are hitting the managed modules. Probably pictures have the same problem. Shouldn't IIS bypass ASP.NET for files that exists in the filesystem?

For example:

public class MyModule:IHttpModule
{
    public void Dispose(){ }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += (o, e) => Debug.Print("Request: " + HttpContext.Current.Request.RawUrl);
    }
}

And I declare it this way:

<modules runAllManagedModulesForAllRequests="true">
  <add name="MyModule" preCondition="managedHandler" type="MVCX.Modules.MyModule, MVCX"/>
</modules>

But, even using the precondition I can see how the static files goes through the module:

Request: /MVCX/
Request: /MVCX/Content/Site.css
Request: /MVCX/Scripts/jquery-1.4.4.min.js

I have tried to ignore the rules for static files, but it does not make a difference:

routes.IgnoreRoute("{Content}/{*pathInfo}");
routes.IgnoreRoute("{Scripts}/{*pathInfo}");

Is this the usual? Or am I missing something here? As far as I know, if the static file request should be answered by IIS. If my managed module is being hit, means that a CLR ThreadPool thread is handling that request, right?

Regards.

UPDATE:

I have disabled the "runAllManagedModulesForAllRequests":

<modules runAllManagedModulesForAllRequests="false">
      <add name="MyModule" preCondition="managedHandler" type="MVCX.Modules.MyModule, MVCX" />
</modules>

And everything seems to work just fine, but I have found this article: http://www.britishdeveloper.co.uk/2010/06/dont-use-modules-runallmanagedmodulesfo.html that recommends remove and readd the "UrlRoutingModule-4.0" module with an empty precondition.

I my machine, the adding of that module is in the root web.config, and it has already an empty preCondition:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config>type machine.config | find "UrlRouting"


C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config>type web.config | find "UrlRouting"
            <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config>

So now I am a little bit confused, what is the status of this parameter? Should I use it or shouldn't? why does it come as "true" by default?

Regards.

I my machine, the adding of that module is in the root web.config, and it has already an empty preCondition

Perfect. That means this module will always run which is required for MVC as it uses extensionless urls.

So now I am a little bit confused, what is the status of this parameter? Should I use it or shouldn't? why does it come as "true" by default?

Because extensionless url support is new in IIS7 SP1 and IIS7.5 SP1. It is available for IIS7 as a patch that you have to request and install. You will find it here with complete answers to your questions: http://support.microsoft.com/kb/980368

Why this parameter comes to true by default ? Because VS2010 was shipped before IIS7 SP1. Maybe it is at false in new MVC projects in VS2010SP1 ?

5 votes

I'm about to start development on a project with very uncertain load/traffic specifics. When it will be released there will certainly be very low load that can easily be handled by a single desktop quad code machine.

The problem is that there will be (after some invite-only period) a strong publicity for the product so I expect considerable traffic/load peaks.

I haven't read enough about cloud providers and I'm mostly leaning toward Amazon or Azure for the credibility these two companies have without checking them out as I should with others (ie. Rackspace that I suppose is also a cloud service provider).

What I want

I would like to create a normal Asp.net MVC web application that can be run on in-house single machine low-cost server. It would run web server along with database (relational and maybe also document) and fulltext search (not SQL FTS but rather high speed separate product like Lucene or Sphinx). But after initial invite-only period I'd like to move this app to the cloud to make it more traffic/load demand-friendly.

As much as I know Amazon offers a sort of virtual machine hosting which I understand you setup as a normal server but has possible flexible resources in terms of load power. I'm not sure if that can be accomplished on Azure as well.

Questions

  1. What is your experience with application transition to cloud and which one did you choose and why?
  2. What would you recommend I should think about when designing/developing the solution to make the transition as painless as possible.
  3. Based on your experience is it better to move to the cloud (financial wise) or is it better to buy your own servers and load balance application yourself and maybe save money on the long run?

"Cloud" is such a vague term. Still, I think this is a very good question.

Basically, IaaS cloud hosting does not magically make your application scale. It's really a virtual private server with very short contract / cancellation periods.

For scalability, the magic lies not so much in the hosting, but in the horizontal scalability of the application code itself. This is related to all the distributed computing challenges. For example, adding more application servers is not always easy: you must be sure that you don't persist any user state in the server application (but rather in a database, static can be evil), caching can be problematic because local caches can make the situation worse if you're using a round-robin strategy, etc.

To answer questions 1 and 2, you don't really have to do anything different just to host on EC2 or Azure -- basically. But of course, it's not that easy when things grow.

For instance, EC2 instance storage is rather limited. Additional storage on EBS, however, does not provide comparable performance characteristics and can be a bit more laggy than a disk. The point here is that EBS does magically scale, and it's probably more PaaS than IaaS; but it's not a simple hard disk and it does, consequently, not behave like a hard drive. I don't know about Azure block storage. In general, expect additional abstraction layers to introduce problems of their own, no matter what they do.

To answer question 3: Typical cloud providers are more expensive than the usual 'round-the-corner VPS providers, but they are, to my experience, also much more reliable and professional. EC2 has a free tier (but it's quite small), Azure gives you a small instance for free for 3 months.

Doing the calculation right is rather tricky; for example, if you have to shut down your service for whatever reason, it's nice to be able to cancel now rather than pay another year - you might want to put this risk into your calculation. On the other hand, both EC2 and Azure will be considerably cheaper if you sign up for 6 or 12 months, rather than paying by the hour.

You might want to check out the free Azure plan, because it's nice to start fiddling around without any cost. A big advantage of cloud providers is that you can scale vertically very easily: buying a 16 core, 64GB RAM server machine is really expensive, but if there's so much traffic on your site, upgrading your plan won't be such a big issue.

Microsoft Azure MVC 3 web role not starting after adding TwilioController base class inheritance

4 votes

Azure web role (MVC 3 project) wont' start with Twilio controller class

  • I have an MVC 3 app that is hosted on MS Azure. It publishes to Azure Web role - No problems.
  • I added the Nuget Twilio and Twilio.Mvc packages. It still published to Azure Web role - No problems.
  • I added a class that inherits from the Mvc.TwilioController base class and subsequently the Azure web role no longer starts.
  • If I remove the TwilioController class inheritance the web role starts.

The projects publishes, runs and twilio functions fine in my local VS Azure emulator environment.

The project References; Twilio.api, Twilio.mvc, Twilio.Twiml, RestSharp, and NewtonSoft.Json are all set to CopyLocal=True.

All Azure Diagnostic logging/tracing is enabled and sending to table storage every 5 seconds but no log data is available when the web role fails to start. Note: If I comment out the twilioController I get an abundance of log data so Azure Diags are configured correctly.

Because the web role continues to abort/cycle/abort, there is no opportunity to RDP to the vm for further troubleshooting.


The following two errors are written to the failing web role's Windows System Event Log about every minute:

The application '/' belonging to site '1273337584' has an invalid AppPoolId 'ca5c9ecb-e68d-4f3a-84c2-c0b4430373e9' set. Therefore, the application will be ignored.

.

Site 1273337584 was disabled because the root application defined for the site is invalid. See the previous event log message for information about why the root application is invalid.


Steps to reproduce (exact steps):

  1. Install Azure Sdk v 1.6
  2. Create a new project using the Azure template (visual studio 10 sp1)
  3. Choose the Asp.net MVC 3 Web Role
  4. Build and Publish to Azure
  5. Success - Web Role starts
  6. Add Nuget Package "Twilio" version 3.3.2
  7. Add Nuget Package "Twilio.Mvc" version 3.1.3
  8. Build and Publish To Azure
  9. Success - Web Role starts
  10. Create an empty controller (HelloController). See below code snippet.
  11. Add TwilioController base class (e.g. Public Class HelloController : TwilioController)
  12. Build and Publish to Azure
  13. Fail - the web role just cyles/aborts/cyles.
  14. Comment out TwilioController (e.g. Public Class HelloController // :TwilioController)
  15. Buld and Publish to Azure
  16. Success - web role starts

    using System.Web.Mvc;
    using Twilio.TwiML.Mvc;
      namespace WindowsAzureProject857481.Controllers
        {
            public class HelloController : TwilioController
            {
                //
                // GET: /Hello/
    
                public ActionResult Index()
                {
                    return View();
                }
    

Any ideas appreciated.

Thanks, Jim

Jim:

I work for Twilio, and own the .NET helper library.

Whats happening is that the Twilio.Mvc assembly is looking for 2.0 version the System.Web.Mvc assembly, since that's what its built against. Its not finding it because its obviously not being packaged with your MVC 3 project.

The fix if fairly easy. Get the Twilio.Mvc source, change the reference to the newer version of MVC and recompile the assembly. I believe our support team is going to contact you with a version I built for you if you don't want to do it yourself.

I think there is probably also a way to use assembly binding redirects to fix the problem as well, if you wanted to try that.

Devin

How to create custom MVC3 ActionLink method?

4 votes

Possible Duplicate:
How to put span element inside ActionLink MVC3?

How to create custom MVC3 ActionLink method that generates this output:

<li>
    <a href="/Home/ControllerName" data-ajax-update="#scroll" 
     data-ajax-mode="replace" data-ajax-method="GET" 
     data-ajax-loading="#progress" data-ajax="true">

     <span>LinkText</span> // this span generated inside <a>

    </a>
</li>

You either create a new extension method that returns an MvcHtmlString object that you put together yourself (mind the html encoding, though), our you create a partial view that you can render when you need it, so you don't have to create HTML through code.

public static class MyHtmlExtensions {
    public static MvcHtmlString MyActionLink(this HtmlHelper html, string action, string controller, string ajaxUpdateId, string spanText) {
         var url = UrlHelper.GenerateContentUrl("~/" + controller + "/" + action);
         var result = new StringBuilder();
         result.Append("<a href=\"");
         result.Append(HttpUtility.HtmlAttributeEncode(url));
         result.Append("\" data-ajax-update=\"");
         result.Append(HttpUtility.HtmlAttributeEncode("#" + ajaxUpdateId));
         // ... and so on

         return new MvcHtmlString(result.ToString());
    }
}

Migrating from ASP.NET WebForms to MVC

4 votes

I've read plenty of questions and articles stating that converting from ASP.NET Webforms to MVC is near enough impossible. However I think my scenario is different.

I foolishly started working a project about a year ago in Webforms, but the approach I took (as far as I understand) is very MVC like. I have disabled forms validation, don't use any postbacks, use URL re-writing and all page changes are AJAX requests that load the page contents of ContentPlaceHolders (using a small hack, overriding the RenderControl method). I've also used my own ORM and RESTful Service API in seperate projects, referenced in the website.

Now the system works really well, the pages are partially refreshed fine and the url's are changed when the ajax calls are made, so when the page is refreshed, it looks exactly the same.

Now I've just been told I need to learn MVC for a new big project (but I have to finish another project first), but I've done a bit of reading on the subject and started a few Hello World apps, and it seems that the idea of ASP.NET MVC is pretty much exactly what I have already created.

Would StackOverflow still recommend against converting the Webforms application to MVC? Are there any other benefits of converting to MVC, besides best practices?

I have a very large and old ASP.NET WebForms application (which was originally written for .NET 1.1!) and have enabled MVC to work in it, side-by-side. I've been writing new features using MVC, and converting old WebForms features to MVC controllers and views.

I ran into a few little issues concerning URL authorization and running in IIS integrated mode, but once I understood the issues they were fairly easy to work out. So nearly impossible? Certainly not!

I can't tell you if it's worth your time to convert it, since I don't know the size, scope or nature of your application, or any business constraints. However, moving to MVC has been a considerable boon to ease of development and quality (since it is easier to compartmentalize and unit test). Not to mention, the features I've written (or rewritten) in MVC are much cleaner, faster, and responsive than the WebForms equivalents.

I'm excited to move it from MVC v2 to v3, and convert to Razor views. So would I? I if I were you and I had the time, I'd do it.

Here's a question I posted that outlines the process and one of the more significant problems I had in the conversion. Migrating legacy ASP.NET to MVC 2 (RC): HttpApplication events not firing, User principal is null

Unit testing asp.net mvc restful routing FormatResult

4 votes

I'm using restful routing module for asp.net mvc and very happy with it. But I can't get one thing. For example I had a controller action like this:

public ActionResult Index()
{
    if (Request.IsAjaxRequest()) 
        return PartialView();
    return View();
}

And had no problem with writing a spec like this:

[Subject(typeof(LotsController))]
public class When_Index_called
{
    static LotsController controller;

    static ActionResult actionResult;

    Establish context = () => {
        controller = mocker.Create<LotsController>();
        controller.ControllerContext = Contexts.Controller.Default;
    };

    Because of = () => actionResult = controller.Index();

    It should_render_view = () => actionResult.AssertViewRendered().ForViewWithDefaultName();

But with use of rest, I want to have an Index method like this:

public ActionResult Index()
{
    return RespondTo(format => {
        format.Html = () => {
            if (Request.IsAjaxRequest()) 
                return PartialView();
            return View();
        };
        format.Json = () => Json(new { });
    });
}

Sure that previous spec fails, because action result is not of type ViewResult, its of type FormatResult. FormatResult by itself overrides ExecuteResult method that returns void. How can I unit test such case if I want to verify action result types and data inside FormatResult?

In the future version of restful routing such code is possible:

var formatResult = actionResult as FormatResult;
ActionResult result = formatResult.ExposeResult().Html();
result.ShouldBeOfType<ViewResult>();

And it will do the job.

How to expose global object like @User in razor views?

4 votes

How/Where can I declare an object like @User so that it can be referenced globally in any razor view using @?

Something similar to:

_LoginPartial.cshtml:

@if(Request.IsAuthenticated) {
    <text>Signed In As  <strong>@User.Identity.Name</strong> |

but instead reference my object:

  @if(this && that) { <text>@MyObject.GetData</text> }

You can change the base type of a Razor page to a one of your own eg:

public class UserAwareViewPage : System.Web.Mvc.WebViewPage
{
  public IPrincipal User { get { return Thread.CurrentPrincipal; } }
}

And then modify your config file like so:

<system.web.webPages.razor>
  <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, 
  System.Web.Mvc, Version=3.0.0.0, 
  Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  <pages pageBaseType="Your.Namespace.UserAwareViewPage">
    <namespaces>
      <add namespace="System.Web.Mvc" />
      <add namespace="System.Web.Mvc.Ajax" />
      <add namespace="System.Web.Mvc.Html" />
      <add namespace="System.Web.Routing" />
    </namespaces>
  </pages>
</system.web.webPages.razor>

Phil Haack has a very good blog post on this here.

Alternatively, you can add an extension method to System.Web.Mvc.WebViewPage (the base type for razor pages) and use this.

public static IPrincipal User(this System.Web.Mvc.WebViewPage page)
{
  return Thread.CurrentPrincipal;
}

Which would be useable like so:

@if(Request.IsAuthenticated) {
<text>Signed In As  <strong>@User().Identity.Name</strong>

Personally, I prefer the first approach, but thought I'd provide the second for an alternative option.

What Does UpdateModel() Do?

4 votes

In layman's terms, what does UpdateModel() do, as well as TryUpdateModel()? I can't seem to find (on SO or the web) any clear explanation of what it actually does (in clear terms), just people having problems using it.

VisualStudio's intellisense is not helping me either. The reason why I ask is because, let's say, if I have this in my controller:

[HttpPost]
public ActionResult Index( UserViewModel vm, FormCollection form)
{    
  var statesCheckBoxes = form["StatesList"];       

  vm.BA.StatesTraveledTo = statesCheckBoxes.Split(',').ToList<string>();

  return View(vm);
}

Aren't I already updating my model by setting vm.BA.StatesTraveledTo ? Why do I need to run UpdateModel? Also, when I actually try to do the following:

[HttpPost]
public ActionResult Index( UserViewModel vm, FormCollection form)
{    
  var statesCheckBoxes = form["StatesList"];       

  vm.BA.StatesTraveledTo = statesCheckBoxes.Split(',').ToList<string>();

  UpdateModel(vm); // IS THIS REDUNDANT TO THE PREVIOUS LINE?

  return View(vm);
}

Nothing seems to happen in that when I inspect the value of the ModelState (after I run UpdateModel() ), I don't see anything indicating that anything has changed. I don't see a new key in the ModelState dictionary.

Really confused. Thanks!

Edit:

Posting the source code for the ViewModel and Model classes:

public class UserViewModel
{
  public BankAccount BA { get; set; }
}

public class BankAccount
{
  public Person User { get; set; }
  public List<string> StatesTraveledTo { get; set; }
}

public class Person
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public int Age { get; set; }
}

what happens when you write

public ActionResult Index( UserViewModel vm)
{    }

and when u inspect in the actionresult you find that vm contains values that you posted from the view. it is because mvc directs the modelbinder to extract values from different sources (form collection, route values, querystring etc) and populate values of your model. But for this to happen your form keys must match the name of properties in your model and if that is the case your model is populated correctly. now we come to the actual question: what does UpdateModel do? simple answer is nothing but model binding. The difference is only that you call it explicitly. The above Actionresult can be rewritten like using UpdateModel

Public ActionResult Index ()
{
   UserViewModel vm = new UserViewModel();
   UpdateModel(vm);// it will do same thing that was previously handled automatically by mvc
}

Now, what was not handled by automatic model binding will not be handled by explicit model binding as well because its not the problem with model binder its the problem with your html. with nested view models like yours, the form field names must be carefully crafted so mvc can correctly inject it to your model without you having to write something like

vm.BA.StatesTraveledTo = statesCheckBoxes.Split(',').ToList<string>(); 

and if you don't want to do such thing check this google search

Is it possible for an asp.net MVC "Action Method" to receive JSON without declaring its specific type on the parameter? If so, how?

4 votes

So, it pretty much is all in the title. Basically, I want to send a JSON through JQuery from client to asp.net MVC. I'm wondering if it's possible for it to receive (but not necessarily parse) any JSON I want to send from JQuery Ajax call, regardless of it's type.. without me having a concrete type/model representation of it. (basically like a dynamic type?)

Doing this the regular way (with me declaring the passing argument as type Object) just brings about nulls, which was what I expected.

Basically, I want to do some kind of "reflection for JSON" type stuff when I receive it, and be able to get it's properties through some kind of foreach loop and etc.

Thanks in advance. Any help would be great!

You could use an IDictionary<string, object> as action argument. Just write a custom model binder that will parse a JSON request into it:

public class DictionaryModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
        {
            return null;
        }

        controllerContext.HttpContext.Request.InputStream.Position = 0;
        using (var reader = new StreamReader(controllerContext.HttpContext.Request.InputStream))
        {
            var json = reader.ReadToEnd();
            if (string.IsNullOrEmpty(json))
            {
                return null;
            }
            return new JavaScriptSerializer().DeserializeObject(json);
        }
    }
}

which will be registered in Application_Start:

ModelBinders.Binders.Add(typeof(IDictionary<string, object>), new DictionaryModelBinder());

then you could have the following controller action:

[HttpPost]
public ActionResult Foo(IDictionary<string, object> model)
{
    return Json(model);
}

to which you can throw anything:

var model = {
    foo: {
        bar: [ 1, 2, 3 ],
        baz: 'some baz value'
    }
};

$.ajax({
    url: '@Url.Action("foo")',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify(model),
    success: function (result) {
        // TODO: process the results from the server
    }
});