Running .NET Core apps on multiple frameworks and What the Target Framework Monikers (TFMs) are about

Avatar

Cesar

image

In .NET Core 1.0 apps (either ASP.NET Core apps or Console apps, as of today) there are new possibilities like being able to run your app (like an ASP.NET Core app) on top of the .NET Core Platform or on top of the traditional .NET Framework 4.5.x which is critical for many enterprise apps that still might not have all the libraries/components compiled for .NET Core available (custom or third party).

In any case, when working with the project.json and the supported frameworks for your app can be a bit unclear because of the multiple possibilities available through project.json.

The bottom line is summarized in these three bullets:

  • The “framework” section in project.json specifies the framework or list of frameworks supported by your app.
  • The “imports” section in project.json is a way to use packages/libraries that are not based on the same version than your target Core platform version, such as “dnxcore” and portable-* libs, as shown below in one of the examples. It is important to use “imports” only for versions of .NET Core and PCL (Portable Class Libraries). Using it with TFMS from the traditional .NET Framework can cause issues or malfunction. 
  • When supporting multiple frameworks in your app, you can selectively run your app on top of one or the other framework by selecting that default framework from Visual Studio or from the command line (as explained below). 

 

As a side note, take into account that Windows 10 UWP apps also run on .NET Core, but you don’t have any alternative about changing the runtime. .NET UWP apps simply use .NET Core underneath and it is transparent for you.

 

What are the Target Framework Monikers (TFMs)

The Target Framework Monikers are IDs of the type framework+version that you can target from your apps in .NET Core and ASP.NET Core.

As examples (there are more), you can use:

– “netcoreapp1.0” For .NET Core 1.0

– “net45”, “net451”, “net452”, “net46”, “net461” for .NET Framework versions 

– “portable-net45+win8” for PCL profiles

– “dotnet5.6”, “dnxcore50” and others, for older .NET Core preview versions (Before .NET Core 1.0 RTM and .NET Core RC2 were released)

– “netstandard1.2”, “netstandard1.5”, etc. for .NET Standard Platform monikers.

The table below defines some examples of the latest frameworks (as of late June 2016) that you can use and how they are referred to and which version of the .NET Standard Library they implement.

image

 

Those are the latest framework versions as of today (Late June 2016), but there are quite a few other TFMs that you can review in this TFMs List.

In the examples below I’m using “netcoreapp1.0” when my app is targeting the .NET Core 1.0 and “net452” when my app is targeting the traditional .NET Framework (v4.5.2).

.NET Platform Standard

To simplify the references between binary-compatible frameworks, the .NET Platform Standard was introduced. This allows the definition of a single moniker to reference a combination of binary compatible frameworks.

Note that .NET Platform Standard Monikers (TxM instead of TFM, because they can target several frameworks) are usually used just on Libraries/Packages to specify what frameworks support each library (in a comparable way to PCLs that can support multiple frameworks), but you usually don’t use “Platform Standard” monikers in a Console Library or ASP.NET app project.json, which just needs to state what specific frameworks is the app going to build and run on.

As simple rules to follow:

App Developers: You target the platform TFM you’re writing for ( netcoreapp1.0, uap10.0 ,  net452 ,  xamarinios , etc.).

Package/Library Authors: Target the lowest  netstandard  version you can. You will run on all platforms that support that  netstandard  version or higher

In this example I won’t use any Platform Standard moniker (only specific framework monikers), but I would if I were using a library/package in addition to the main console app.

Running a .NET Core app on .NET Core

You can specify the framework you want to run your app on by specifying that in the project.json in your project.

For running on the CoreCLR you need to configure the “frameworks” key in your project.json like the following:

“frameworks”: {
  “netcoreapp1.0“: {
  }
}

However, in most of the current templates and sample apps (June/July 2016 with .NET Core 1.0 just released) you’ll see it as the following:

“frameworks”: {
  “netcoreapp1.0”: {
    “imports”: “dnxcore50”
  }
}

This is just because by having the “dnxcore50” in the imports section you can also reference Preview .NET Core libraries that were created until .NET Core RC1 (using the “dnxcore50” moniker). This is one example of using “imports”.

If using “imports” to reference the traditional .NET Framework, there are many risks when targeting two frameworks at the same time from the same app, so that should be avoided.

At the end of the day, “imports” is smoothening the transition from other preview frameworks to netstandard1.x and .NET Core.

 

Running a .NET Core app on the traditional .NET Framework 4.5.x

If you just want to run your .NET Core app (like an ASP.NET Core Web API service) on the traditional .NET Framework, you can do it by changing adding/changing the moniker in the “frameworks” section.

Running your .NET Core app on the traditional .NET Framework 4.5.2

You need to have the “frameworks” key in your project.json configured like the following:

“frameworks”: {
  “net452”: {
  }
}

When you have a single target framework, like in the examples above, when running the app, logically, it will directly run on the configured framework.

But, what if I’m targeting several frameworks? Let’s see that in the following section.

 

Supporting several frameworks/runtimes by your .NET Core app

In .NET Core apps, you can target multiple frameworks from the same application (not at the same time, though).

When there are incompatibilities (like something from Windows that is only supported by the traditional .NET Framework), you can use pre-compiler directives, like:

 

