Creating your first shared library in .NET Core
Sharing libraries between projects is a fundamental requirement in all but the most simple applications. This post looks at how things have changed with .NET Core. We look at the different types of class library available, how to create a simple library and the best way to reference shared libraries from other applications.
Choosing a project type
When you come to create a class library for .NET Core, you may be a little confused to find a couple of different options that seem to be applicable.
In the new project dialog, under .Net Core, we have the Class Library (.NET Core) option:
In the .NET Standard folder, we also have Class Library (.NET Standard)
Both options produce class library projects that can be referenced from .NET Core applications but .NET Standard libraries can be compatible with full .NET framework applications as well as Xamarin and anything else that adheres to the .NET Standard.
For this reason, it is recommended that you choose the .NET Standard option unless you need access to functionality that is not part of the .NET Standard and only available in .NET Core.
If you ever need to switch between the two then you only need to change the TargetFramework in the csproj file.
.NET Standard class libraries reference netstandard:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
</Project>
.NET Core class libraries reference netcoreapp:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
The dotnet CLI
If you are using the dotnet command line tools instead of the Visual Studio GUI then things are less confusing.
dotnet new classlib
Typing the above command will result in a .NET Standard class library. As far as I can tell, there are not any CLI templates that generate the other type of class library.
Choosing a Target Framework Version
I am not going to explain in detail what the .NET Standard is because there is plenty of existing info available. All I will say is that .NET Standard is essentially a versioned specification, with later versions adding more APIs. Higher versions are not allowed to remove functionality so if you library works with version 1.3, it is guaranteed to also work with 1.4, 1.6, 2.0 etc. If you target a .NET standard version with your library then all platforms which implement that version are able to run your library.
Essentially:
- Class libraries target a particular .NET Standard version.
- Platforms (e.g .NET Core, .NET (Full) Framework, Xamarin) implement particular .NET Standard versions.
- Applications target platform versions and this determines which .NET standard libraries are available to the application.
Choosing a higher version gives you access to more APIs at the expense of less compatibility. Therefore, the recommended advice is to choose the lowest version that contains everything that you require. Having said that, if your library is only for internal use then compatibility may not be a consideration.
Changing the target framework is as simple as editing the csproj file. You can do this manually (right click the project name and select Edit projectname.csproj) or via the GUI (right click the project name and select Properties).
Adding code to your library
Obviously without some code in your library, it will be useless. There is nothing new here though. You can add NuGet dependencies and add code files just like any other project.
If you find that there are types or methods that are not recognised by the compiler then it is worth checking the .NET standard version you are targeting. By default, your class library will target 1.4 but I have found that it is often necessary to move to 1.5 or more. The .NET standard github repo is very useful when you run into issues.
Using our class library
Now assuming that we have written our highly useful class library, let's look at how to reference it from another application.
Project Reference
The simplest way to use our class library is to add it as a project reference.
This results in the compilation of the library along with the application. Whenever a change is made to the class library, it is automatically picked up by the application (locally) and will be included when the application is published.
If the class library is only used by one application then it makes sense to add the library project to the same solution as the application and to reference the class library as a project reference. If the library is shared however, then this method can be problematic.
You probably want your source control repository to include everything necessary for a build. After all, this is essential for automated builds and continuous integration but is rather difficult with shared project references. Do you duplicate the shared source code in multiple places leading to multiple variants and no proper versioning? Alternatively, perhaps you keep all projects in the same large version control repository and rely on relative paths to reference projects.
Both approaches are bad and it is far easier to only use project references for class libraries that are specific to the solution. Any shared library (i.e. used by multiple independent applications) needs to be distributed and referenced using a different technique.
DLL Reference
Another common approach is to add direct references to DLL files. Your shared library is completely separate from your application code and you have full control over the release of new versions (with proper version numbers). To allow the application to build on CI servers etc. you might be tempted to create a lib folder in your application and copy in DLLs.
This approach was fairly common before the advent of NuGet but in .current versions of Net Core (< version 2), this is not possible. Visual Studio tooling will allow you to add the reference but you will get run time errors similar to:
FileNotFoundException: Could not load file or assembly 'ClassLibStd, Version=1.0.1.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
or
.NET Core projects only support referencing .NET framework assemblies in this release. To reference other assemblies they need to be included in a NuGet package
As mentioned, .NET Core 2 will allow this technique (which is very helpful for older third party libraries distributed as DLL only) but for our own shared libraries, then there is a must better approach which we will talk about next.
Package Reference (NuGet)
NuGet is the best method for sharing libraries in .NET Core. Before anyone gets the wrong idea, I am only talking about the use of NuGet packages, rather than publishing all your private library code to the public nuget.org feed!
Creating a NuGet package from a class library in VS 2017 is trivial. Simply right-click the project, choose Properties and go to the package tab. Check the box at the top and then fill in the package information as required:
This will modify the csproj with the information you entered (obviously you can also edit the file manually):
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
<PackageId>DevTrends.ExampleLibrary</PackageId>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>1.0.1</Version>
<Authors>Paul Hiles</Authors>
<Company>DevTrends</Company>
<Product>DevTrends.ExampleLibrary</Product>
<Description>An example library that does very little</Description>
</PropertyGroup>
</Project>
If you then build you project and check the bin folder, you will find a NuGet package has been automatically created.
Referencing your NuGet package requires you to copy it to a known location before adding the location as a package source. In reality, this gives you two options:
- a shared drive which is accessible to all developers and all build tools
- part of the project folder structure, referencing the folder as a relative path.
Adding an additional (fixed path) NuGet package source in Visual Studio is very straightforward:
While you can add additional package sources directly to Visual Studio, it means that all developers need to do so in order to build the project which is not ideal. Instead, a better approach is to add a NuGet.config file to the folder containing your solution file. This can be checked in to source control, automatically configuring the package source for everyone.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="DevTrends Relative Example" value=".\nuget" />
<add key="DevTrends Absolute Example" value="e:\nuget" />
</packageSources>
</configuration>
The major downside of this approach is the fact that you will probably need to commit your NuGet packages to source control if you have cloud based build tools, or if you developers and build server do not have access to a common location where you can store your packages. The solution to this is to create your own private NuGet server which sounds like it might be complicated but can actually be set up in a few hours. We'll talk about this next time.
Conclusion
We looked at the two different types of class library available in .Net Core and concluded that the .NET Standard type is the preferred option in most cases.
We then examined three different methods for sharing your library with other applications. Project references have their place but for libraries used in multiple applications, the recommended approach is to use NuGet packages. We showed how simple it is to create a NuGet package from a class library and how you can easily reference your packages locally.
Next time we will look at creating a secure, private NuGet package feed for your business using Visual Studio Team Services.
Hey,
just dropped in to say I enjoyed that article. I just wish I had seen it yesterday, it would've saved me around 4 hours of trial and error.
I'm currently in the process of establishing a code base in .NET Standard at my place of work. We have a lot of full framework legacy code and we always just used to put the dlls in a common folder and reference them. The aproach always had its flaws, but for the most part it worked quite well.
Obviously (as I know now) this absolutely doesn't work as out-of-the-box for .NET Standard. Even something as simple as a .NET Standard library with a single nuget package reference wouldn't work when referenced directly as a dll.
I figured it out eventually myself, but I wish someone would've just told me to hit publish on that project and add the nuget package to my solution.
Thank you! Concise and straight to the point.