Showing posts with label asp.net mvc. Show all posts
Showing posts with label asp.net mvc. Show all posts

Tuesday, December 10, 2013

A little JQuery <-> Json <-> Mvc

It has been quite awhile since I've dealt with complex model binding across a set of inputs in mvc.

If one controller action's params (or a property on one of those params) is an IEnumerable<T> then Mvc will not write the element names in a way that will properly go back to the server.

Short and sweet (and probably hacky way) to do it:

I Json serialized the parent model into an html attribute on to some parent of the value(s) I want to submit. Some questions allow many answers, and I want to save each time a question answer is changed.

I'm working on making that serialization not contain the about to be consumed IEnumerable<T>property.
var save = function (element, value, event, previousValue,debug) {
    console.log('saving:' + value + ' to ' + saveUrl);

    
    var demoText = $(element).closest('[data-demographic]').attr('data-demographic');
    var demoJson = htmlDecode(demoText);
    var demoModel = JSON.parse(demoJson);
    var oldAnswers = demoModel.PossibleAnswers;
    demoModel.PossibleAnswers = [];
    //TODO: handle multi-select, single-select radio, etc...
    if (Object.prototype.toString.call(value) === '[object Array]') {
        //value and previousValue could be arrays for multi-select answers
        $.each(value, function (i, e) {
            demoModel.PossibleAnswers.push({ Id: e });
        });
    } else {
        demoModel.PossibleAnswers.push({ Id: value,Text:$(element).parent().text().trim()  });
    }
    var data = JSON.stringify(demoModel);

    data=data.replace('][', '].[');
    $.ajax({
        dataType: 'json',
        url: saveUrl,
        contentType: 'application/json; charset=utf-8',
        accept: debug ? {
            json: 'application/json',
        } : {},
        type: 'POST', data: data,
        success: function (data, status, jqXhr) {
            if (debug) {
                var answerMirror = data.PossibleAnswers;
                delete data.PossibleAnswers;
                console.log(data);
                console.log(answerMirror);
            } else { //update dom to new read-only model?
                //TODO: adjust with new html
                console.log(data);
            }
            
        }
    });
};
There's the heart of it. Notice I don't actually have to use the special name=foo.bar[0].Id on my inputs, but that is probably still a good idea. This thing was almost working without doing any JSON.stringify but the IEnumerable<T> data was ignored by Mvc's model binder. With that in mind, I had to specify the contentType of the ajax request.

Wednesday, December 4, 2013

Inline markup delegates and razor helper delegate wars

So I had some markup that was being duplicated all over the place in MVC.

This is mostly boilerplate bootstrap paneling, with masonry on top.

How would you provide a generic razor to meet bootstrap/masonry without creating a bunch of classes or partials to account for special case(s)?  How in razor would you write a method that can take inline markup (for example any @<text> call)?

 One way is @helper Take note that HelperResult's documentation says specifically it is not intended to be used directly in your code. So I headed in down the path of inline razor helper delegates:

Monday, February 11, 2013

MyModelMetadataProvider

