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


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

Comments (25)

  1. Mike says:

    With .NET Standard 2.0 supporting interop with .NETFramework, which platform would be best to base your project on? By that I mean should I create the project in .NET core and reference Framework assemblies? or vice-versa, create the project in .NETFramework and reference core assemblies? or does not make a difference..

    1. I'd use .NET Core whenever is possible for your requirements (as it is cross-platform) and full .NET Framework (which might only work on Windows depending on the APIs being used, plus full .NET framework is heavier) only when you have no other choice because the API you want to use doesn't exist in .NET Core.
      Take into account that the more pure .NET Core you use, the easier you'll have it for modern scenarios. Not just cross platform across Linux and Windows but also environments on Docker containers which are a lot better/lighter when running on pure .NET Core images than full Windows Containers.
      Sure, if your .NET Framework libraries don't use any particular Windows API, it might also run okay on Linux, but, the more libraries you have on .NET Standard Library and .NET Core projects, the better for the future. 🙂

  2. Nima Ara says:

    Thanks Cesar for the very helpful post.

    Just to be clear, when I "dotnet publish" the sample HelloWorld .NET Core Console App supporting multiple frameworks, I can see 2 folders one for ".NETCoreApp,Version=v1.0" and the other for ".NETFramework,Version=v4.5.2/win7-x64" in the case of the ".NETCoreApp" can I assume that the above just produced a Self-contained .NET Core Applications? If yes then how is this different from Scott's solution at: http://www.hanselman.com/blog/SelfcontainedNETCoreApplications.aspx

    Thank you.

    1. By default, just by using "dotnet publish" you are NOT creating a self-contained app including the framework, you are just publishing your binaries.
      If you'd like to include the .NET Core Framework as part of it, you need to specify additional config.
      To be generated an executable rather than a DLL, to allow the application to be self-contained (independent of the framework installed on the machine), you must remove the element “type”: “platform”:and you must add the runtimes that the application should be compiled by add the tab runtimes.
      Take a look here:
      https://blogs.msdn.microsoft.com/luisdem/2016/10/11/net-core-how-to-publish-a-self-contained-application-exe/

      Finally, take into account that the way you do it will be different when using .csproj projects with VS 2017 which won't use project.json.

      1. Nima Ara says:

        Brilliant, crystal clear.

  3. Pavel Biryukov says:

    Thanks, very interesting article.

  4. Sekar says:

    Hi,
    This .net core will not go with SharePoint. Then what is the point of having .net core??? So many days trying to get nuget which will support .net core.. Before releasing any version, please make sure it supports all the Microsoft products which run on .net framework. Otherwise, there is no point .net core or some product got released. I am not trusting this .net core... Its two weeks frustration...

    1. @Sekar. .NET Core is a brand new framework, cross-platform that runs on Linux. It's been created from scratch and many legacy/existing products still don't support it. If we had to wait for all existing products to support it, it would be released way too late.
      Take a look to my blog post where you can see when to use it and when NOT to use it.
      https://blogs.msdn.microsoft.com/cesardelatorre/2016/06/27/net-core-1-0-net-framework-xamarin-the-whatand-when-to-use-it/

  5. Petar says:

    So I can not target netstandard1.6 from Console App, because it's not framework:

    “frameworks”: {
    “netstandard1.6”: {
    }
    }

    Netstandard is only available for libraries?

    1. @Petar - Right, .NET Standard is only available for libraries to be shared across multiple frameworks, "ala PCL" (Portable Class Library) because it is the evolution of the PCLs.
      An application/program cannot run/support multiple frameworks at the same time, you need to decide what framework is using your application so it builds and runs accordingly.

  6. G. Auersberg says:

    Providing the same TFM for traditional ASP.NET and .NET Core applications is a very bad decision. We've developed a library for ASP.NET applications (100,000+ installations) which gets the HTTPContext from the web application. Traditional ASP.NET uses System.Web.HttpContext while ASP.NET Core (MVC Core) has it in Microsoft.AspNetCore.Http.HttpContext. If we now create a NuGet package with a "net451" both, traditional ASP.NET and ASP.NET Core applications can reference the library, but if our library references System.Web.dll it fails in ASP.NET Core and if the library references Microsoft.AspNetCore.*.dlls it fails in MVC5. It should be possible for NuGet/project.json/msbuild to check if a library uses System.Web.dll or Microsoft.AspNetCore.*.dlls. This could be accomplished with a simple flag on the moniker in the NuGet package.

    1. @G. Auersberg - This was a deliberate choice when the product was still in Preview. Moving forward we might not support ASP.NET Core on .NET Framework anymore (Still a TBD decision, nothing confirmed) as you'll be able to have interop between .NET Core and traditional .NET Framework when using .NET Standard 2.0, so you could consume traditional .NET Framework libs from an ASP.NET Core app running on .NET Core.
      The reason we did what you mentioned was because .NET Core wasn’t "compatible enough". With .NET Core vNext and .NET Standard 2.0 we think we’re compatible enough to cut these ties. It does remove the need to introduce another TFM. If you'd like further insights about this topic, I can put you in direct contact with my peers owning this area, like Immo Landwerth. If that's the case, send me an email to CESARDL at Microsoft.com and I'll add Immo to the thread.

  7. How can I publish my library supporting multiple frameworks to nuget in one single package?

    1. @ Robert Te Kaat - If you want to support multiple frameworks, your NuGet package has to be a Library based on .NET Standard or PCL (Portable Class Library) although I recommend .NET Standard. PCL is the "old" .NET Standard.

  8. Brandon says:

    So if you run .NET Core and target frame 4.6.2 because you want to use those libraries does that mean you completely lose the speed advantage you get from using .NET core as well as the ease of deployment?

    1. @Brandon - Performance in ASP.NET Core running on .NET Framework 4.6.2 (CLR) is almost the same than running ASP.NET Core on top of .NET Core (CoreCLR). What you'd lose is cross-platform and modular/smaller memory footprint.
      Also, performance of ASP.NET Core running on Kestrel is a lot better than performance on IIS. However, for security reasons you still might want to use IIS but only as HTTP gateway pointing to your Kestrel-based ASP.NET Core services.

  9. Matt says:

    This was very helpful.

    Are there any examples of using a .NET Core library from a traditional .NET Framework application?

    Or is that like using a traditional .NET Framework library from a .NET Core app, where you need to run on the same kind of framework (full vs Core) as the framework of the library you are trying to use?

    Hopefully that makes sense.

    Thanks

    1. @Matt You'll be able to consume a .NET Core Lib from a traditional .NET Framework app when we release .NET Standard 2.0, very soon. For now, a .NET Core Lib can be consumed only from a .NET Core app.

  10. ilker says:

    I think most people are trying to use both new and old framework (core 1.0 and 4.6.x) in the same time since not everything is ready or hard to convert in a short time.

    One example could be I start with Core 1.0 Web application and I want to send email. So I have to add System.Net.Mail namespace from .Net 4.6.1. In this case project doesn't compile.

    Another example, I have .Net Core 1.0 Web application and I want to include .Net 4.6.1 Library project which contains EF 6.

    It would be good to see some working examples. Still most of the term are confusing to solve problems above from explanations.
    Thanks.

    1. @iker - I you want to consume traditional .NET libraries, you need to be using ASP.NET Core running on top of the traditional .NET Framework (CLR). If your ASP.NET Core app is running on the CoreCLR, then you cannot. For that scenario that you mention, "brown-field", the recommended approach would be the first that I mentioned, so using ASP.NET Core running on top of the traditional CLR (.NET Framework CLR).

      1. Juan Pablo Yiguerimián says:

        Does it mean that if I want to include a .NET Framework 4.5 library in my .NET Core app, it won't run on Linux and Mac ?

        I has this project.json

        Dependencies
        "Microsoft.NETCore.Portable.Compatibility": "1.0.1",
        "NUnit": "3.4.1",
        "dotnet-test-nunit": "3.4.0-beta-2",
        "AllureCSharpCommons": "0.2.0"

        test an frameworks
        "testRunner": "nunit",

        "frameworks": {
        "netcoreapp1.0": {
        "imports": [
        "portable-net45+win8",
        "net45"
        ],
        "dependencies": {
        "Microsoft.NETCore.App": {
        "version": "1.0.0-*",
        "type": "platform"
        }
        }
        }
        },

        And running this code

        private AllureCSharpCommons.Allure _lifecycle;

        [TestCase]
        public void NewTest()
        {
        _lifecycle = AllureCSharpCommons.Allure.Lifecycle;
        Assert.AreEqual(true, true);
        }

        I got this error

        Errors and Failures
        1) Error : web_importer_tests_01.Tests.AllureTests.NewTest()
        System.MissingMethodException : Method not found: 'Boolean System.Reflection.Assembly.op_Equality(System.Reflection.Assembly, System.Reflection.Assembly)'.
        at log4net.Core.LoggerManager.GetRepository(Assembly repositoryAssembly)
        at log4net.LogManager.GetRepository()
        at AllureCSharpCommons.Utils.Logger.Setup()
        at AllureCSharpCommons.Allure..ctor()
        at AllureCSharpCommons.Allure.get_Lifecycle()
        at web_importer_tests_01.Tests.AllureTests.NewTest()

        1. @Juan Pablo. That's right. A .NET Framework 4.5 library is based on Windows, so many APIs would crash on a Linux machine.
          However, pretty soon, when we release .NET Standard 2.0 (kind of the PCL evolution) you'll be able to consume a traditional .NET Lib from a .NET Core app thanks to a new shim we're building (like Interop), as long as in that .NET library you are not using any specific Windows API but just your plain .NET code, of course.
          With .NET Standard 2.0 viceversa is also true. You will also be able to consume a .NET core Lib from a traditional .NET Framework app.

  11. Francisco says:

    Un artículo muy aclaratorio. !Gracias César!.

  12. This is such a nice explanation to showcase the .NET Core capabilities. Loved it.

Skip to main content