Creating a shared EF Core database project for use in WPF and Xamarin.Forms apps
I could never find a good example of using a single EntityFramework Core project in both WPF and Xamarin.Forms apps, with multiple providers. In theory, it’s dead simple, but there are a few little things you have to tinker with to make it work. Here’s how…
The basic idea
The way I go about this is I create a basic database context that only contains the DbSet
properties and then make one more context per database provider (PostgreSQL, SQLite in this case) that derives from the basic one. The derived contexts then override their OnConfiguring
methods where they set up the connection for their specific provider using a connection string extracted from a helper class.
Why multiple database contexts? In short: migrations. We want to be able to perform migrations for each provider separately since each provider might need the migration to be a bit different from the others. Having multiple contexts allows us to call the Add-Migration
command on each of the contexts separately.
Check out the example project
Check out the repository with the complete solution at https://github.com/michaldivis/ocean
Solution structure
Data (.NET Standard class library)
This project will contain the EF Core database context, data models, and some helpers.
Dependencies
Nuget
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Design
- Microsoft.EntityFrameworkCore.Tools
- Npgsql.EntityFrameworkCore.PostgreSQL
- Microsoft.EntityFrameworkCore.Sqlite
Logic (.NET Standard class library)
This project will contain a simple view model that will be shared by the other apps. This will be referencing the Data project.
Dependencies
Projects
- Data
Nuget
- Microsoft.EntityFrameworkCore
DesktopApp (.NET Core WPF)
An example WPF app that will use PostgreSQL as the database. This will be referencing the Data and Logic projects.
Dependencies
Projects
- Data
- Logic
Nuget
- Microsoft.EntityFrameworkCore
- Npgsql.EntityFrameworkCore.PostgreSQL
MobileApp (Xamarin.Forms)
An example mobile app that will use SQLite as the database. This will be referencing the Data and Logic projects.
Dependencies
Projects
- Data
- Logic
Nuget
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Sqlite
Used versions
SDK
- .NET Core 3.1
- .NET Standard 2.0
- Xamarin.Forms 4.8
Nuget packages
- Microsoft.EntityFrameworkCore 3.1.7
- Npgsql.EntityFrameworkCore.PostgreSQL 3.1.4
- Microsoft.EntityFrameworkCore.Sqlite 3.1.7
Creating the data project — contexts, models, helpers
Folders
I’ve created two folders in the data project
- Config — migration configuration values will be here
- Contexts — db contexts for different platforms will be here
- Helpers — helper classes for making this usable on multiple platforms will be here
- Migrations
- — PostgreSqlMigrations — Migrations for PostgreSQL will be here
- — SqliteMigrations — Migrations for SQLite will be here
- Models — data model classes will be here
Models
Let’s say we want to have a database of fish. Here’s a class called Fish
.
I’m going to using this class as the example data model.
A way to use different database contexts for different platforms
Since we want to use different databases (PostgreSQL, SQLite) for the two apps, we’ll need a way to make the Data project take in some configuration (wanted database provider, connection string).
For that I’ve created the following items in the “Helpers” folder:
DbProvider.cs
This is an enum that will help us determine what database provider to use
IConnectionString.cs
An abstraction of a connection string, which differs from one provider to another
PostgreSqlConnectionString.cs
A connection string definition for PostgreSQL
SqliteConnectionString.cs
A connection string definition for SQLite
DbHelper.cs
This static class is going to be used to configure the connection from the other projects (DesktopApp, MobileApp)
Database contexts
To use this on multiple platforms, we need to have different database contexts for each platform. However, they need to share the same data sets.
That’s why we’ll create one base context and derive the others from that.
Here’s the base context.
Here’s the PostgreSQL context:
Here’s the SQLite context:
Almost ready, let’s migrate
Now before we use the app, there’s one last step — migrations.
To create an initial (or any other) migration for each database context, we’ll to do the following:
Change the Data project’s target framework to multiple targets to run migrations with it
Go to the Data.csproj and change this:
<TargetFramework>netstandard2.0</TargetFramework>
to this
<TargetFrameworks>netcoreapp2.0;netstandard2.0</TargetFrameworks>
The reason we do that is to be able to run commands using the Data project in the Package Manager Console (which needs a runnable project type to function).
Migrate
Open the Package Manager Console (Tools -> Nuget Package Manager -> Package Manager Console in Visual Studio 2019)
Make sure to have the Data project selected as the default project.
And run the migration command:
SQLite migration command
Add-Migration SqliteMigration001 -Context SqliteOceanDbContext -OutputDir Migrations/SqliteMigrations
PostgreSQL migration command
Add-Migration PostgreSqlMigration001 -Context PostgreSqlOceanDbContext -OutputDir Migrations/PostgreSqlMigrations
After the command finishes, there should be some new files in the corresponding Migrations/ folder.
We’re now ready to use the thing!
Configuring the connection for each platform
MobileApp project
Now let’s configure the database from the mobile project.
Let’s first add a reference to the Data project.
Database file path
We’ll need a way to get some valid database file path for each platform (Android, iOS). Let’s do that.
Create an interface called IDbPathFinder
in the MobileApp project
Create an Android implementation of that interface in the MobileApp.Android project
Create an iOS implementation of that interface in the MobileApp.iOS project
Configuring on start
To configure the database connection before we need to use, we’ll make all the call in the App.xaml.cs
file.
We’ll add a method called ConfigureDatabase
, that will contain the configuration and then call that method from the OnStart
method.
The App.xaml.cs file should now look like this:
DesktopApp project
Now let’s configure the database from the desktop project.
Let’s first add a reference to the Data project.
To configure the database connection before we need to use, we’ll make all the call in the App.xaml.cs
file.
Add a method called ConfigureDatabase
, that will contain the configration.
Then override the OnStartup
method and call the ConfigureDatabase
from it.
The App.xaml.cs file should now look like this:
Note that in this example I’m hard coding the connection string, which is a terrible idea. Don’t do that!
Let’s try it
Basic view model
To try this, I’ll create a simple class called MainViewModel
that will serve as a view model for both the DesktopApp and the MobileApp.
I’ve referenced the Data project and created the following class:
Desktop app view
I’ve added a reference to the Logic project and edited the MainWindow to look like this:
MainWindow.xaml.cs
MainWindow.xaml
Mobile app view
I’ve added a reference to the Logic project and edited the MainPage to look like this:
MainPage.xaml.cs
MainPage.xaml
That’s it
Woohoo! That’s it.