So I was tired of not having any inheritance on Attributes in Mvc for scaffolding, column order, display names and the rest. Here's some code to Allow an attribute called ParentsAttribute to allow you to inject parental metadata for another class that Mvc's DisplayFor will listen to.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MyMvc.Models
{

  public class ParentsAttribute:Attribute
  {
    public Type[] Values { get; set; }
    public ParentsAttribute(params Type[] parents)
    {
      Values = parents;
    }
  }


  public class MyModelMetadataProvider:DataAnnotationsModelMetadataProvider
  {

    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
      //called once for each property of the viewmodel

      var attrs =attributes.ToArray(); // for possible multiple enumeration
      
      var modelMetadata=base.CreateMetadata(attrs, containerType, modelAccessor, modelType, propertyName);

      if (containerType == null) return modelMetadata;
      var parents =(ParentsAttribute) containerType.GetCustomAttributes(typeof(ParentsAttribute),false).FirstOrDefault(); //search all parents for display attributes that we can inherit
      if(parents==null)
        return modelMetadata;

      var props = from metdataParents in parents.Values
                  from mProp in metdataParents.GetProperties()
              where mProp.CustomAttributes.Any() && mProp.Name == propertyName
                  select new { parent = metdataParents, mProp };
      var sample=props.ToArray();


      var q = from metdataParents in parents.Values
            from mProp in metdataParents.GetProperties()
            where mProp.CustomAttributes.Any() && mProp.Name == propertyName
            
            let da = mProp.GetCustomAttributes(typeof(DisplayAttribute),true).OfType<DisplayAttribute>().FirstOrDefault()
            let dna = mProp.GetCustomAttributes(typeof(DisplayNameAttribute),true).OfType<DisplayNameAttribute>().FirstOrDefault()
            //don't copy down required, may not be required for the current class or viewmodel
            //let ra = mProp.GetCustomAttributes(typeof(RequiredAttribute), true).OfType<RequiredAttribute>().FirstOrDefault()
            where da !=null || dna !=null
            
              select new { mProp,da,dna };
      foreach (var modifier in q)
      {
        if (modifier.da != null)
        {
          //http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/1b78397f32fc#src/System.Web.Mvc/DataAnnotationsModelMetadataProvider.cs
          if (string.IsNullOrEmpty(modelMetadata.Description)) modelMetadata.Description = modifier.da.GetDescription();
          if (string.IsNullOrEmpty(modelMetadata.ShortDisplayName)) modelMetadata.ShortDisplayName = modifier.da.GetShortName();
          if (string.IsNullOrEmpty(modelMetadata.Watermark)) modelMetadata.Watermark = modifier.da.GetPrompt();
          modelMetadata.Order = modifier.da.GetOrder() ?? modelMetadata.Order;
          if (string.IsNullOrEmpty(modifier.da.GetName()) == false) //only set current object if it is overriding the display
          {
            modelMetadata.DisplayName = modifier.da.GetName();  
          } else if (modifier.dna != null)
          {
            modelMetadata.DisplayName = modifier.dna.DisplayName;
          }
          
        }
        
      }

      return modelMetadata;

    }
  }
}
And don't forget to hook it up in your Global.asax! ModelMetadataProviders.Current = new MyModelMetadataProvider();

Wednesday, February 6, 2013

AutoScaffold Index.cshtml tabular view site-wide

I was tired of generating the index.cshtml list over and over again everytime the model or viewmodel changes. So how could I get that to be runtime generated? Enter Haacked's article on Mvc Tabular Display Template Updated to work with Razor and with IEnumerable First the DisplayTemplate
@using System
@using MyMvc.Models
@model IEnumerable<dynamic>
@{
    //http://haacked.com/archive/2010/05/05/asp-net-mvc-tabular-display-template.aspx
    var items = Model.ToArray();
    var metadata = ModelMetadata.FromLambdaExpression(m => m.ToArray()[0], ViewData);
    var properties = metadata.Properties.Where(pm => TableViewHelper.ShouldShow(pm, ViewData)).OrderBy(pm => pm.Order);
}

<table>
    <thead>
        <tr>
            @foreach (var property in properties)
            {
                <th>@property.GetDisplayName()</th>
            }
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            var itemMetadata = ModelMetadata.FromLambdaExpression(m => item, ViewData);
            <tr>
               
                @foreach (var property in properties)
                {
                    var propertyMetdata = itemMetadata.Properties.Single(m => m.PropertyName == property.PropertyName);
                    <td>
                        @Html.DisplayFor(m => propertyMetdata.Model)
                    </td>
                }
            </tr>
        }
    </tbody>
    
</table>

