I upgraded an ASP.NET MVC 3 project to MVC 4, and upgraded to VS 2012 at the same time. Everything compiled fine, and worked fine in the browser, but the intellisense was totally broken.

I couldn't for the life of me figure it out. Everything in web.config looked right, the correct assembly versions were referenced, etc.

Finally, I realized that the issue was in the Views\Web.config. The version numbers of assemblies there were pointing to MVC 3 and WebPages 1, rather than MVC 4 and WebPages 2.

To solve the issue, change all the version numbers in that file appropriately (4.0.0.0 for System.Web.Mvc references, and 2.0.0.0 for System.Web.WebPages.Razor references).

I just ran into an issue where DataBinder.Eval was exploding with an exception like this:

'System.Dynamic.ExpandoObject' does not contain a definition for 'Id'.

The problem here is that DataBinder.Eval uses either reflection and type descriptors to bind to properties. ExpandoObject (and dynamic types) don't support reflection. ExpandoObject doesn't implement ICustomTypeDescriptor either, leading to an exception when attempting to bind to an ExpandoObject.

After half an hour of googling, I found Bertrand Le Roy's excellent example of building custom type descriptors: Fun with C# 4.0's dynamic. I'm not sure why it took me so long to find it, so I figure I'll give him a bit of google juice and add some more keywords.

Also, that sample is rather out of date, so you'll have to do a couple of tweaks to make it work in .NET 4/4.5 (see below). Once that's done, you will have a bindable wrapper object that takes an ExpandoObject and can be directly bound to. Given and ExpandoObject "dyn" with a property named "Id", you can do this:

var bindableObj = new DynamicTypeDescriptorWrapper(dyn);

var value = DataBinder.Eval(bindableObj, "Id");

Changes required

First, change DynamicHelper.cs to the following:

public static class DynamicHelper {
    public static object GetValue(object dyn, string propName) {
        // Warning: this is rather expensive, and should be cached in a real app
        var GetterSite = CallSite<Func<CallSite, object, object>>.Create(
                Binder.GetMember(CSharpBinderFlags.None,
                    propName, 
                    dyn.GetType(),
                    new [] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }
                    ));

        return GetterSite.Target(GetterSite, dyn);
    }

    public static void SetValue(object dyn, string propName, object val) {
        // Warning: this is rather expensive, and should be cached in a real app
        var SetterSite = CallSite<Func<CallSite, object, object, object>>.Create(
                Binder.SetMember(CSharpBinderFlags.None,
                    propName, 
                    dyn.GetType(),
                    new [] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant |
                                            CSharpArgumentInfoFlags.UseCompileTimeType, null) }
                    ));

        SetterSite.Target(SetterSite, dyn, val);
    }
}

Then, change GetValue and SetValue in DynamicPropertyDescriptor as follows:

public override object GetValue(object component) {
    if (component is DynamicTypeDescriptorWrapper)
        component = ((DynamicTypeDescriptorWrapper)component).GetPropertyOwner(this);

    if (_owner != component) throw new InvalidOperationException("GetValue can only be used with the descriptor's owner.");
            
    return DynamicHelper.GetValue(component, _propertyName);
}

public override void SetValue(object component, object value) {
    if (component is DynamicTypeDescriptorWrapper)
        component = ((DynamicTypeDescriptorWrapper)component).GetPropertyOwner(this);

    if (_owner != component) throw new InvalidOperationException("SetValue can only be used with the descriptor's owner.");
            
    OnValueChanged(component, EventArgs.Empty);

    DynamicHelper.SetValue(component, _propertyName, value);
}

The WiX docs on the topic (Integrating WiX Projects Into Daily Builds) give a good starting point, but are incomplete, at least for the recent builds. Two additional things they forget to mention: WixToolPath needs to be an absolute path for the Wix.targets project to function properly, and WixExtDir needs to be set to WixToolPath.

To build WiX from the binaries with MSBuild, do the following (requires MSBuild 4):

  1. Snag the binaries from the latest weekly build: WiX Weekly Releases
  2. Extract them into a folder you can access with a relative path inside your source control root
  3. Edit all of your .wixproj files and do the following snippet before the <Import Project="$(WixTargetsPath)" > tag:
      <WixToolPath>$([System.IO.Path]::GetFullPath('RELATIVEPATHTOWIX\wix\'))</WixToolPath>
      <WixTargetsPath>$(WixToolPath)Wix.targets</WixTargetsPath>
      <WixTasksPath>$(WixToolPath)wixtasks.dll</WixTasksPath>
      <WixExtDir>$(WixToolPath)</WixExtDir>
    </PropertyGroup>
    
  4. Update the RELATIVEPATHTOWIX to be a relative path that points to the WiX binaries you extracted in step 2.

Sometimes my application has a section with normal ASP.NET requests that doesn't go through the ASP.NET MVC pipeline. I still want to integrate it with the ASP.NET MVC section of my app, using the same master page for example. The ViewMasterPage validates to make sure that the page being rendered is a ViewPage with the same type, so you can't use a normal page with a ViewMasterPage. If you run a ViewPage without an ASP.NET MVC context, all of the html helper methods will throw NullReferenceExceptions.

The solution to this issue is to make your page inherit from ViewPage, and then spoof the ASP.NET MVC context before the page renders. This will allow the page to work with a normal ASP.NET request (no ASP.NET MVC pipeline), but also use the same ViewMasterPage used for the rest of your site.

Here's what to do:

  • Make your page class inherit from ViewPage instead of Page.
  • Add the following method to your page:
    void SpoofMvc()
    {
        ControllerContext ctx = new ControllerContext();

        ctx.RouteData = new RouteData();

        ctx.HttpContext = new HttpContextWrapper(Context);
        ctx.RouteData.Values.Add("controller", "TightContent");
        ctx.RouteData.Values.Add("action", "View");

        this.Html = new HtmlHelper(new ViewContext(ctx, new WebFormView(this.Request.FilePath), ViewData, new TempDataDictionary(), Response.Output), this);
    }
  • Call the method before the page is rendered. Page_Load or Page_PreRender should work.