@Shazwazza

Shannon Deminick's blog all about web development

Multi targeting a single .Net project to build for different framework versions

May 2, 2014 04:18

Consider this scenario:

  • I have a project that relies on ASP.Net MVC and currently this project is built against the .Net framework 4.0 and MVC 4
  • I want to support MVC 5 for this project
  • To support MVC 5, I’d need to upgrade the project to use .Net framework 4.5
  • This would mean that developers still running .Net framework 4.0 would no longer be able to use my updated project

I also don’t want to have to create different projects and assemblies that target specific MVC versions (in this case you’d end up with assembly names like MyProject.MVC4.dll and MyProject.MVC5.dll). I have seen this done with other solutions and it works but I feel it is not necessary unless your project is using specific functionality from a particular MVC version.

In my case my project will compile perfectly against MVC 4 and 5 without any codebase changes so I’m not actually targeting against a specific MVC version ( >= 4 ). If your project uses specific functionality from the different MVC versions I think you will have to output different assemblies like MyProject.MVC5.dll. In that case this post will probably help: http://blogs.korzh.com/2013/12/nuget-package-different-mvc-versions.html

The goal

The end result is that I want a single project file that has 4 build configurations:

  • Debug / Release
    • These will build against .Net 4.0
    • This will reference MVC 4
  • Debug-Net45 / Release-Net45
    • This will build against .Net 4.5
    • This will reference MVC 5

Each of these build configurations exports the same assembly name: MyProject.dll

I then want a single NuGet package which will install the correct DLL based on the .Net version that the user has installed.

How it’s done

Build configuration setup

To start with you’ll have a project that targets .Net framework 4.0 and references MVC 4 and 2 standard build configurations: Debug, Release.

First we need to create 2 new build configurations: Debug-Net45 and Release-Net45 and have these configurations output the DLLs to a custom folder. To do this we launch the Configuration Manager:

2014-04-30_2014

Create a new build configuration:

2014-04-30_2016

Enter the name of the new configuration and copy the configuration from it’s associated existing one. So Debug-Net45 would copy from Debug and Release-Net45 copies from Release.

2014-04-30_2021

Do this for both configurations – Debug-Net45 and Release-Net45.

Project setup

The next step requires manually editing your .csproj file since Visual Studio doesn’t want to normally allow you to do this. This is how you can configure each build type to target a different framework version. You’ll first need to find these 2 entries:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">

Inside each of these elements you need to explicitly tell these build configurations to use .Net framework 4.0 by adding this element:

<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>

Next you’ll want to copy/paste both of these property groups and:

  • Change the Condition to check for the new build configurations and to target .Net framework 4.5
  • Change the OutputPath to have a specific .Net 4.5 folder
  • Change the TargetFrameworkVersion to be .Net 4.5

You should end up with 2 new elements that look something like:

<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug-Net45|AnyCPU'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\Debug-Net45\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <ErrorReport>prompt</ErrorReport>
    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
    <PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Net45|AnyCPU'">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release-Net45\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
    <PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>

Next we need to do this for the MVC references or any framework specific references you need to target. In this example it is just the MVC references and you’ll need to wrap them with an ItemGroup element, you’ll end up with something like this to target the MVC 4 libs:

<ItemGroup Condition=" '$(Configuration)'=='Debug' Or '$(Configuration)'=='Release'">
    <Reference Include="System.Web.Helpers, Version=2.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll
      </HintPath>
    </Reference>
    <Reference Include="System.Web.Mvc, Version=4.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll
      </HintPath>
    </Reference>
    <Reference Include="System.Web.Razor, Version=2.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll
      </HintPath>
    </Reference>
    <Reference Include="System.Web.WebPages, Version=2.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll
      </HintPath>
    </Reference>
    <Reference Include="System.Web.WebPages.Deployment, Version=2.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll
      </HintPath>
    </Reference>
    <Reference Include="System.Web.WebPages.Razor, Version=2.0.0.0, .....">
      <Private>True</Private>
      <HintPath>
        ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll
      </HintPath>
    </Reference>
</ItemGroup>

Then we need to go ahead and copy/paste this block but target the different build configurations and then swap out these MVC 4 version references with the MVC 5 version references.

Nuget package restore