<span>Showing @Model.Count() items</span>
Then the helper
@using System
@using MyMvc.Models
@model IEnumerable<dynamic>
@{
    //http://haacked.com/archive/2010/05/05/asp-net-mvc-tabular-display-template.aspx
    var items = Model.ToArray();
    var metadata = ModelMetadata.FromLambdaExpression(m => m.ToArray()[0], ViewData);
    var properties = metadata.Properties.Where(pm => TableViewHelper.ShouldShow(pm, ViewData)).OrderBy(pm => pm.Order);
}

<table>
    <thead>
        <tr>
            @foreach (var property in properties)
            {
                <th>@property.GetDisplayName()</th>
            }
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            var itemMetadata = ModelMetadata.FromLambdaExpression(m => item, ViewData);
            <tr>
               
                @foreach (var property in properties)
                {
                    var propertyMetdata = itemMetadata.Properties.Single(m => m.PropertyName == property.PropertyName);
                    <td>
                        @Html.DisplayFor(m => propertyMetdata.Model)
                    </td>
                }
            </tr>
        }
    </tbody>
    
</table>

<span>Showing @Model.Count() items</span>
Then add a Views/Shared/Index.cshtml
@model System.Collections.Generic.IEnumerable<dynamic>
@{
    ViewBag.Title = "Index";
}

@Html.DisplayForModel("Table")

Change your Mvc AddView template

I didn't like that the AddView template ignored the DisplayAttribute(Order=0). So I adjusted it following http://blogs.msdn.com/b/webdev/archive/2009/01/29/t4-templates-a-quick-start-guide-for-asp-net-mvc-developers.aspx I added the following to my CodeTemplates/AddView/CSHTML/List.tt file
IEnumerable<PropertyInfo> SortProperties(IEnumerable<PropertyInfo> props)
    {
      var pl = props.ToList();
      var length = pl.Count;
      var q = from p in pl
            let da = p.GetCustomAttribute<DisplayAttribute>()
            where da != null
            let o = da.GetOrder()
            where o.HasValue
            orderby o
            select new { p, o.Value };

      var ordered = q.ToList();
      var remainder = pl.Except(ordered.Select(o => o.p).ToArray()).ToArray();
      var destination = new PropertyInfo[length];

      foreach (var o in ordered)
      {
        destination[o.Value] = o.p;
      }

      if (ordered.Count == length)
        return ordered.Select(o => o.p).ToArray();

      var remainderCount = 0;
      for (int i = 0; i < pl.Count; i++)
      {
        if (destination[i] != null)
          continue;
        destination[i] = remainder[remainderCount];
        remainderCount++;

      }
      return destination;


    }
then updated GetEligibleProperties to call it in the property loop. Here's the unit test:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

using Microsoft.VisualStudio.TestTools.UnitTesting;


namespace MyMvc.Tests.CodeTemplates.AddView.CSHTML
{
  [TestClass]
  public class ListTests
  {
    class SortTester
    {
      public Guid FileGuid { get; set; }

      [Display(Order = 0)]
      public string FileName { get; set; }

      public int CustomerId { get; set; }

