Blog for Junior Developers C#/.NET

Thursday, August 08, 2024

If we want to work with a database in C#, we have several options. We can use the once more popular ADO.NET or one of several ORM frameworks. In ADO.NET you have to write pure SQL, and in ORM frameworks you can rely on C# classes that generate SQL themselves underneath. In my experience, .NET developers most often use Entity Framework, Entity Framework Core and Dapper. In today's article, I wanted to introduce you to Entity Framework Core, which in my opinion is currently the best.

introduction-to-entity-framework-core-cooperation-with-the-database-in-csharp.jpg

ORM


Let's start with what ORM is. ORM stands for Object-Relational Mapping. It is a way of mapping an object-oriented architecture to a relational database.

ORM allows us to create database queries using an object-oriented programming paradigm, i.e. we can operate on ordinary objects, e.g. in C#, and calls to various methods or properties will be converted into SQL queries.

Suppose we want to add information about a new book to the database. Thanks to the ORM, instead of writing the entire query, that is

INSERT INTO Books (column1, column2) 
VALUES (value1, value2);

Just call the method:

Books.Add(book);

And pass the object we want to add.

From a programmer's point of view, this is an ideal solution, thanks to which a programmer, even without typical database knowledge, can work with the database without any problems. Using an object-oriented language is more natural for us programmers. Of course, it is worth knowing at least the basic SQL syntax, thanks to which you will be able to optimize these queries, but this is not mandatory knowledge at the beginning.

In addition, of course, writing queries using an object-oriented language is much faster, sometimes it is enough to call one method or property instead of writing many lines in SQL. What's more, you definitely won't make any typos, which sometimes happens in SQL. In addition, ORMs have many advanced functions such as transaction support, migrations, and connection pool support.

When it comes to the disadvantages of ORM, the first thing that comes to mind is the fact that the created queries may not be fast and optimal, but I guarantee you that with the right knowledge, your queries will be just that. You just need to remember a few basic rules.


Entity Framework


Entity Framework itself is an open source ORM framework available on the .NET Framework platform. It is currently one of the most downloaded libraries from nuget. It allows C# programmers to access the database without having to write pure SQL, just using objects.

It converts method calls and context properties into SQL, so you can easily create database applications without knowing SQL queries. Of course, if you need it, in addition to objects, you can also write your own SQL.

The Entity Framework itself allows us to create a database in 4 ways, i.e. we have a code first, code first from database, database first and model first approach. It allows us to map models to tables in the database.

You can create models using various conventions, and you can overwrite each convention thanks to the appropriate configuration, thanks to which we have full control over the database we create. We can create configurations using, among others, data annotations and Fluent API.

Its main goal is to increase the productivity of programmers when working with databases. We can generate queries and commands to read and write to the database. If you want to query a database, you can use the LINQ syntax. Entity Framework will execute the appropriate query and map the result to the objects of your model.

It supports all relationships such as 1 to 1, 1 to many, or many to many. In addition, it allows you to create parameterized queries, tracks changes to objects in memory, and allows you to read, add, update and delete data. It allows you to create indexes, migrations, and transactions. It also supports work with procedures, views and functions.

The highest version of Entity Framework is version 6, which was originally released in 2013. Only minor improvements to this version have been developed since then, as Entity Framework has been replaced by Entity Framework Core since 2016. This has caused a lot of confusion, but this is the Entity Framework Core that is currently being developed and it is recommended to use this version of the framework. You need to note that Entity Framework and Entity Framework Core are two separate libraries.


Entity Framework Core


Entity Framework Core was supposed to be the next version of Entity Framework, initially even called Entity Framework 7, but in fact Entity Framework Core was written from scratch.

It is a lightweight, extensible, open source, cross-platform framework that is part of Microsoft .NET Core. It was created to be easier to use and have better performance than its predecessor Entity Framework, and as tests show, it has achieved this. Entity Framework Core is much faster than Entity Framework itself. It is available as a separate nuget, which sometimes causes a little confusion.

Entity Framework Core supports databases such as SQL Server, SQLite, PostgreSQL, MySQL, Oracle and Firebird. Although it is primarily intended to work with applications based on .NET Core, versions 2.1 and 3.1 can also be used with applications that are developed on the .NET 4.7.2 platform. and higher. Entity Framework Core 5.0 no longer supports applications written in the .NET Framework.