For this tutorial I’m assuming you are using Nuget to reference your MVC libs and an easy way to get Nuget to play reasonably with this setup is to enable Nuget package restore for your solution – right click on your solution and click the button:

2014-05-01_1625

Then in your packages.config file you can manually add the MVC 5 references:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.AspNet.Mvc" 
           version="4.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.AspNet.Razor" 
           version="2.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.AspNet.WebPages" 
           version="2.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.Web.Infrastructure" 
           version="1.0.0.0" targetFramework="net40" />
  
  <package id="Microsoft.AspNet.Mvc" 
           version="5.0.0" targetFramework="net45" />
  <package id="Microsoft.AspNet.Razor" 
           version="3.0.0" targetFramework="net45" />
  <package id="Microsoft.AspNet.WebPages" 
           version="3.0.0" targetFramework="net45" />
</packages>

Now when you build your solution under the different build configurations Nuget will automatically go and get the correct MVC versions based on the current .Net Framework version.

Thats it!

With all of this in place it should ‘just work’ :) You will notice some odd looking icons in your references list in Visual Studio but it’s nothing to worry about, Visual Studio is just confused because it isn’t familiar with how you’ve tricked it!

2014-05-01_1709

So now when you build using your different build configurations, it will output the correct DLLs to the correct build folders:

  • Both Debug & Release will output to /bin/Debug & bin/Release with MVC 4 libraries and the MyProject.dll will be compiled against .Net Framework 4.0
  • Both Debug-Net45 & Release-Net45 will be output to /bin/Debug-Net45 & /bin/Release-Net45 with MVC 5 libraries and the MyProject.dll will be compiled against .Net Framework 4.5

And here’s some proof:

2014-05-01_1714

2014-05-01_1715

If you are using some automated build processes with MSBuild you can then just target the build configuration names you’d like to export.

The Nuget setup is pretty simple since you can target your dependencies by framework:

<group targetFramework="net40">
<!--between 4 and less than version 5-->
<dependency id="Microsoft.AspNet.Mvc" version="(4.0.20710,5)" />
</group>