      [Display(Order = 1)]
      public int? ExpectedTrxCount { get; set; }
    }
    IEnumerable<PropertyInfo> SortProperties(IEnumerable<PropertyInfo> props)
    {
      var pl = props.ToList();
      var length = pl.Count;
      var q = from p in pl
            let da = p.GetCustomAttribute<DisplayAttribute>()
            where da != null
            let o = da.GetOrder()
            where o.HasValue
            orderby o
            select new { p, o.Value };

      var ordered = q.ToList();
      var remainder = pl.Except(ordered.Select(o => o.p).ToArray()).ToArray();
      var destination = new PropertyInfo[length];

      foreach (var o in ordered)
      {
        Console.WriteLine("ordering:" + o.p.Name);
        destination[o.Value] = o.p;
      }

      if (ordered.Count == length)
        return ordered.Select(o => o.p).ToArray();

      var remainderCount = 0;
      for (int i = 0; i < pl.Count; i++)
      {
        if (destination[i] != null)
          continue;
        destination[i] = remainder[remainderCount];
        remainderCount++;

      }
      foreach (var d in destination) Console.WriteLine(d.Name);
      return destination;


    }
    [TestMethod]
    public void Sort_DisplayOne_IsSecond()
    {
      var type = typeof(SortTester);
      
      var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
      var expected = props.First(p => p.Name == LinqOp.PropertyOf(()=>new SortTester().FileName).Name);
      var sorted = SortProperties(props);
      var actual = sorted.First();
      Assert.AreEqual(expected, actual);

    }
    [TestMethod]
    public void Sort_DisplayZero_IsFirst()
    {
      var type = typeof(SortTester);

      var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
      var expected = props.First(p => p.Name == LinqOp.PropertyOf(() => new SortTester().ExpectedTrxCount).Name);
      var sorted = SortProperties(props);
      var actual = sorted.Skip(1).First();
      Assert.AreEqual(expected, actual);

    }
  }
}

Here's the source of LinqOp

Thursday, January 31, 2013

Mvc4 unit(?) testing of views with BDD & SpecFlow

I wanted to unit test my mvc. I had no idea what that could mean. Not my pocos or models, my Mvc. After googling what are the primary targets taken by unit testers? Controllers, almost never Views. The level of complexity behind getting a test framework given v as our view to do v.ExecuteResult() seems over the top immense. The spec flow: Feature: Register a new User In order to register a new User As member of the site So that they can log in to the site and use its features Scenario: Browse Register page When the user goes to the register user screen Then the register user view should be displayed And the view should have a username input field The MsTest class:
using System;
using System.Web.Mvc;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using MvcContrib.TestHelper;

using MvcSpecFlow.Controllers;
using MvcSpecFlow.Views.Account;

using RazorGenerator.Testing;

using TechTalk.SpecFlow;

//http://www.codeproject.com/Articles/82891/BDD-using-SpecFlow-on-ASP-NET-MVC-Application
//http://blog.davidebbo.com/2011/06/precompile-your-mvc-views-using.html
//http://blog.davidebbo.com/2011/06/unit-test-your-mvc-views-using-razor.html

namespace MvcSpecFlow.Tests
{
  [Binding]
  public class RegisterUserSteps
  {
    ActionResult result;

    AccountController controller;

    [When(@"the user goes to the register user screen")]
    public void WhenTheUserGoesToTheRegisterUserScreen()
    {
      controller = new AccountController();
      result = controller.Register();

    }

    [Then(@"the register user view should be displayed")]
    public void ThenTheRegisterUserViewShouldBeDisplayed()
    {
      Assert.IsInstanceOfType(result, typeof(ViewResult));

      var vResult = (ViewResult)result;
      Assert.AreEqual(string.Empty, vResult.ViewName);

      vResult.AssertViewRendered().ForView(string.Empty); //should follow convention not pass a special view name
      
    }

    [Then(@"the view should have a username input field")]
    public void ThenTheViewShouldHaveAUsernameInputField()
    {
      var view = new Register();
      var doc = view.RenderAsHtml();
      var username = doc.GetElementbyId("UserName");
      Assert.IsNotNull(username);
    
    }
  }
}
The requirements:
  1. Use RazorGenerator to precompile your views
  2. The view you are testing has the RazorGenerator set as the Custom Tool
  3. The view you are testing does not have the following razor: @Html.AntiForgeryToken()
  4. Your testing project has RazorGenerator.Testing installed.

Wednesday, May 23, 2012

Foray into custom model binders

I have a complex viewmodel
public class ScenarioCreationDTO
  {
    [Required]
    [StringLength(500)]
    public string Comments { get; set; }
    [Required]
    public IEnumerable<int> PriceLists { get; set; }
    public IEnumerable<PriceListCurrencyDTO> Currencies { get; set; }
    public IEnumerable<PriceListCountryDTO> Countries { get; set; }
  }
