.NET Solution Structure

The Goal

Imagine a software solution that consists of two .NET applications: one desktop application and one web application. Both of these applications should access the same database. Additionally, in the long run, you might want to add new applications which should rely on the same database.

A typical structure of your .NET solution would look as follows: you would build each of these applications independently (i.e. in their own project) and have them reference a shared database project. In the process, you realize that the different application projects have different needs and decide to target the different implementations of .NET accordingly.

The desktop application could, for example, target the .NET Framework since it may only need to run on Windows OS. Whereas the web application could target the .NET Core, since it may need to be deployed across multiple platforms (MacOS, Linux, even docker containers!).

It is at this point when you become aware of the in-depth specifications of the vast Microsoft .NET ecosystem. You cannot reference a project targeting .NET Framework from one targeting .NET Core. Neither does it work the other way around. But what about the database access technology, how does Entity Framework integrate in all this?

And you start wondering: is it even possible?

Is there a way to structure your .NET solution to allow sharing a database project between two (or more!) projects that target .NET Core and .NET Framework respectively?

We found out that it is possible! Keep on reading to figure out how.

The Tools

For the shared database project, Microsoft is providing us with a database access framework – Entity Framework – which ships in two flavors (Entity Framework 6 and Entity Framework Core). They are both shipped as NuGet packages which makes it easy to integrate them into any project by means of the Visual Studio NuGet Package Manager.

Entity Framework 6 runs on the .NET Framework only and has been first released in 2008, being considered the stable and mature Microsoft ORM solution. At the time of writing, EF6 is still being supported with bug fixes and minor improvements but the current roadmap does not seem to have big plans for upcoming Entity Framework 6.x versions.

On the other hand, Entity Framework Core is a complete rewrite of EF6, having been released in 2016. It now has the full attention of both the EF development team and the .NET developer community. And for a good reason, since it is considered to bring many improvements and new features when compared to EF6. Not only is EF Core designed to be more lightweight and extensible, but it keeps the same syntax as its predecessor, meaning that the developer experience does not change for those used to Entity Framework 6. This article does not cover the differences between the Entity Framework 6 and Entity Framework Core. For an extensive survey, check out the respective documentation.

However, the major EF Core feature that helps us on our mission is the following:

EF Core is supported by both implementations of .NET: .NET Framework and .NET Core.

Let’s see it in action and figure out how we can use EF Core to achieve our desired .NET solution structure.

The Way

The frameworks and their versions

At the time of writing, the following are the latest stable versions of the frameworks we are using:

  • .NET Framework: 4.6.1
  • .NET Core: 2.1
  • .NET Standard: 2.0
  • Entity Framework Core: 2.2.3

The projects and their dependencies

As outlined in our scenario, we will build a sample solution with 3 projects:

  1. Database Class Library Project, targeting .NET Standard
  2. App Project, targeting .NET Framework
  3. App Project, targeting .NET Core

Both App Projects reference the Database Project which is only possible if the Database project is targeting .NET Standard.

The dependency graph of the projects in our demo solution.

We are now going to consider each of the projects and install their dependencies. We can achieve that using the NuGet Package Manager UI in Visual Studio.

.NET Standard Database Class Library Project

The EF Core APIs are not all packaged into one single assembly, so we can only install dependencies to the ones we need. Firstly, EF Core will need to know which database we will be working with. For the purpose of our demo we will persist the data in SqlServer LocalDB, which comes with Visual Studio. For this we will install the Microsoft.EntityFrameworkCore.SqlServer NuGet package.This package exposes to us a provider that allows us interaction with the database.


The Microsoft SQL Server database provider for Entity Framework Core.

Secondly, we want to create and execute migrations so we need access to the migrations commands and logic. These are present in the Microsoft.EntityFrameworkCore.Tools NuGet package.


Entity Framework Core Tools for the NuGet Package Manager Console in Visual Studio.

Note* Referencing the Microsoft.EntityFrameworkCore package is not necessary, since the Core packages will be automatically pulled given the above two packages reference them.

.NET Core App Project

For the .NET Core project, we need to install the following dependency: Microsoft.EntityFrameworkCore.Design.

Shared design-time components for Entity Framework Core tools.
.NET Framework App Project

For the .NET Framework project, we need to install the following dependencies: Microsoft.EntityFrameworkCore.Design.


Shared design-time components for Entity Framework Core tools.

And Microsoft.EntityFrameworkCore.SqlServer.

The data model

C#

For defining the data model that’s going to be stored in the database, we can create the class Entity.cs and add some basic properties.

The DbContext

The DbContext class is provided by Entity Framework as a way to represent the settings of each database connection. Find out more about it here. This is how we would set it up for a basic use case.

C#

The first migration

Our .NET solution structure is defined above and complete with NuGet dependencies and cross-project references. The only thing we need to do is to create the initial migration to get the database up and running.

Whenever we run any migration commands we need to keep in mind the following parameters needed by the Package Manager Console:

  • target-project
    • is where the commands will change files
    • In our case, we want to have the Database Project as target-project
  • startup-project
    • needs to be an executable (cannot be a class library project!) and is the one that the tools build and run
    • Any of the two Application Projects
The Package Manager Console and the target-project & startup-project parameters.

Running the add-migration command, using the same syntax as in Entity Framework 6, creates the migrations table and instantiates the database.

And.. that’s it!

Really?

Yes, that’s it! The following demonstrates a mock access of the two applications to the shared database.

The database access demo

The following code shows how to use the DbContext to connect to the database, create one entity and read all entities in the database. For the purpose of this demo, both applications are console applications. However, notice that the use of the DbContext does not change across the two applications. How amazing is that?!?

.NET Core App

C#

.NET Framework App

C#


Now the entities added via each of the apps are in the database.

Database access from the two applications.

Summary

We managed to setup the project dependencies that allow us to have one shared database project, which is referenced by two projects targeting different .NET frameworks. With this solution structure in place, you can now go on and focus on the functionality of your distributed application!

1 reply

Comments are closed.