<group targetFramework="net45">
<!--between 4 and less than version 6 (who knows what'll happen then)-->
<dependency id="Microsoft.AspNet.Mvc" version="(4.0.20710,6)" />
</group>

Then for each of your lib files in Nuget you ensure that you output them to the framework specific /lib folders such as:

target="lib\net40"
target="lib\net45"

This process has been implemented for the Client Dependency Framework version 1.8.0 to support MVC 4 and 5 without having to change the assembly name.

Creating code-behind files for Umbraco templates

January 29, 2011 04:04
This post was imported from FARMCode.org which has been discontinued. These posts now exist here as an archive. They may contain broken links and images.
I’ve always had this idea in my head that one of the downfalls of using Umbraco when coming form standard ASP.Net web application was the missing code-behind files. You know, when you create a new web application and add an .aspx page to it it conveniently comes with a .cs and design.cs file. Most of the time I would even let the code-behind file inherit from my own custom Page/MasterPage implementation, e.g. a SecuredPage that comes with various properties and methods to handle authentication. Although Umbraco uses regular masterpages (if you haven’t turned it off in the web.config) all you get in the backoffice is the actual page template. Now, don’t get me wrong: I love the way Umbraco let’s you edit all aspects of your site via the backend and gives you the utmost flexibility and 100% control over the output, presented in a refreshingly simple manner. Yet sometimes you need a bit more, and it’s just another clear plus for Umbraco that you are able do the following without ever having to modify the core.

The 'aha' moment that it is actually quite easy to add code-behind files to Umbraco masterpages came to me when I had to port a quite big ASP.Net website to Umbraco. The website had grown organically over the years with lots of custom templates, user controls, etc. The site also had multi-language support, all of which was handled in the code-behind files of the pages. The goal was to get it over to Umbraco as quick as possible, then rework the functionality bit by bit. So I started by creating a new Umbraco site and ‘wrapped’ it in a web application project in Visual Studio.

 

1-28-2011 5-00-55 PM

[Please refer to the comments below to find more information on how to set this up in Visual Studio.]

After adding a couple of document types and templates in Umbraco the masterpages folder looks something like this:

1-28-2011 5-28-34 PM

The Root.master file is the main master page, Page1.master and Page2.master are nested master pages in Umbraco. I’ve included all three of them in the solution. Now it’s time to create the code-behind file: right-click on the masterpages folder and add three C# classes and name them Root.master.cs, Page1.master.cs and Page2.master.cs. The result should be something like this:

1-28-2011 5-29-38 PM

Visual Studio automatically groups them together, fantastic. Yet they are not really hooked up yet, VS does the grouping just based on file names. The master directive on Root.master currently looks like this:

<%@ Master Language="C#" MasterPageFile="~/umbraco/masterpages/default.master" AutoEventWireup="true" %>

To hook up the cs file we need to add the CodeBehind and Inherits attributes like so:

<%@ Master Language="C#" MasterPageFile="~/umbraco/masterpages/default.master" AutoEventWireup="true" CodeBehind="Root.master.cs" Inherits="Umbraco_4._6._1.masterpages.Root"%>

You should get an error at this point as the compiler complains that Root is not convertible to System.Web.UI.MasterPage, so we need to fix this in the cs file as well by making the class partial (necessary if you want to later add designer files as well) and inheriting from System.Web.UI.MasterPage. An empty Page_Load message can’t hurt as well:

using System; namespace Umbraco4_6_1.masterpages { public partial class Root : System.Web.UI.MasterPage { protected void Page_Load(object sender, EventArgs e) { } } }

You should now be able to switch between both files by pressing F7 in Visual Studio. Let’s try to add a Property and reference that from the template:

public string Message { get; set; } protected void Page_Load(object sender, EventArgs e) { Message = "All the best from your code-behind file!! :)"; }

and something like this on the template:

<div> <%= Message %> </div>

Now we just need to compile the project and navigate to a content page that uses the Root template to see the message.

 

Adding designer files

[As Simon Dingley pointed out below there is an even easier way to create the designer files: right-click on the master.aspx page and select "Convert to web application", which will create the .designer file for the selected item.]

We can also add a designer file to the duo to make things even better. After adding Root.master.designer.cs, Page1.master.designer.cs and Page2.master.designer.cs the solution looks like this:

1-28-2011 5-49-22 PM

Visual Studio is now rightfully complaining that it got duplicate definitions for the classes and even suggests to add the partial keyword, which we will quickly do. After that is all working and compiling nicely we need to give Visual Studio control over the designer files. That is easily accomplished by slightly modifying each .master file (e.g. by adding a single space to an empty line) and saving it, VS will do the rest for you. The most important thing this will do for you is to reference all controls you add to the template so they are available for use in the code-behind file.

Now let’s try to modify the message value from the code-behind of Page1 by adding

protected void Page_Load(object sender, EventArgs e) { ((Root) Master).Message = "Hello from the nested master page!"; }

to it. Browsing to any Umbraco page that uses the Page1 template will now show the new message.

Using an iPhone with the Visual Studio development server & Charles

June 11, 2010 23:26

Dave Ward did a good post recently on how to use the Visual Studio development server from a mobile devise such as an iPhone. But there’s a problem for us here, we use Charles which I have found to be a better than Fiddler (it’s also cross-platform so I can use it both on my Mac and Windows machines).

So after reading Dave’s post I decided to have a look at how to do it if you’re using Charles, and well it’s pretty darn simple.

I’d suggest that you read Dave’s post first as I’m going to assume that you have, I’m just going to point out what you need to do different for Charles.

Charles Configuration

The first thing you need to do is find out on what port Charles is running on, by default Charles is on port 8888, but you can find the settings under Proxy > Proxy Settings

charles-proxy-config

Next we need to configure the external access to the HTTP Proxy that Charles is running. This is something that Charles handles differently to Fiddler, it’s actually a lot more configurable as you can define individual IP’s or IP ranges for access.

To do this you need to navigate to Proxy > Access Control Settings

charles-access-control

Then you just need to click Add and enter the IP (or range) which you want to allow access to. I’ve just allowed access to the IP of my iPhone, which is 192.168.1.44.

Conclusion

The rest of Dave’s post is all you need to get this working, you connect to your computer from your external device in just the same way.

Hopefully this helps you out if you’re not a Fiddler user but want to be able to use a mobile device with Visual Studio’s development server.