50

I'm building a class library and I will deploy it a NuGet package, which lets me choose different assemblies to be added as references based on the .NET framework version of the project it's added to. This is a very nice feature, but what I'm wondering is whether it is possible to have a single class library project, and build it against mulitple versions of the .NET framework?

I'd rather avoid having:

MyLibrary40.dll and MyLibrary45.dll

if possible, because the two projects would have to share a lot of code. The 4.5 version will be offering async functions, which is a 4.5 feature.

Does anyone know what the best approach for this is? Can I use multiple build configurations? Or must I go down the separate project route?

If I was working in C++ I'd probably use multiple configurations and #if blocks around the functions that are only supported in one configuration, but I worry this would lead to me having two assemblies with the same name that do different things.

Thanks in advance!

Mark Bell
  • 28,985
  • 26
  • 118
  • 145
Dave Kerr
  • 5,117
  • 2
  • 29
  • 31
  • So you want to target different versions, within one assembly? – Lars Kristensen Jul 17 '13 at 12:50
  • this is the best way to get BadImageException :) – ilansch Jul 17 '13 at 12:52
  • But, i have found a work around to compile, you can look at the links i put in my question, they might help in your journey - http://stackoverflow.com/questions/15549011/project-reference-work-around-net-4-5-and-net-3-5 – ilansch Jul 17 '13 at 12:53
  • 2
    @LarsKristensen, I want to have one project file but be able to produce two assemblies - one that supports .NET 4.0 and one that supports .NET 4.5 and adds some async functions - without having to create separate projects.. – Dave Kerr Jul 17 '13 at 12:57
  • I asked this also, no strict answer was given, maybe you will have better answers then i had, what you need is configuration manager.. http://stackoverflow.com/questions/15321757/how-to-build-same-project-with-multiple-configuration-c-sharp-visualstudio2012 – ilansch Jul 17 '13 at 13:02
  • this might help also http://stackoverflow.com/questions/5006397/targetting-multiple-net-framework-versions-by-using-different-project-configura?rq=1 – ilansch Jul 17 '13 at 13:05
  • I believe you will have issues if you try to compile code as 4.0 that has 4.5 keywords / method calls in it that do not exist. – tsells Jul 17 '13 at 13:39
  • @Geek you can do it, by following the links i provided, BUT its not safe, once you be in lower run time, he will get bad image exception or any other exception when calling the 4.5 operation – ilansch Jul 17 '13 at 14:47
  • My point was it won't even compile if you try to use the same project for multiple assemblies. If you try to compile the following program with 4.0 - it will fail. class Program { static void Main(string[] args) { DoStuff(); } private static async void DoStuff() { int myvalue = await GetStuff(); } private static async Task GetStuff() { await Task.Delay(5000); return 0; } } – tsells Jul 17 '13 at 17:47
  • If you compile it to the higher version then try the redirect - agreed it will blow up at run time. My recommendation would be to have multiple projects or not use the 4.5 features. – tsells Jul 17 '13 at 17:49
  • Thanks to all for the comments, I've gone with the solution below! – Dave Kerr Jul 18 '13 at 13:54
  • @tsells: That's what [conditional compilation](http://stackoverflow.com/questions/2923210/conditional-compilation-and-framework-targets) should be used for. – O. R. Mapper Jun 05 '16 at 16:37

4 Answers4

40

You will at least need one VisualStudio Solution with 2 projects (one for .net 4 and one for .net 4.5).

Add all codefiles to the .net 4-project and in the other project you add the code files as link (use "Add Existing Item..."-Dialog and chose Add as link)

Now you add all codes and classes for .NET 4.5 to your 4.5-project.

Additionally you should define your own compiler switches (conditional compilation symbols) to your projects. Like NET4 for your .net 4-project and NET4.5 to your .net 4.5-project)

You set the switches in the project settings under Build->General->Conditional Compilation Switches

In your code you can use the switches as follows to generate code for .NET 4 or .NET 4.5

#if NET4
  // code only for .NET 4
#endif

// code for all framework versions.

#if NET45
  // code only for .NET 4.5
#endif
Jehof
  • 34,674
  • 10
  • 123
  • 155
  • 5
    Thanks very much, this is indeed the way I have gone. I have a .NET Framework 4.5 Class Library called 'JsonClient' and it has all of the features. The .NET 4.0 version is called 'JsonClientLite' and includes exactly the same code, but instead of using conditional compilation, I have a separate file for each class with Async methods (so I have JsonClient.cs in both, JsonClientAsync.cs only in the main one) - I use shortcuts to the files in the .NET 4.0 one, just like you described. I'm happy with how this works, it's seems a clean way to go - thank you very much for your answer! – Dave Kerr Jul 18 '13 at 13:52
  • 1
    I wrote https://github.com/CADbloke/CodeLinker to solve this - it populates a new project with items from an existing project. – CAD bloke Jun 23 '17 at 13:47
