Class Libraries in .NET Core

Class Library Head Image

Today’s header image was created by Thomas Kelley, the original source for the image is available here

What’s a Class Library?

This section contains some pretty trivial and silly examples. Please ignore the silliness and triviality of the code, it’s just for illustration purposes.

Also, you can skip this section if you know what a class library is.

Imagine that you have some code that you want to run a whole bunch of times in different places. It might be some code to generate a collection of reports, and you might need to generate those reports from a bunch of different places in your application, for instance

Well, you could extract that code and put it into a class library to make it easier to call it where ever you need it without having to duplicate the code everywhere.

Sounds good to me, I hate having multiple blocks of code that do the same thing and having to keep them all in sync. I always forget to update one when I do that

Not that I do that, of course. No, that would be bad practise.

If you have multiple copies of the same method somewhere, then it could mean that you’re suffering from Code Smell.

And no one likes stinky code

So we extract common code out to a class library, and this will help us with maintaining that code. Here’s an example of code that can easily be extracted:

public void SomeMethod()
{
var userChoice = (default)int;
Console.WriteLine("Please choose from the following options:"
+ Environment.NewLine + "1. Hello, World!"
+ Environment.NewLine + "2. Goodbye, World!");
userChoice = int.Parse(Console.ReadLine());
switch(userChoice)
{
case 1:
Console.WriteLine("Hello, World!");
break;
case 2:
Console.WriteLine("Goodybe, World!");
}
}
public void SomeOtherMethod()
{
var userChoice = (default)int;
Console.WriteLine("Please choose from the following options:"
+ Environment.NewLine + "1. Hello, World!"
+ Environment.NewLine + "2. Goodbye, World!");
userChoice = int.Parse(Console.ReadLine());
switch(userChoice)
{
case 1:
Console.WriteLine("Hello, World!");
break;
case 2:
Console.WriteLine("Goodybe, World!");
}
}
view raw reused-code.cs hosted with ❤ by GitHub

Gotta love trivial examples, right?

We can extract the common code here out to a method, and call it in both ‘SomeMethod’ and ‘SomeOtherMethod’

public void PromptUserForAnswer()
{
var userChoice = (default)int;
Console.WriteLine("Please choose from the following options:"
+ Environment.NewLine + "1. Hello, World!"
+ Environment.NewLine + "2. Goodbye, World!");
userChoice = int.Parse(Console.ReadLine());
switch(userChoice)
{
case 1:
Console.WriteLine("Hello, World!");
break;
case 2:
Console.WriteLine("Goodybe, World!");
}
}
public void SomeMethod()
{
PromptUserForAnswer();
}
public void SomeOtherMethod()
{
PromptUserForAnswer();
}
view raw reused-method.cs hosted with ❤ by GitHub

One of the many reasons why this is less smelly (and therefore a little better) is that if we ever want to add more prompt options (or take some away), then we only need to edit the ‘PromptUserForAnswer’ method.

This is a pretty standard software engineering practise.

When we get to MVC applications, we’ll be storing our POCOs, View Models and Database Entity Models in separate class libraries.

Not to mention our database access and business logic layers too.

But what if we want to use that common code in other classes and namespaces?

Here comes another trivial example

namespace MyApp.UserInterface
{
public class LoginForm
{
public void PromptUserForOptions()
{
// what do we do if we want to call PromptUserForAnswer here?
}
}
}
namespace MyApp.ResourceManager
{
public class Resources
{
public void PromptUserForOptions()
{
// what do we do if we want to call PromptUserForAnswer here?
}
}
}

Here could use a class library, that way our code to prompt the user would be in one place, and can be used across namespaces without having to do any crazy hacking where we link separate projects and namespaces together without references.

Let’s not worry about linking in that manner, because it can get difficult to manage

namespace MyApp.CommonCodeLibrary
{
public static class MyClassLibrary
{
public static string PromptUserForAnswer()
{
var userChoice = (default)int;
Console.WriteLine("Please choose from the following options:"
+ Environment.NewLine + "1. Hello, World!"
+ Environment.NewLine + "2. Goodbye, World!");
userChoice = int.Parse(Console.ReadLine());
switch(userChoice)
{
case 1:
return "Hello, World!";
case 2:
return "Goodybe, World!";
}
return String.Empty;
}
}
}
namespace MyApp.UserInterface
{
public class LoginForm
{
public void PromptUserForOptions()
{
var message = MyApp.CommonCodeLibrary.MyClassLibrary.PromptUserForAnswer();
Console.WriteLine(message);
}
}
}
namespace MyApp.ResourceManager
{
public class Resources
{
public void PromptUserForOptions()
{
var message = MyApp.CommonCodeLibrary.MyClassLibrary.PromptUserForAnswer();
Console.WriteLine(message);
}
}
}