#if NET452
  // access something that requires the traditional .NET Framework
  // like some Windows related stuff
#endif

As you might guess, the way you add additional frameworks targeted by your app is as easy as adding more framework-monikers to the “frameworks” section in your project.json. That’s why that section has a name in plural and is therefore a possible list of frameworks! 😉

 

Creating a sample HelloWorld .NET Core Console App supporting multiple frameworks

Let’s do a simple end-to-end example starting from a hello-world console app.

Create a hello-world console app with the VS template called “Console Application (.NET Core)”…

image

 

Modify the project.json to support multiple frameworks

When you just created the project, you’d have a similar project.json to the following below:

{
  “version”: “1.0.0-*”,
  “buildOptions”: {
    “emitEntryPoint”: true
  },

  “dependencies”: {
    “Microsoft.NETCore.App”: {
      “type”: “platform”,
      “version”: “1.0.0”
    }
  },

  “frameworks”: {
    “netcoreapp1.0“: {
      “imports”: “dnxcore50”
    }
  }
}

That means that you initially only support your app running on the .NET Core platform and runtime (“netcoreapp1.0”), that you might reference packages of older preview versions of the .NET Core (“dnxcore50” comes from the initial branding of .NET Core 5.0 in preview/beta that changed to 1.0 as .NET Core is a huge shift in .NET).

It also means that your project has general “dependencies”, initially to “Microsoft.NETCore.App”.

If I want my app to support several frameworks/runtimes (.NET Core 1.0 with the CoreCLR & .NET Framework 4.5.2 with the CLR), my project.json would be now something like:

{
  “version”: “1.0.0-*”,
  “buildOptions”: {
    “emitEntryPoint”: true
  },

  “dependencies”: {
  },

  “frameworks”: {
    “netcoreapp1.0”: {
      “imports”: “dnxcore50”,
      “dependencies”: {
        “Microsoft.NETCore.App“: {
          “type”: “platform”,
          “version”: “1.0.0”
        }
      }
    },
    “net452”: {
    }
  }

Note that I added the “net452” moniker to my list of frameworks, but I also needed to move the “Microsoft.NETCore.App” dependency from the “global dependencies” that was ok when I had a single framework, to the framework specific dependencies under “netcoreapp1.0”, because the dependency to Microsoft.NETCore.App is not supported by the traditional .NET Framework 4.5.2.

 

ASP.NET Core 1.0 project.json supporting multiple frameworks

For a similar case, but in this case having an ASP.NET Core 1.0 project.json supporting multiple frameworks check this simple gist I created:

https://gist.github.com/CESARDELATORRE/9d6a236f28056fa7bcd7c12c45deae22 

 

Write the Platform Specific Code

Now, let’s add the platform specific code, in this case, specific for Windows:

For instance, I wrote the following code in my main function that is writing to the Windows Event Log through .NET Framework classes, using the pre-compiler directive that detects the framework version:

image

 

Run de app on a chosen framework

Finally, since I have two possible frameworks to run my app on… How can I specify which framework do I want to run it? You need to “say” which framework you want to run on when executing the app, right?. 🙂

That is something that can be specify in the execution environment, therefore:

1. If running the app from Visual Studio:

Select the “by default framework” in the drop-down menu from VS like below:

image

 

That list will depend on how many frameworks you are supporting in your app. If you have a single framework, then you won’t see that option.

 

2. If running the app from its cmd environment:

If you’re running your application from the command line interface (dotnet run), you can also specify the selected framework by using the -f directive, as the example below:

dotnet run -f NET452

 

3. When I run my example app, In this particular case, only when I run it on top of the .NET Framework 4.5.2, I can see debug the “Windows code” and see that it generated my custom event in the Windows Event Log:

image

 

If this app were running on the .NET Core Platform, let’s say on a Linux box  or a Mac, this code won’t be executed, but the app would still be running on Linux or MacOS.

 

Important: Take into account that when you run your .NET Core app (like this console app or an ASP.NET Core web-api) on top of the traditional .NET Framework you will lose the cross-platform and lightweight/modular capabilities from .NET Core .

Enjoy .NET Core! 🙂

 

Download – HelloWorld Console app Targeting Multiple Frameworks

Here’s the code at GitHub, so you can clone the repo and run it in VS or VS Code.

Although, it cannot be more simple. The C# code is boiler-plate code. The only interesting file in this example is the modified project.json file. 😉

https://github.com/CESARDELATORRE/NETCoreConsoleApp.TargetingMultipleFrameworks 

 

Additional resources

TFMs (Target Framework Monikers)

Here’s a gist from David Fowler in the .NET team that might give you more insights about TFMs (Target Framework Monikers):

https://gist.github.com/davidfowl/8939f305567e1755412d6dc0b8baf1b7 

 

The .NET Standard Library and the .NET Platform Standard:

https://github.com/dotnet/corefx/blob/master/Documentation/architecture/net-platform-standard.md 

 

.NET Platform Standard explained with C# Interfaces and Implementations Smile by David Fowler:

https://gist.github.com/davidfowl/8939f305567e1755412d6dc0b8baf1b7 (With a great discussion from many folks in it!)

 

Deprecated Monikers

image

Further info in GitHub about monikers, here

Avatar
Cesar De la Torre

Principal Program Manager, .NET

Follow Cesar   

No Comments.