Currencies has a compound key. How do I get binding to work for either checkBoxList or RadioButtons? Sometimes the user is allowed to select multiple, other times not. Perhaps the problem was in the view?
<tbody>
                                    @foreach (SelectListItem item in ViewBag.CurrencySelect)
                                    { 
                                        <tr>
                                            <td>
                                                @if (ViewBag.MultipleCurrencies)
                                                {
                                                    @Html.RadioButton("currencies", item.Value, item.Selected)
                                                }
                                                @Html.Label(item.Text)
                                            </td>
                                        </tr>
                                    }
                                </tbody>
I did not find a RadioButtonList helper, nor a RadioButtonFor<T> that took multiple values. Thanks to Msdn magazine and Buildstarted.com I now have a custom model binder.
protected override void OnApplicationStarted()
    {
      base.OnApplicationStarted();
      ModelBinders.Binders.Add(typeof(PriceListCurrencyDTO),new PriceListCurrencyBinder());

- global.asax.cs
public class PriceListCurrencyBinder:IModelBinder
  {
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
      ValueProviderResult value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
      var selected = value.AttemptedValue;
      var result = Shared.SyntaxSugar.Helpers.Deserialize<PriceListCurrencyDTO>(selected);
      return result;
    }
  }

var currencySelect=currencies.Select(c => new SelectListItem()
      {
        Text = c.CurrencyCode,
        Value = Shared.SyntaxSugar.Helpers.Serialize(c),
        Selected = priceListCurrenciesSelected != null && priceListCurrenciesSelected.Any(plc=>c.PriceListID==plc.PriceListID && c.CurrencyCode==plc.CurrencyCode)
      }).ToArray();
      ViewBag.CurrencySelect = currencySelect;
[HttpPost]
    public virtual ActionResult Create(byte regionID, Int64 dealID, Model.ViewModel.Scenarios.ScenarioCreationDTO data){
//...
}
and finally the view
<tbody>
                                    @foreach (SelectListItem item in ViewBag.CurrencySelect)
                                    { var i=-1; i++;
                                        <tr>
                                            <td>
                                                @if (ViewBag.MultipleCurrencies)
                                                {
                                                    @Html.RadioButton("currencies["+i+"]", item.Value, item.Selected)
                                                }
                                                @Html.Label(item.Text)
                                            </td>
                                        </tr>
                                    }
                                </tbody>

Friday, January 13, 2012

Stand up Ninject on MVC against EF4

Here's the bindings I used to get it done


    /// <summary>
    
    /// Load your modules or register your 
services here
    
    /// </summary>
    
    /// <param name="kernel">The kernel.</param>
    