17

A simple approach is to add another .csproj file in the same folder, and configure it to build a different framework version. This avoids having to add links to files, as both projects are essentially views over the same folder structure.

Say you have the structure:

- MyLibrary\
  - MyLibrary.sln
  - MyLibrary\
    - MyLibrary.csproj
    - Program.cs

Duplicate MyLibrary.csproj to the same folder and edit to change a few things:

  • <ProjectGuid> just make a new GUID for this element's value
  • <TargetFrameworkVersion> specify the alternative version here, eg: v4.5 or v3.5
  • <OutputPath> (for Debug and Release) set this to a unique path, such as bin\Debug\net45 and bin\Debug\net45, to allow each project's output to end up in a unique location

You must also add a new element to the non-conditional <PropertyGroup> element, so that the two projects don't collide in the obj folder during parallel builds. This is important, and protects against weird race condition bugs.

<PropertyGroup>
  <BaseIntermediateOutputPath>obj\net45\</BaseIntermediateOutputPath>

Finally, add this new project to your existing solution.

This approach works hand in hand with defining compilation switches such as NET35 and NET45, and using #if NET35 / #endif directives.

Two open source projects that use this technique are MetadataExtractor and NetMQ. You can refer to them in case you hit trouble.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • 1
    Is the answer by @DrewNoakes still the way to go if you use Visual Studio 2015? Or are there better ways to do this nowadays? Just wondering – Jordy van Eijk May 11 '16 at 10:05
  • 1
    I use this in Visual Studio 2015. However once the xproj/project.json stuff lands, that'll probably be a better way of targeting multiple platforms. – Drew Noakes May 11 '16 at 10:35
  • Thanks for the quick answer... I will keep that in mind. – Jordy van Eijk May 11 '16 at 11:52
  • "Duplicate MyLibrary.csproj to the same folder" - this sounds suspiciously as if any changes (added files) to one `.csproj` file would have to be manually done in the other, as well (?), which sounds like a rather error-prone approach. – O. R. Mapper Jun 05 '16 at 16:47
  • 1
    @O.R.Mapper, yep, it's not perfect. If you're lucky, one project will fail to build. Otherwise you may miss a file out. However in some cases you need to -- perhaps you provide a shim class for something that's missing in one target version. If you know of a better solution, I'm all ears. – Drew Noakes Jun 05 '16 at 19:53
  • 2
    @O.R.Mapper If you add any files using the accepted answer you still will have to remember to link them in any of the other projects too. – Joel McBeth Jun 15 '16 at 18:42
  • @DrewNoakes: That is somehow correct, but I would probably still prefer to exclude the *file content* declaring the shim class by means of conditional compilation rather than have different project files for different target versions. – O. R. Mapper Jun 16 '16 at 10:15
  • @jcmcbeth: Indeed, the accepted answer provides no advantage in this respect. – O. R. Mapper Jun 16 '16 at 10:17
  • If your project uses NuGet packages for dependencies this causes a conflict with the packages.config file (since it contains a reference to the version your project needs, and it cannot have any other name besides 'packages.config'). I can't tell if this will cause any issues though because a NuGet package restore will still pull down all versions available. – mlhDev Jun 28 '18 at 16:34
5

Old question I know, and this wasn't a suitable answer at the time... but now it is possible to use a Shared Project, which is logically the same as adding a project as link rather than each file individually.

Stephen Drew
  • 1,415
  • 19
  • 31
  • Neat idea, but it has a lot of limitations, the most obvious being your class library can't reference anything that isn't also a Shared Project. Which makes sense, I suppose, but until every NuGet package is a Shared Project, probably not useful in most circumstances. – mhenry1384 Mar 24 '16 at 14:46
  • Hmm, not sure what you are referring to here. I can create a shared library A and a class library B that references A and Newtonsoft NuGet package. I can then write code inside A that uses e.g. JObject. This is true in VS2015 as well as VS2017 – Stephen Drew Mar 31 '17 at 23:10
1

If it is now, you can create "SDK style project." MSDN

hdefu
  • 11
  • 3
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/29847105) – Procrastinator Sep 17 '21 at 05:39
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-ask). – Community Sep 17 '21 at 06:02