What we’ve done here is created a static class within our ‘CommonCodeLibrary’ namespace called ‘MyClassLibrary’, and called the ‘PromptUserForAnswer’ method from within it.

We’ve created a static class so that we don’t have to have an instance of it in order to use it.

Obviously, keeping all of these classes and namespaces in the same file would quickly become unmanageable. So here is an example screenshot of how you might separate namespaces out into separate files.

Separated Namespaces
Each directory is a namespace, and each file is either a class or an interface within that namespace

Here are a selection of edited class declarations from within those directories to help show what I mean.

// BaseController.cs
namespace dwCheckApi.Controllers
{
public class BaseController : Controller
{
}
}
// dwContext.cs
namespace dwCheckApi.DatabaseContexts
{
public class DwContext : DbContext
{
}
}
// BookService.cs
namespace dwCheckApi.Services
{
public class BookService : IBookService
{
}
}

This is also a cheeky sneak peak at an application that I’m working on. Don’t worry, I’ll write a blog post on it, when it’s ready for the big time.

Now that all of that makes sense, let’s create a class library in .NET Core.

Creating a Class Library in .NET Core

Creating a class library in .NET Core isn’t any different to creating any of the applications types.

I’ll have a post on those soon, and will put a link in here.

If you remember from my introductory post on Unit Tests in .NET Core

You haven’t read it? You’d better go take a look at it, I’ll wait.

I placed my class libraries in a separate directory to my code. This is a pretty good practice to use, as it not only visually separates the code but physically does it too.

Test-Library directory layout
The directory layout of the test-library

In my tutorials thus far, we’ve been using a Yoeman generator to template our applications. Let’s break that cycle and hand roll everything.

You can still use Yoeman (or Visual Studio) if you want, though.

Also, I’ll be using the terminal. But you don’t have to.

Create a directory for our application somewhere and change into, then create one for our source code.

mkdir consoleAppWithClassLibrary
cd consoleAppWithClassLibrary
mkdir src
view raw console-commands.sh hosted with ❤ by GitHub

Outside of our source code, we need a file called global.json. This tells Roslyn where to go to find the library code, since we’re burying it all in a src folder.

{
"projects": [
"src"
]
}
view raw global.json hosted with ❤ by GitHub

Next we’re going to create some directories for our code, and change into the one for our class library.

mkdir app
mkdir library
cd library
view raw console-commands.sh hosted with ❤ by GitHub

We’re starting here first, but you can create the app first if you wish. The application isn’t going to be complex, but we won’t be able to compile it until the library is completed.

First we need to add a project.json to our library directory.

{
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable"
},
"frameworks": {
"netstandard1.6": {
"dependencies": {
"NETStandard.Library": "1.6.0"
}
}
}
}

Pretty standard stuff

Now we’re going to add some code. Our class library isn’t going to do anything fantastically stupendous. We’ll have one function that takes two ints and returns an object which holds the sum of the two ints.

Let’s create a file called “Response.cs” which will be the POCO we’ll use to return the data from our Procedure class.

More on the Procedure class in a moment.

namespace Library
{
public class Response
{
public string Key { get; set; }
public string Value { get; set; }
}
}
view raw Response.cs hosted with ❤ by GitHub

In bigger applications, you would usually separate common POCOs out to a directory and namespace of their own, since they’re shared between the application and library. But this example is trivial enough that it won’t really matter if we don’t.

Now let’s add a file called “Procedure.cs”, this will contain our class library method.

namespace library
{
public class Procedure
{
public Response Addition (int left, int right)
{
return new Response
{
Key = $"The Answer to {left} plus {right}",
Value = $"{left + right}"
};
}
}
}
view raw Procedure.cs hosted with ❤ by GitHub

That’s the class library all written up. We’d better make sure it builds.

dotnet restore
dotnet build
view raw console-commands.sh hosted with ❤ by GitHub

If all goes well, then you should get something similar to this as output from the build command:

Project library (.NETStandard,Version=v1.6) will be compiled because inputs were modified
Compiling library for .NETStandard,Version=v1.6
Compilation succeeded.
0 Warning(s)
0 Error(s)
Time elapsed 00:00:01.4420829
view raw console-commands.sh hosted with ❤ by GitHub