private static void RegisterServices(IKernel kernel)
    {
      
  kernel.Bind<string>()
        .ToMethod(f =>
#pragma warning disable 0618 //disable obsolete warning as this is the ONLY place this method should be used in the application
          
  ProjectDb.Adapter.EfExtensions.CreateConnectionString(
#pragma warning restore 0618
          "data source=databaseSource;initial 
catalog=YourDbName;integrated 
security=True;multipleactiveresultsets=True;App=EntityFramework"))
      .Named("efString");

      
kernel.Bind(typeof(Project.Shared.Behaviors.IRepository<>))
  .To<ProjectDb.Adapter.DEVEntities>()
  .InThreadScope()
        
  .WithConstructorArgument("connectionString",kernel.Get<string>("efString"));


    }

Wednesday, October 19, 2011

Routing except webforms requests

I have a legacy webforms (.aspx) project that I'm sliding routing and MVC3 into. I needed a route that would not prevent the default page from being handled by IIS, but would allow all other controller style routing.  How do you make a regex that only fails uri's that have .aspx or .axd in them?

Then I thought well I still don't want anything that doesn't look like an mvc route (things that end in .foo or .fooo)  Sounds like a crazy negative regex.
Here's the regular expression:

^(.(?!\.[a-zA-Z0-9]{3,}))*$


Match anything that doesn't look like a file extension 


And here's the routeAdd in global.asax


static void RegisterRoutes(RouteCollection routes)
  {

     routes.MapRoute("mvc", "{controller}/{action}/{id}",defaults: new{action="index",id=""}, constraints:new{controller=@"^(.(?!\.[a-zA-Z0-9]{3,}))*$"});

  }

Wednesday, November 3, 2010

Experimental Technologies

Before I lose track of all the things I've been touching in the last 6 months, I wanted to say something on the matter of each and keep track of where I've been and where I might want to go.

Mef - twice (both plug-in UI projects)

Vs2010 Add-ins -

  1.  checks all project reference to ensure none are absolute paths, one that d
  2.  ExtensionsProject for my team
    1. parses all project files 
    2. locates .config files
    3. checks for config values to be in compliance with team standards
    4. checks your projects' FileCodeModel to confirm the code meets other team standards
    5. will remove some project file customizations temporarily, confirm it builds in release mode, then brings up the svn commit dialog
    6. allows you to store compliance information on your modules/projects on the team for easy access.
  3. used a pluggable UI by importing an Action or using the default built-in messagebox if no extension is found.

  1. Team policy reminder/enforcers
    1. warn on calls to forbidden methods
      1.  GC.Collect
      2. GC.AddMemoryPressure
      3. Messagebox.Show
    2. warn if inheriting directly from Windows.Forms or Windows.Control
    3. warn if a control or form subclass constructor does not call InitializeComponent()
    4. warn if a control property is not set per team standards
      1. DialogBorderStyle must be fixed
    5. error if you do not override certain virtual properties (legacy need from vs2005 designer bug)
    6. error if you have code that raises a NotImplementedException
    7. warn if you don't have hungarian notation to name controls
    8. warn if fields are not private
  2. Policy to ensure a project does not call a Config value or index that does not exist.
  • Mef extension  UI
  • Web data scraper/exporter
  • datagrid context menu
Mvc
  • Mvc 2
    • Lots of projects 2 at work, many more at home
  • Mvc 3
    • Got 2/3 through a project and had to go back to MVC2 due to changing requirements, transfer went very well.
  • Unity - Nice DI framework from Microsoft.
  • Ninject - Very nice lightweight DI framework
  • Poor Man's - Did manual DI for the longest time, so happy to have finally switched to learning Ninject and Unity
  • Wrote a task that walks all found project files under a path and determines safe build DirectedAcylicGraphs
    • Detects Circular dependencies/references
    • walks project files in parallel
  • Wrote a task on the ordering task that will generate a properly parallelizing and multi-threading MsBuild project file.
Parallelization/threading/async
  • Rx - wrote a producer consumer where a consumer can produce additional items to be consumed
  • PLinq - used in Rx producer consumer to parallelize the Rx search
  • Async CTP - have not used it quite yet, but did attend the 2010 PDC broadcast
  • Linq-2-Sql - lots of personal projects
  • EF4 - now that I've used it, I actually like it much better than linq to sql

Javascript cross site script without Preflight / Cross Origin Resource Sharing - also Http Access Control
  • Wrote javascript that uses jQuery in a bookmarklet to allow users to save data from a page on one site cross-domain into a private secured store in a db, with group sharing options, aka Cross Origin Sharing requests
Unit Testing - 

I'm sure I've forgotten some and did not get to looking at the future of where I want to go, but it's quite a nice start.

Thursday, February 4, 2010

MVC 2 reusable content options

There are 2 main sources of reusable or dynamic page content in Asp.net MVC. Partial Views(using Html.RenderPartial), and HtmlHelpers. Other options include Html.RenderAction, and Ajax calls (for adding content after the page is loaded).  Here's my understanding of them thus far.

Partial views can be strongly typed, but if the type is different from your hosting page(s) you lose the ability to do Html.DisplayFor, Html.TextBoxFor, and have it utilize that type's names. This can all be overcome by designing a custom DTO or ViewModel for the partial view, but that's more steps. Partial views internally work very similar to if they were inside a regular view, however the <%= %> does not seem to come up by default in intellisense. Instead, you get<%@ Assembly= %>. It's nice if you prefer to model your code in a similar way to how a view would look.

Another option is HtmlHelper, which is what you are using when you type Html.TextBox, or Html.TextBoxFor, etc... You write one of these by putting a static class in your project for extension methods. Then writing an Extension method that extends HtmlHelper like so

using System.Web.Mvc;
using BReusable; //For the Member class


public const string JavaScript = " < script type=\"text/javascript\" language=\"javascript\">\n";
public static string JqueryLoadForId(this HtmlHelper helper, Expression<Funcobject>> memberName,
                                  
string function)
{
    
return JavaScript + "$(function() {\n$('#' + '" + Member.Name(memberName) + "')\n." + function +
          
";\n});\n\n";
}

As you can see this code can start to look messy and probably suffers from pre-mature optimization. String.Format suffers a performance penalty, but using that would allow most of this code to sit in a .js or resource file instead of the poor formatting options that string literals leave us.

RenderAction is useful for returning one of the normal controller result types 
  • ViewResult – Represents HTML and markup.
  • EmptyResult – Represents no result.
  • RedirectResult – Represents a redirection to a new URL.
  • JsonResult – Represents a JavaScript Object Notation result that can be used in an AJAX application.
  • JavaScriptResult – Represents a JavaScript script.
  • ContentResult – Represents a text result.
  • FileContentResult – Represents a downloadable file (with the binary content).
  • FilePathResult – Represents a downloadable file (with a path).
  • FileStreamResult – Represents a downloadable file (with a file stream).
Ajax options
  • Ajax class
    • use AjaxOptions parameter to specify many options
      • Example:


        new AjaxOptions { 
                Confirm="Are you sure?",
                HttpMethod="POST",
                UpdateTargetId="divResultText",
                LoadingElementId="divLoading",
                OnSuccess = "myCallback"
            }
    • Ajax.ActionLink
    • using(Ajax.BeginForm()){}
  • javascript or jQuery Ajax calls to web methods

Wednesday, November 4, 2009

Configuring IIS on XP to use Asp.net 3.5, and MVC

Installing IIS (may require the XP cd or XP installer files to be available):

First Control Panel -> Add/Remove programs -> Add/Remove Windows Components
Select Internet Information Services (IIS), click Details. - Instructional site with screenshots

Out of the box Asp.net appears configured for .net 2.0 (at least it was on my machine where .net 2.0,3.5, and 4.0 were installed). So anything higher may not function. If your .net stuff isn't working try this next fix:

So to get it to run on my machine at work and at home I had to do the following:
  • C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>aspnet_regiis.exe -ua
  • C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>aspnet_regiis.exe -I
Here's a complete rundown on that part of the solution

Also in some cases your application folder may not be set up as an application:

control panel ->Administrative Tool-> Internet Information Services

select your application folder and on the directory or virtual directory tab in the Application Settings section, Application name would be greyed out, and a button on the right will say Create. Click this.


Finally to get MVC to function properly (this is a security risk in some respects I hear, but should be fine for private personal network testing:

 It's safer to do this to the specific MVC directory, but you can do it for the entire website

control panel ->Administrative Tool-> Internet Information Services

select your application folder ( Entire website if you prefer, which is less secure) , right click and go to properties. On the directory or virtual directory folder click Configuration.

Click Add. Click Browse on the new dialog that opened. Change the drop down type to dll files. Navigate to C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll and click open. then in the extension section type .*

I had to click in the Executable path textbox to have it expand the path into the textbox.

Take OFF the checkmark for Check that file exists.

Click ok on this dialog, and the remaining 2 that are open. - Source