For new projects, it is recommended to use the latest version of Entity Framework Core. You can also use it in any application, be it web, desktop, console or even mobile.

In Entity Framework Core you have 2 approaches to creating a database, i.e. code first and database first. You can also create a database from scratch using your classes in C# code, or you can start working with a database that has already been created.

After all, when it comes to the main idea of ​​these 2 frameworks, it is very similar, it is not that they are two completely different worlds, it is just that they differ a little in some places in syntax, but more or less both frameworks are based on this alone.


Practice – discussion of the project


Let's move on to practice for a moment. I will now show you what basic database queries and commands look like in Entity Framework Core.

We will be working on a project called Bookstore where I have installed packages for Entity Framework Core. It is a simple console application that consists of several classes.

This is what the context class looks like:

namespace Bookstore
{
    class ApplicationDbContext : DbContext
    {
        public DbSet<Book> Books { get; set; }
        public DbSet<Category> Categories { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var builder = new ConfigurationBuilder()
                .AddJsonFile($"appsettings.json", true, true);

            var config = builder.Build();

            optionsBuilder
                .UseSqlServer(config["ConnectionString"]);
        }
    }
}

It contains 2 DbSets Books and Categories. These are mappings of 2 tables in the database with the same names. This is what our entities look like:

namespace Bookstore
{
    public class Book
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }

        public int? CategoryId { get; set; }
        public Category Category { get; set; }
    }
}
  

namespace Bookstore
{
    public class Category
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public ICollection<Book> Books { get; set; } = new HashSet<Book>();
    }
}

The Books entity has properties such as Id, Name, Price and a reference to Category. In turn, the Category entity has Id, Name properties and a Books collection navigation property. Also, a book can have 1 category, but 1 category can be assigned to multiple books.

Finally, the connection string in the appsettings.json file:

{
  "ConnectionString": "Server=.\\SQLEXPRESS;Database=Bookstore;User Id=x;Password=y;"
}

We can switch to the Program class and the main Main method, where we will write some basic queries and commands using Entity Framework Core.

namespace Bookstore
{
    class Program
    {
        static async Task Main(string[] args)
        {
        }
    }
}


Adding a new record to the database


To add a new record to the database using Entity Framework Core, you first need to create a new object, in our example an instance of the Book class.

var book = new Book { Name = "Książka 1", Price = 10 };

We do not have to fill in the Category property, it is not required, so for the simplicity of the example, we will skip setting the category itself for now (it will be useful for subsequent, more advanced examples). The ID will be generated automatically by the database. To add such a book, we need a new instance of a class implementing the DbContext class, in our case ApplicationDbContext.

using (var context = new ApplicationDbContext())
{

}

Then, inside this using, we call the Add method on the context and DbSet Books and pass our previously generated object.

context.Books.Add(book);

We save changes to the database.

await context.SaveChangesAsync();

At this point, the record will actually be added to the database.

This is what the whole code will look like:

namespace Bookstore
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var book = new Book { Name = "Książka 1", Price = 10 };

            using (var context = new ApplicationDbContext())
            {
                context.Books.Add(book);
                await context.SaveChangesAsync();

            }
        }
    }
})


Retrieving data from the database


To show you that this data will actually be added to the database, we can immediately first download it using EF Core and then display it in the console of our application. First, we will prepare a new list of Book objects:

var books = new List<Book>();

And then, right after saving the first book, we will download information about all books from the database. To do this, we will refer to the context again, then the Books DbSet and call the ToListAsync() method.

books = await context.Books.ToListAsync();

Here, all records from the Books table will first be retrieved and then assigned to the books variable. Below, we can best display these records on the console using a foreach loop.

foreach (var item in books)
{
    Console.WriteLine($"Id: {item.Id}. Książka: '{item.Name}' - {item.Price:0.00} PLN.");
}

Using string interpolation, we display first the id of a given book, then its name and price.

Now we will run the application and verify whether the data has actually been correctly added to the database.

introduction-to-entity-framework-core-cooperation-with-the-database-in-csharp-1.jpg

As you can see, we have 1 record in the database. Everything seems to be working properly. All properties have been completed. If we run the applications again now:

introduction-to-entity-framework-core-cooperation-with-the-database-in-csharp-2.jpg