You’ll need to fix any errors before we can continue, so go do that if there are any. But if you’ve copied the code correctly, then there won’t be any.

Consuming a Class Library in .NET Core

We’ll need to change over to our ‘app’ directory before continuing.

Assuming that you’re still in the ‘library’ directory.

cd ..
cd app
view raw console-commands.sh hosted with ❤ by GitHub

Because ‘app’ (which contains the Application code) and ‘library’ (which contains the class library) are in separate locations, we need to tell the project.json file for ‘app’ to get ‘library’ from somewhere.

And that’s what we’ll do here, over in our project.json we’ll have to add a reference to our ‘library’ namespace, and that it’s a project within our code base (otherwise, Roslyn is going to try and pull a packaged called ‘library’ from NuGet).

{
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable",
"emitEntryPoint": true
},
"dependencies": {
"library": {
"target": "project"
}
},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
}
},
"imports": "dnxcore50"
}
}
}
view raw app-project.json hosted with ❤ by GitHub

Between lines 7 and 9 (which I’ve highlighted) we’re telling Roslyn to look for a local project called ‘library’ and include the binary which is produced by compiling that library in the binary directory of our ‘app’ project.

Now we need to add the program logic. It’s also going to be quite trivial.

This post is for demonstration purposes, remember. Also, who doesn’t love a trivial example.

Now we need to make our application do something. So create the program.cs file (without it, Roslyn won’t compile our app project) and fill it in with this:

// Static link to the console so that we can simplfy out calls to
// Console.WriteLine later
using static System.Console;
// Our class library's namespace
using Library;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
// Create an instance of the Procedure class
// from the class library
var proc = new Procedure();
// Call the Addition method and store the response
var resp = proc.Addition(19, 23);
// Write the response data to the Console
WriteLine(resp.Key);
WriteLine(resp.Value);
}
}
}
view raw program.cs hosted with ❤ by GitHub

Hopefully the file contents are simple enough to follow.

Next, let’s build this sucker.

dotnet restore
dotnet build
view raw console-commands.sh hosted with ❤ by GitHub

And if all goes well, you’ll see something like

Project app (.NETCoreApp,Version=v1.0) will be compiled because inputs were modified
Compiling app for .NETCoreApp,Version=v1.0
Compilation succeeded.
0 Warning(s)
0 Error(s)
Time elapsed 00:00:01.8966733
view raw console-commands.sh hosted with ❤ by GitHub

You shouldn’t get any errors, but if you do then you’ve failed at copy-pasting.

The final step is to run our app and see what happens.

dotnet run
view raw console-commands.sh hosted with ❤ by GitHub

Which should produce output similar to the following

Project library (.NETStandard,Version=v1.6) was previously compiled. Skipping compilation.
Project app (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
The Answer to 19 plus 23
42
view raw console-commands.sh hosted with ❤ by GitHub

And there we have it.

We’ve built a class library and a console application to go with it, both with .NET Core.

Before we gp, let’s take another look at the directory structure.

Console App With Class Library Directory Structure
The directory structure of our simple application, as seen from Visual Studio Code

What we’ve told Roslyn to do is go and get the binary from the ‘library’ directory and include it in the ‘app’ directory before building that. Building ‘library’ as we go, if the source files for it have changed.

That way, when we run the ‘app’ project, our ‘library’ code will be loaded alongside it.

Pretty cool, huh?

Sidelines

An interesting tidbit before I go:

"frameworks": {
"netstandard1.6": {
"dependencies": {
"NETStandard.Library": "1.6.0"
}
}
}

Because we built the Class Library against .NET Standard 1.6, it can be used with a .NET Framework application (as long as that .NET Framework application targets version 4.6.1 of .NET Framework, that is). If you take a look in library’s bin folder, you’ll see (nested within some directories) something like this:

Library Bin Folder
The contents of the bin folder for our class library

If you drop the library.dll file into a .NET Framework project as a dependency, it will run fine.

All because of the magic of the .NET Standard.

A .NET developer specialising in ASP.NET MVC websites and services, with a background in WinForms and Games Development. When not programming using .NET, he is either learning about .NET Core (and usually building something cross platform with it), speaking Japanese to anyone who'll listen, learning about languages, writing for his non-dev blog, or writing for a blog about Retro Gaming (which he runs with his brother)