Xamarin.Forms and Entity Framework Core 2.0: CRUD Example
Sunday, August 27, 2017

I am very exiced to write this blog post. First time I seen EF was in ASPNET MVC and I fall in love with it, now when I am also very active in Xamarin Development, Entity Framework is something that I miss from my ASP.NET dev "world". With version Core 2.0 all platforms Android, iOS and UWP (uwp soon) are supported so I decided to write this blog post.
When I learn something I like to see it from zero to some CRUD operations in the app, so this blog post will be like that. We will have couple of tables (two), our context class and CRUD operations in Xamarin.Forms.
Hope that you will like it.
First of all our shared project need to be .NET Standard Library 2.0 and here is a good article about it: https://blog.xamarin.com/building-xamarin-forms-apps-net-standard/
You can also check out these great blog posts from Adam Pedley:
- https://xamarinhelp.com/entity-framework-core-xamarin-forms/
- https://xamarinhelp.com/dot-net-standard-pcl-xamarin-forms
If you have any question about migrating to .NET Standard please comment down bellow and I will try to make special blog post about that.
Create new Xamarin.Forms app as always I will use PCL and I will migrate to the .NET Standard Library 2.0.. and add our three standard folders: Models, Views, ViewModels and additional ones are Helpers and DataAccess.
Like this:

Note: At this moment EF Core 2.0 is working on all platforms but UWP is not supporting .NET Standard 2.0 at it will be supported by the end of the year.
More info here: https://github.com/dotnet/standard/blob/master/docs/versions.md
But when UWP become .NET Standard 2.0 "friendly" I will update this blog post.
Note #2: At this moment EF Core is not supporting lazy load.
More info here: https://github.com/aspnet/EntityFrameworkCore/issues/3797.
And here is a couple of word from Rowan Miller: "Just to add one data point to the discussion. Lazy Loading isn't going to be in 2.0 but we are adding some features that are groundwork for implementing it. The biggest one is Life Cycle Hooks, which combined with the EntityEntry APIs we added in 1.1, will allow a rudimentary roll-your-own lazy loading pattern."
Community: "Entity Framework Core 2.0 is available in Xamarin.Forms to all platforms (UWP is coming very very soon)"
Me:
Ok we can start with tutorial now :
First we need to add nuget packages to use EF in our app, in your .NET Standard class library and Android and iOS project add this nuget: Microsoft.EntityFrameworkCore.Sqlite
When you finish installing the nuget you want to add this line in iOS project in AppDelagate.cs:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public override bool FinishedLaunching(UIApplication app, NSDictionary options) { | |
SQLitePCL.Batteries.Init(); | |
global::Xamarin.Forms.Forms.Init(); | |
LoadApplication(new App()); | |
return base.FinishedLaunching(app, options); | |
} |
I will use MVVM for this tutorial and that is my advice to you also, the main focus of this tutorial will be Entity Framework, so if anything is blurry for you about MVVM, you can checkout some of my previous tutorials and blog posts about MVVM.
First thing we need to do is to make our models, our entity models which will represent the tables in our database. For this tutorial I will use two classes/tables with one-to-many relationship with Team and Player.
These classes looks like this:
Team.cs:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
namespace Xamarin.Forms_EFCore.Models { | |
public class Team { | |
public int TeamId { get; set; } | |
public string Title { get; set; } | |
public string Manager { get; set; } | |
public string City { get; set; } | |
public string StadiumName { get; set; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
namespace Xamarin.Forms_EFCore.Models { | |
public class Player { | |
public int PlayerId { get; set; } | |
public string Name { get; set; } | |
public int JerseyNumber { get; set; } | |
public string Position { get; set; } | |
public int TeamId { get; set; } | |
public virtual Team Team { get; set; } | |
} | |
} |
That are the classes which we will use in our database.
If you are familiar with EntityFramework you will know that we need some class which will represent the context of our database.
So we need one class which will inherent from DbContext class and it will hold our two table classes with in this case two DbSet properties.
Here you can see a code snippet for our DatabaseContext and some additional code lines that will be described in the next few paragraphs:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.EntityFrameworkCore; | |
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
using Xamarin.Forms; | |
using Xamarin.Forms_EFCore.Helpers; | |
using Xamarin.Forms_EFCore.Models; | |
namespace Xamarin.Forms_EFCore.DataAccess { | |
public class DatabaseContext : DbContext { | |
public DbSet<Team> Teams { get; set; } | |
public DbSet<Player> Players { get; set; } | |
public DatabaseContext() { | |
this.Database.EnsureCreated(); | |
} | |
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { | |
var dbPath = DependencyService.Get<IDBPath>().GetDbPath(); | |
optionsBuilder.UseSqlite($"Filename={dbPath}"); | |
} | |
} | |
} |
It ensures that the database for the context exists. If it exists, no action is taken. If it does not exist then the database and all its schema are created. If the database exists, then no effort is made to ensure it is compatible with the model for this context.
Also inside of DatabaseContext class we have method OnConfiguring where you can set some options for database. In this particular case we need to get sqlite database path for every platform that is used in our solution. To get these values best approach is to use DependencyService which is built in Xamarin.Forms.
Code lines for getting sqlite database path for platforms:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Android | |
var dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "EFCore.db"); | |
// iOS | |
var dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "..", "Library", "EFCore.db"); | |
// UWP | |
var dbPath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "EFCore.db"); |
In this example I am using the DependencyService, you will see the whole code in github repo. I will write another blog post about DependencyService in Xamarin.Forms. If you don't want to go with DependencyService you can simply pass this string that you get from from code lines above to the App.cs in your shared project from platforms specific projects and store it in some public static string property and access it from db context. The decision is yours. 😎
Now our database is ready and we can now implement CRUD operations in our App.
BaseViewModel:
As I mentioned early I will use MVVM in this app example so first thing that I want to make is a Base-view-model which will hold the OnPropertyChanged() method implementation so it is easier to us just to inherit from it and use it with our properties in view model.
Our BaseViewModel:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Runtime.CompilerServices; | |
using System.Text; | |
namespace Xamarin.Forms_EFCore.Helpers { | |
public class BaseViewModel : INotifyPropertyChanged { | |
public event PropertyChangedEventHandler PropertyChanged; | |
protected void OnPropertyChanged([CallerMemberName]string propertyName = "") => | |
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); | |
} | |
} |
READ:
Main idea in this app is to list all the teams, view details about them, add players to the team... So first view will be ListPage for our Teams.Create new content page called TeamsListPage.xaml and it should look like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" | |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | |
x:Class="Xamarin.Forms_EFCore.Views.TeamsListPage" | |
Title="All teams"> | |
<ContentPage.ToolbarItems> | |
<ToolbarItem Text="Add new" | |
Icon="ic_add.png" | |
Command="{Binding AddTeamCommand}" /> | |
</ContentPage.ToolbarItems> | |
<ContentPage.Content> | |
<ListView ItemsSource="{Binding AllTeams}" | |
HasUnevenRows="True" | |
ItemTapped="OnTeamTapped" | |
BackgroundColor="#f5f5f5"> | |
<ListView.ItemTemplate> | |
<DataTemplate> | |
<ViewCell> | |
<Grid BackgroundColor="White" | |
Margin="4"> | |
<Grid.RowDefinitions> | |
<RowDefinition Height="Auto" /> | |
<RowDefinition Height="Auto" /> | |
<RowDefinition Height="Auto" /> | |
</Grid.RowDefinitions> | |
<Label Grid.Row="0" | |
Text="{Binding Title}" | |
FontSize="Medium" | |
Margin="4" | |
FontAttributes="Bold" /> | |
<StackLayout Orientation="Horizontal" | |
Grid.Row="1" | |
Margin="4" | |
Padding="2"> | |
<Label Text="{Binding StadiumName}" | |
FontSize="Small" /> | |
<Label Text="{Binding City}" | |
FontSize="Small" /> | |
</StackLayout> | |
</Grid> | |
</ViewCell> | |
</DataTemplate> | |
</ListView.ItemTemplate> | |
</ListView> | |
</ContentPage.Content> | |
</ContentPage> |
As you can see we have our controls ready to do binding from viewmodel.
code behind class called TeamsListPage.xaml.cs:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Xaml; | |
using Xamarin.Forms_EFCore.Models; | |
using Xamarin.Forms_EFCore.ViewModels; | |
namespace Xamarin.Forms_EFCore.Views { | |
[XamlCompilation(XamlCompilationOptions.Compile)] | |
public partial class TeamsListPage : ContentPage { | |
public TeamsListPage() { | |
InitializeComponent(); | |
} | |
protected override void OnAppearing() { | |
base.OnAppearing(); | |
BindingContext = new TeamListViewModel(); | |
} | |
private async void OnTeamTapped(object sender, ItemTappedEventArgs e) { | |
if (e.Item != null) { | |
var team = (Team)e.Item; | |
await Navigation.PushAsync(new TeamDetailsPage(team.TeamId)); | |
} | |
} | |
} | |
} |
and the main part is our view-model:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Collections.ObjectModel; | |
using System.Linq; | |
using System.Text; | |
using System.Windows.Input; | |
using Xamarin.Forms; | |
using Xamarin.Forms_EFCore.DataAccess; | |
using Xamarin.Forms_EFCore.Helpers; | |
using Xamarin.Forms_EFCore.Models; | |
using Xamarin.Forms_EFCore.Views; | |
namespace Xamarin.Forms_EFCore.ViewModels { | |
public class TeamListViewModel : BaseViewModel { | |
private ObservableCollection<Team> allTeams; | |
public ObservableCollection<Team> AllTeams { | |
get { return allTeams; } | |
set { | |
allTeams = value; | |
} | |
} | |
public ICommand AddTeamCommand { get; private set; } | |
private DatabaseContext _context; | |
public TeamListViewModel() { | |
_context = new DatabaseContext(); | |
var teamList = _context.Teams.ToList(); | |
AllTeams = new ObservableCollection<Team>(teamList); | |
AddTeamCommand = new Command(async () => await Application.Current.MainPage.Navigation.PushAsync(new AddTeamPage())); | |
} | |
} | |
} |
We are populating this collection with list of teams from database, so in constructor of view-model we need to initialize DatabaseContext object with known linq syntax we are getting all teams from database.
Next step is to implement Create operation with Entity Framework.
CREATE:
Create new page inside of Views folder called AddTeamPage.xaml. This page is simple "form" layout with couple of entry controls for Team properties.XAML code looks like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" | |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | |
x:Class="Xamarin.Forms_EFCore.Views.AddTeamPage" | |
Title="Add new team"> | |
<ContentPage.ToolbarItems> | |
<ToolbarItem Text="Save" | |
Icon="ic_save.png" | |
Command="{Binding SaveTeamCommand}" /> | |
</ContentPage.ToolbarItems> | |
<ContentPage.Content> | |
<StackLayout> | |
<Entry Placeholder="Team Name/Title" | |
Text="{Binding Title}" | |
Margin="4" /> | |
<Entry Placeholder="Manager name" | |
Text="{Binding Manager}" | |
Margin="4" /> | |
<Entry Placeholder="City name" | |
Text="{Binding City}" | |
Margin="4" /> | |
<Entry Placeholder="Name of the stadium" | |
Text="{Binding StadiumName}" | |
Margin="4" /> | |
</StackLayout> | |
</ContentPage.Content> | |
</ContentPage> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Xaml; | |
using Xamarin.Forms_EFCore.ViewModels; | |
namespace Xamarin.Forms_EFCore.Views { | |
[XamlCompilation(XamlCompilationOptions.Compile)] | |
public partial class AddTeamPage : ContentPage { | |
public AddTeamPage() { | |
InitializeComponent(); | |
} | |
protected override void OnAppearing() { | |
base.OnAppearing(); | |
BindingContext = new AddTeamViewModel(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
using System.Windows.Input; | |
using Xamarin.Forms; | |
using Xamarin.Forms_EFCore.DataAccess; | |
using Xamarin.Forms_EFCore.Helpers; | |
using Xamarin.Forms_EFCore.Models; | |
namespace Xamarin.Forms_EFCore.ViewModels { | |
public class AddTeamViewModel : BaseViewModel { | |
private string title; | |
public string Title { | |
get { return title; } | |
set { | |
title = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string manager; | |
public string Manager { | |
get { return manager; } | |
set { | |
manager = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string city; | |
public string City { | |
get { return city; } | |
set { | |
city = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string stadiumName; | |
public string StadiumName { | |
get { return stadiumName; } | |
set { | |
stadiumName = value; | |
OnPropertyChanged(); | |
} | |
} | |
public ICommand SaveTeamCommand { get; private set; } | |
private DatabaseContext _context; | |
public AddTeamViewModel() { | |
_context = new DatabaseContext(); | |
SaveTeamCommand = new Command(SaveTeam); | |
} | |
async void SaveTeam() { | |
Team team = new Team { | |
City = City, | |
StadiumName = StadiumName, | |
Title = Title, | |
Manager = Manager | |
}; | |
_context.Teams.Add(team); | |
_context.SaveChanges(); | |
// After adding new entry to database close this page | |
await Application.Current.MainPage.Navigation.PopAsync(); | |
} | |
} | |
} |
We have standard binding from controls to the view-model properties, also there is one command which will trigger SaveTeam() method for the adding team to database.
That method is interesting for us.
We are creating new object of type Team, and for Team properties we are setting those from View-model which also represents the data from xaml controls (Using MVVM and Binding).
Now we are ready to add that entry to database, with standard approach using .Add(objectToInsert) method, and of course we need to commit that insert with .SaveChanges().
Now we will test this:

DELETE:
When user clicks on list view item on the TeamListPage we want to open new page called TeamDetailsPage and show basic info about this team. So let's implement that, if you scroll up you will see that in our TeamListPage we have one event for openning new page.TeamDetailsPage XAML:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" | |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | |
x:Class="Xamarin.Forms_EFCore.Views.TeamDetailsPage" | |
Title="About team"> | |
<ContentPage.ToolbarItems> | |
<ToolbarItem Text="Add player" | |
Command="{Binding AddPlayerCommand}" | |
Icon="ic_add_player.png" /> | |
<ToolbarItem Text="Edit team" | |
Icon="ic_edit.png" | |
Command="{Binding EditTeamCommand}" /> | |
<ToolbarItem Text="Delete team" | |
Icon="ic_delete.png" | |
Command="{Binding DeleteTeamCommand}" /> | |
</ContentPage.ToolbarItems> | |
<ContentPage.Content> | |
<StackLayout> | |
<Label Text="{Binding Title}" | |
Margin="8" | |
FontSize="Medium" | |
Grid.Row="0" /> | |
<Label Text="{Binding Manager}" | |
Grid.Row="1" | |
FontSize="Small" | |
Margin="8" /> | |
<Label Text="{Binding City}" | |
Grid.Row="2" | |
FontSize="Small" | |
Margin="8" /> | |
<Label Text="{Binding StadiumName}" | |
Grid.Row="3" | |
FontSize="Small" | |
Margin="8" /> | |
<StackLayout Grid.Row="4" | |
Margin="8"> | |
<Label Text="Players:" | |
FontSize="Small" | |
Margin="4" /> | |
<ListView ItemsSource="{Binding Players}" | |
HasUnevenRows="True" | |
ItemTapped="OnPlayerTapped" | |
Margin="4"> | |
<ListView.ItemTemplate> | |
<DataTemplate> | |
<ViewCell> | |
<StackLayout Orientation="Horizontal" | |
HorizontalOptions="StartAndExpand"> | |
<Label Text="{Binding Name}" | |
FontSize="Small" | |
Margin="4" /> | |
<Label Text="{Binding Position}" | |
FontSize="Small" | |
Margin="4" /> | |
<Label Text="{Binding JerseyNumber}" | |
FontSize="Small" | |
Margin="4" /> | |
<Image Source="ic_edit.png" | |
HeightRequest="17" | |
HorizontalOptions="End" | |
VerticalOptions="Center" | |
Margin="4" /> | |
</StackLayout> | |
</ViewCell> | |
</DataTemplate> | |
</ListView.ItemTemplate> | |
</ListView> | |
</StackLayout> | |
</StackLayout> | |
</ContentPage.Content> | |
</ContentPage> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Xaml; | |
using Xamarin.Forms_EFCore.Models; | |
using Xamarin.Forms_EFCore.ViewModels; | |
namespace Xamarin.Forms_EFCore.Views { | |
[XamlCompilation(XamlCompilationOptions.Compile)] | |
public partial class TeamDetailsPage : ContentPage { | |
private int teamId; | |
public TeamDetailsPage(int teamId) { | |
InitializeComponent(); | |
this.teamId = teamId; | |
} | |
protected override void OnAppearing() { | |
base.OnAppearing(); | |
BindingContext = new TeamDetailsViewModel(teamId); | |
} | |
public async void OnPlayerTapped(object sender, ItemTappedEventArgs e) { | |
if (e.Item != null) { | |
var player = (Player)e.Item; | |
await Navigation.PushAsync(new EditPlayerPage(player.PlayerId)); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Collections.ObjectModel; | |
using System.Linq; | |
using System.Text; | |
using System.Windows.Input; | |
using Xamarin.Forms; | |
using Xamarin.Forms_EFCore.DataAccess; | |
using Xamarin.Forms_EFCore.Helpers; | |
using Xamarin.Forms_EFCore.Models; | |
using Xamarin.Forms_EFCore.Views; | |
namespace Xamarin.Forms_EFCore.ViewModels { | |
public class TeamDetailsViewModel : BaseViewModel { | |
private string title; | |
public string Title { | |
get { return title; } | |
set { | |
title = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string manager; | |
public string Manager { | |
get { return manager; } | |
set { | |
manager = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string city; | |
public string City { | |
get { return city; } | |
set { | |
city = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string stadiumName; | |
public string StadiumName { | |
get { return stadiumName; } | |
set { | |
stadiumName = value; | |
OnPropertyChanged(); | |
} | |
} | |
private ObservableCollection<Player> players; | |
public ObservableCollection<Player> Players { | |
get { return players; } | |
set { players = value; } | |
} | |
public ICommand AddPlayerCommand { get; private set; } | |
public ICommand EditTeamCommand { get; private set; } | |
public ICommand DeleteTeamCommand { get; private set; } | |
private DatabaseContext _context; | |
private int _teamId; | |
public TeamDetailsViewModel(int teamId) { | |
_teamId = teamId; | |
_context = new DatabaseContext(); | |
var team = _context.Teams.Find(teamId); | |
// Setting property values from team object | |
// that we get from database | |
Title = team.Title; | |
City = team.City; | |
StadiumName = team.StadiumName; | |
Manager = team.Manager; | |
Players = new ObservableCollection<Player>(_context.Players.Where(p => p.TeamId == teamId)); | |
// Commands for toolbar items | |
AddPlayerCommand = new Command(async () => await Application.Current.MainPage.Navigation.PushAsync(new AddPlayerPage(team.TeamId))); | |
EditTeamCommand = new Command(async () => await Application.Current.MainPage.Navigation.PushAsync(new EditTeamPage(team.TeamId))); | |
DeleteTeamCommand = new Command(DeleteTeam); | |
} | |
void DeleteTeam() { | |
var team = _context.Teams.Find(_teamId); | |
_context.Teams.Remove(team); | |
_context.SaveChanges(); | |
Application.Current.MainPage.Navigation.PopAsync(); | |
} | |
} | |
} |
First step is to get a an team by Id using Find() method, when we have our team to delete we can call Remove() method and as parameter send the object to remove from database, and as always commit that with SaveChanges().
Let's test this:

UPDATE:
In our TeamDetailsPage we have toolbar item for opening an EditTeamPage so there we will implement Update operations for team class/table.
XAML code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" | |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | |
x:Class="Xamarin.Forms_EFCore.Views.EditTeamPage" | |
Title="Edit team"> | |
<ContentPage.ToolbarItems> | |
<ToolbarItem Text="Save" | |
Icon="ic_save.png" | |
Command="{Binding SaveTeamCommand}" /> | |
</ContentPage.ToolbarItems> | |
<ContentPage.Content> | |
<StackLayout> | |
<Entry Placeholder="Team Name/Title" | |
Text="{Binding Title}" | |
Margin="4" /> | |
<Entry Placeholder="Manager name" | |
Text="{Binding Manager}" | |
Margin="4" /> | |
<Entry Placeholder="City name" | |
Text="{Binding City}" | |
Margin="4" /> | |
<Entry Placeholder="Name of the stadium" | |
Text="{Binding StadiumName}" | |
Margin="4" /> | |
</StackLayout> | |
</ContentPage.Content> | |
</ContentPage> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Xaml; | |
using Xamarin.Forms_EFCore.ViewModels; | |
namespace Xamarin.Forms_EFCore.Views { | |
[XamlCompilation(XamlCompilationOptions.Compile)] | |
public partial class EditTeamPage : ContentPage { | |
private int teamId; | |
public EditTeamPage(int teamId) { | |
InitializeComponent(); | |
this.teamId = teamId; | |
} | |
protected override void OnAppearing() { | |
base.OnAppearing(); | |
BindingContext = new EditTeamViewModel(teamId); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
using System.Windows.Input; | |
using Xamarin.Forms; | |
using Xamarin.Forms_EFCore.DataAccess; | |
using Xamarin.Forms_EFCore.Helpers; | |
namespace Xamarin.Forms_EFCore.ViewModels { | |
public class EditTeamViewModel : BaseViewModel { | |
private string title; | |
public string Title { | |
get { return title; } | |
set { | |
title = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string manager; | |
public string Manager { | |
get { return manager; } | |
set { | |
manager = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string city; | |
public string City { | |
get { return city; } | |
set { | |
city = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string stadiumName; | |
public string StadiumName { | |
get { return stadiumName; } | |
set { | |
stadiumName = value; | |
OnPropertyChanged(); | |
} | |
} | |
public ICommand SaveTeamCommand { get; private set; } | |
private int _teamId; | |
private DatabaseContext _context; | |
public EditTeamViewModel(int teamId) { | |
_context = new DatabaseContext(); | |
var team = _context.Teams.Find(teamId); | |
_teamId = team.TeamId; | |
Title = team.Title; | |
Manager = team.Manager; | |
StadiumName = team.StadiumName; | |
City = team.City; | |
SaveTeamCommand = new Command(SaveTeam); | |
} | |
async void SaveTeam() { | |
var team = _context.Teams.Find(_teamId); | |
team.Title = Title; | |
team.Manager = Manager; | |
team.StadiumName = StadiumName; | |
team.City = City; | |
_context.Teams.Update(team); | |
_context.SaveChanges(); | |
await Application.Current.MainPage.Navigation.PopAsync(); | |
} | |
} | |
} |
First thing is to get existing team from database by Id, this is very important because we will make changes on this object and later save/update it to database.
After we have our object of type team to update, we will change its values by those from our view-model, from user input from XAML.
Now we need to save those changes with two approaches, one is to use Update and other approach is with EntityState.
Now we can test this:

It works! With this example we have completed our CRUD on Team table.
Now we can continue to do some operations with Player class/table in our db. If you followed this tutorial from start to this point you will be able to do this by yourself if you want. But I will finish this tutorial with those operations so if you want you can follow me to the end.
Add new Player:
In TeamDetailsPage you can find toolbar item for adding new player for that team, so we will use it to open new page called AddPlayerPage.xaml, and in that page we are passing the teamId just to know in which team we are adding that new player.Xaml:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" | |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | |
x:Class="Xamarin.Forms_EFCore.Views.AddPlayerPage" | |
Title="Add new player"> | |
<ContentPage.ToolbarItems> | |
<ToolbarItem Text="Save" | |
Icon="ic_save.png" | |
Command="{Binding SavePlayerCommand}" /> | |
</ContentPage.ToolbarItems> | |
<ContentPage.Content> | |
<StackLayout> | |
<Label Text="{Binding TeamName, StringFormat='New player for: {0}'}" | |
Margin="4,12,4,4" /> | |
<Entry Placeholder="Full name" | |
Text="{Binding Name}" | |
Margin="4" /> | |
<Entry Placeholder="Position" | |
Text="{Binding Position}" | |
Margin="4" /> | |
<Entry Placeholder="Jersey Number" | |
Text="{Binding JerseyNumber}" | |
Margin="4" /> | |
</StackLayout> | |
</ContentPage.Content> | |
</ContentPage> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Xaml; | |
using Xamarin.Forms_EFCore.ViewModels; | |
namespace Xamarin.Forms_EFCore.Views { | |
[XamlCompilation(XamlCompilationOptions.Compile)] | |
public partial class AddPlayerPage : ContentPage { | |
private int teamId; | |
public AddPlayerPage(int teamId) { | |
InitializeComponent(); | |
this.teamId = teamId; | |
} | |
protected override void OnAppearing() { | |
base.OnAppearing(); | |
BindingContext = new AddPlayerViewModel(teamId); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Windows.Input; | |
using Xamarin.Forms; | |
using Xamarin.Forms_EFCore.DataAccess; | |
using Xamarin.Forms_EFCore.Helpers; | |
using Xamarin.Forms_EFCore.Models; | |
namespace Xamarin.Forms_EFCore.ViewModels { | |
public class AddPlayerViewModel : BaseViewModel { | |
private string teamName; | |
public string TeamName { | |
get { return teamName; } | |
set { | |
teamName = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string name; | |
public string Name { | |
get { return name; } | |
set { | |
name = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string position; | |
public string Position { | |
get { return position; } | |
set { | |
position = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string jerseyNumber; | |
public string JerseyNumber { | |
get { return jerseyNumber; } | |
set { | |
jerseyNumber = value; | |
OnPropertyChanged(); | |
} | |
} | |
public ICommand SavePlayerCommand { get; private set; } | |
private int _teamId; | |
private DatabaseContext _context; | |
public AddPlayerViewModel(int teamId) { | |
_teamId = teamId; | |
_context = new DatabaseContext(); | |
var team = _context.Teams.Where(t => t.TeamId == teamId).FirstOrDefault(); | |
TeamName = team.Title; | |
SavePlayerCommand = new Command(SavePlayer); | |
} | |
void SavePlayer() { | |
var team = _context.Teams.Where(t => t.TeamId == _teamId).FirstOrDefault(); | |
Player player = new Player() { | |
JerseyNumber = Int32.Parse(JerseyNumber), | |
Name = Name, | |
Position = Position, | |
TeamId = _teamId | |
}; | |
_context.Players.Add(player); | |
_context.SaveChanges(); | |
Application.Current.MainPage.Navigation.PopAsync(); | |
} | |
} | |
} |
SavePlayer() method will handle the adding new player to the database, inside of it we are getting the values from our viewmodel and creating new player object with that values, and for property "foreign key" TeamId we will set teamId that we passed from TeamDetailsPage.
After that we just add it to dbcontext like always, and commit that with SaveChanges().
Let's test this:

Edit Player:
... and edit is pretty same approach, only difference is that we will update it. To edit player we need to tap it on the list of player in TeamDetailsPage, we will pass the playerId to the EditPlayerPage.Xaml:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" | |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | |
x:Class="Xamarin.Forms_EFCore.Views.EditPlayerPage" | |
Title="Edit player"> | |
<ContentPage.ToolbarItems> | |
<ToolbarItem Text="Save" | |
Icon="ic_save.png" | |
Command="{Binding SavePlayerCommand}" /> | |
</ContentPage.ToolbarItems> | |
<ContentPage.Content> | |
<StackLayout> | |
<Label Text="{Binding TeamName, StringFormat='Playing for: {0}'}" | |
Margin="4,12,4,4" /> | |
<Entry Placeholder="Full name" | |
Text="{Binding Name}" | |
Margin="4" /> | |
<Entry Placeholder="Position" | |
Text="{Binding Position}" | |
Margin="4" /> | |
<Entry Placeholder="Jersey Number" | |
Text="{Binding JerseyNumber}" | |
Margin="4" /> | |
</StackLayout> | |
</ContentPage.Content> | |
</ContentPage> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Xaml; | |
using Xamarin.Forms_EFCore.ViewModels; | |
namespace Xamarin.Forms_EFCore.Views { | |
[XamlCompilation(XamlCompilationOptions.Compile)] | |
public partial class EditPlayerPage : ContentPage { | |
private int playerId; | |
public EditPlayerPage(int playerId) { | |
InitializeComponent(); | |
this.playerId = playerId; | |
} | |
protected override void OnAppearing() { | |
base.OnAppearing(); | |
BindingContext = new EditPlayerViewModel(playerId); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.EntityFrameworkCore; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Windows.Input; | |
using Xamarin.Forms; | |
using Xamarin.Forms_EFCore.DataAccess; | |
using Xamarin.Forms_EFCore.Helpers; | |
namespace Xamarin.Forms_EFCore.ViewModels { | |
public class EditPlayerViewModel : BaseViewModel { | |
private string teamName; | |
public string TeamName { | |
get { return teamName; } | |
set { | |
teamName = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string name; | |
public string Name { | |
get { return name; } | |
set { | |
name = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string position; | |
public string Position { | |
get { return position; } | |
set { | |
position = value; | |
OnPropertyChanged(); | |
} | |
} | |
private string jerseyNumber; | |
public string JerseyNumber { | |
get { return jerseyNumber; } | |
set { | |
jerseyNumber = value; | |
OnPropertyChanged(); | |
} | |
} | |
public ICommand SavePlayerCommand { get; private set; } | |
private int _playerId; | |
private DatabaseContext _context; | |
public EditPlayerViewModel(int playerId) { | |
_playerId = playerId; | |
// usage of using with db context | |
using (_context = new DatabaseContext()) { | |
var players = _context.Players.ToList(); | |
var player = _context.Players.Include(x => x.Team).SingleOrDefault(x => x.PlayerId == _playerId); | |
// player.Team = _context.Teams.Find(player.TeamId); | |
TeamName = player.Team.Title; | |
Name = player.Name; | |
Position = player.Position; | |
JerseyNumber = player.JerseyNumber.ToString(); | |
SavePlayerCommand = new Command(SavePlayer); | |
} | |
} | |
void SavePlayer() { | |
using (_context = new DatabaseContext()) { | |
var player = _context.Players.Find(_playerId); | |
player.Position = Position; | |
player.Name = Name; | |
player.JerseyNumber = Int32.Parse(JerseyNumber); | |
_context.Players.Update(player); | |
_context.SaveChanges(); | |
} | |
Application.Current.MainPage.Navigation.PopAsync(); | |
} | |
} | |
} |
In SavePlayer() method we are setting new values from our view to the player object that we get from database by playerId, and committing changes using SaveChanges() (nothing new).
Let's test this:

With this example we are done!
And that is it, we have our app done, we have implemented all CRUD operations using Entity Framework 2.0 with Xamarin.Forms.
Code for this example is available on the my GitHub repo more about it here:
https://github.com/almirvuk/Xamarin.Forms_EFCore
I hope this tutorial was helpful for you. My goal with this tutorial was to show CRUD examples using Entity Framework Core 2.0. If anything was blurry you can write me in the comment section down bellow. If this was helpful you can also write it, and share it with your friends.
Best regards! Almir Vuk
29 comments
Hi Almir,
ReplyDeleteThanks for sharing tha t valueable blog post. Adam Pedley blogged about that stuff in Feb '17 and also about how to migrate an existing PCL based XF App to .NET Standard.
https://xamarinhelp.com/entity-framework-core-xamarin-forms/
https://xamarinhelp.com/dot-net-standard-pcl-xamarin-forms/
Would appreciate if you could add those to links at the beginning of your blog. Thanks
Eric
Hi Eric,
DeleteYou are right, I just added them. :)
Best regards!
You mention PCL a lot here. Shouldn't that just be netstandard projects? Not PCL?
ReplyDeleteYou are right, we need to migrate from PCL to .NET Standard Library which I mentioned right at the beginning, I just edited those three word where PCL is mentioned instead of .NET Standard library,
DeleteThank you
It will be better if you also provide a simplified version of this tutorial. Just to make sure that EFCore 2.0 works on Xamarin.Forms (excluding UWP). For example, just use a single model Player, a simple UI and a simple database operation (Add just fine). Thank you!
ReplyDeleteYou can find that kind of tutorial on Xamarin Help from Adam Pedley
Deletehttps://xamarinhelp.com/entity-framework-core-xamarin-forms/
Best regards!
Thank you for your work on this tutorial. I have been trying to get Entity Framework to run with Xamarin for the past two weeks and now with your tutorial I finally have!
ReplyDeleteYou have saved me many more weeks of frustration, thank you.
Glad to hear that sir! Best regards!
DeleteThank yo, nice post.
ReplyDeleteSuper job Man, Thank a lot. Was very helpful to get in with EFCore for Xamarin.
ReplyDeleteThank you very much! Nice to hear that!
DeleteI tried to re-build your project using latest template from vs 2017 which does not use packages.config file, and when you deploy to the droid emulator i am getting this nasty error
ReplyDeleteD/Mono ( 7808): Assembly Loader probing location: 'System.Runtime.CompilerServices.Unsafe'.
F/monodroid-assembly( 7808): Could not load assembly 'System.Runtime.CompilerServices.Unsafe' during startup registration.
Looks like it is still active not resolved issue. Wonder if you encountered the same issue recently using EFCore
Have you looked here: https://github.com/aspnet/EntityFrameworkCore/issues/8922
Deleteyeah, don't feel to move back to use packages.config to fix this problem. Still acrive issue.
DeleteI agree, it seems like we will need to wait to this issue to be resolved :(
DeleteGreetings I try to execute the gintub project in visual studio 2017 and it tells me the following error
ReplyDeleteSeverity Code Description Project File Line Status suppressed
Error NU1201 The Xamarin.Forms_EFCore project is not compatible with uap10.0.10586 (UAP, Version = v10.0.10586). The Xamarin.Forms_EFCore project supports: netstandard2.0 (.NETStandard, Version = v2.0)
what will I be doing wrong
I will need to update that project code and target .net standard 2.0 in uwp project.
DeleteYou can also do that, or if you are not going to use UWP project you can simply unload it and use ios and android version. :)
Deletewhen I think the project does not indicate the pcl option
ReplyDeleteI create it directly with .NET Standard when I include the nugget Microsoft.EntityFrameworkCore.Sqlite ver 2.0.2 the android application stops working and sends the following error
Assembly Loader probing location: 'System.Runtime.CompilerServices.Unsafe'.
04-09 21: 29: 31.589 F / monodroid-assembly (3814): Could not load assembly 'System.Runtime.CompilerServices.Unsafe' during startup registration.
04-09 21: 29: 31.589 F / monodroid-assembly (3814): This might be due to an invalid debug installation.
04-09 21: 29: 31.589 F / monodroid-assembly (3814): A common cause is to 'adb install' the app directly instead of doing from the IDE.
Could you please tell me what I'm doing wrong
Thank you in advance for your response
excuse my english language
Hello and thanks for this great blogs. I'm a beginner in programming with Xamarin and using MVVM and EF.
ReplyDeleteI'm getting an exception on this.Database.EnsureCreated();
Exception text is :
Unhandled Exception:
System.InvalidOperationException: A suitable constructor for type 'Microsoft.EntityFrameworkCore.Internal.DiagnosticsLogger`1[Microsoft.EntityFrameworkCore.DbLoggerCategory+Infrastructure]' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor. occurred
I have no clue :-/
Can you give me a hint please ?
THanks :-)
regards
ReplyDeleteI was able to execute the example without any problem, thank you very much for sharing this very well-prepared example. Congratulations
What other samples would you recommend to continue learning
Thank you very much for this guide :)
ReplyDeleteThanks for this great tutorial. Hoping you could also make tutorial with dependency injection.
ReplyDeleteThat is also possible :) Thank you for reading my blog :)
DeleteHey Almir, thank you for this great tutorial. I learned so many new things but I was wondering is it possible to see tables of database like the whole database?
ReplyDeleteIt was really a nice post and I was really impressed by reading this .NET Online Course
ReplyDeleteThanks for this blog!
ReplyDeleteHowever, I have tried running the android application on Xamarin Live Player App on my phone and it comes up with an error of unhandled exception. Is this due to the config of the SQL database. How do I set this configuration of packages.config?
I am not sure what do you think by that, can you please explain your issue again? Thanks!
DeleteNice job man!
ReplyDeleteBut how can I implement M:N relation?