This will also add another record. A unique ID was generated for each of them. Also, this is how adding and displaying records works. Whole code:

namespace Bookstore
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var book = new Book { Name = "Książka 1", Price = 10 };
            var books = new List<Book>();

            using (var context = new ApplicationDbContext())
            {
                context.Books.Add(book);
                await context.SaveChangesAsync();

                books = await context.Books.ToListAsync();
            }

            foreach (var item in books)
            {
                Console.WriteLine($"Id: {item.Id}. Książka: '{item.Name}' - {item.Price:0.00} PLN.");
            }
        }
    }
}


Updating data in the database


In order to update data using EF Core, let's start by downloading one record that already exists and which we will update in a moment.

var bookToUpdate = await context.Books.FindAsync(1);

Using the FindAsync method, we will retrieve the record with Id 1 from the database. Then we will change the value of this book to 99.

bookToUpdate.Price = 99;

And we will save the changes made to the database.

await context.SaveChangesAsync();

We can run the application and verify the changes.

introduction-to-entity-framework-core-cooperation-with-the-database-in-csharp-3.jpg
As you can see, now the book with Id 1 has a value of PLN 99, so this record has been updated correctly. Similarly, we can download a book about Id 2 and update its name.

var bookToUpdate = await context.Books.FindAsync(2);
bookToUpdate.Name = "Ksiażka 2";

introduction-to-entity-framework-core-cooperation-with-the-database-in-csharp-4.jpg
As you can see, the name has also been updated. This is what the whole code looks like:

namespace Bookstore
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var books = new List<Book>();

            using (var context = new ApplicationDbContext())
            {
                var bookToUpdate = await context.Books.FindAsync(2);
                bookToUpdate.Name = "Ksiażka 2";

                await context.SaveChangesAsync();

                books = await context.Books.ToListAsync();
            }

            foreach (var item in books)
            {
                Console.WriteLine($"Id: {item.Id}. Książka: '{item.Name}' - {item.Price:0.00} PLN.");
            }
        }
    }
}


Deleting data from the database


Finally, I will show you how to delete data from the database using Entity Framework Core. To do this, we call the Remove method again on the context and a specific DbSet, passing the object we want to remove. Here you just need to fill in the object ID.

context.Books.Remove(new Book { Id = 1 });

We then save these changes to the database.

await context.SaveChangesAsync();

I'm launching the application now.

introduction-to-entity-framework-core-cooperation-with-the-database-in-csharp-5.jpg

Notice that there is no longer a book for Id 1 in the database. There is only a book for Id 2. Full code:

namespace Bookstore
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var books = new List<Book>();

            using (var context = new ApplicationDbContext())
            {
                context.Books.Remove(new Book { Id = 1 });
                await context.SaveChangesAsync();

                books = await context.Books.ToListAsync();
            }

            foreach (var item in books)
            {
                Console.WriteLine($"Id: {item.Id}. Książka: '{item.Name}' - {item.Price:0.00} PLN.");
            }
        }
    }
}



Entity Framework Core School


If you would like to learn Entity Framework Core from the inside, how to create fast applications and efficient database queries, consider joining the Entity Framework Core School - [here].


SUMMARY


This is what the 4 simplest queries and commands in Entity Framework Core look like. We didn't write a single line of SQL and our data in the database was changed. If you haven't had any contact with this framework yet, you definitely need to catch up. In the next articles, I will show some good practices in using this framework. It has great capabilities, but when writing queries in EF Core, you must remember a few basic rules that will make your queries quick, but this is a topic for another article.

If you liked this article, be sure to join my community. Sign up for the free newsletter, where every week I share valuable materials, especially regarding C# and the .NET platform (free subscription - newsletter).

Author of the article:
Kazimierz Szpin

KAZIMIERZ SZPIN
Software Developer C#/.NET, Freelancer. Specializes in ASP.NET Core, ASP.NET MVC, ASP.NET Web API, Blazor, WPF and Windows Forms.
Author of the blog CodeWithKazik.com

Previous article - Programming and Studies. Does a .NET Developer Need Studies?
Next article - Blazor School - Online Course
Dodaj komentarz

Search engine

© Copyright 2024 CodeWithKazik.com. All rights reserved. Privacy policy.
Design by Code With Kazik