- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- Core Tech
Guided: Crafting a Library Management System Leveraging EF Core
In this Guided Code Lab, you'll delve into Entity Framework Core (EF Core), a robust Object-Relational Mapping (ORM) framework that is essential for .NET developers looking to integrate a sophisticated database persistence layer into their applications. EF Core simplifies data manipulation, allowing you to interact with your database using strongly-typed .NET objects, thereby streamlining database operations. Whether you're new to EF Core or looking to solidify your understanding, this lab is designed to provide you with a comprehensive and practical learning experience, setting a strong foundation for your future .NET development endeavors.
Lab Info
Table of Contents
-
Challenge
Welcome to the lab:
Welcome to the lab:
Guided: Crafting a Library Management System Leveraging EF Core
Entity Framework Core (EF Core) is a powerful Object-Relational Mapping (ORM) framework that offers significant advantages for .NET developers.
EF Core Benefits
---- #### Simplify DB Interactions EF Core is a powerful Object-Relational Mapping (ORM) framework for .NET, designed to simplify database interactions by mapping C# objects to database tables. This approach eliminates much of the need for SQL coding, allowing developers to work with databases using familiar C# constructs.LINQ Integration
Its integration with Language Integrated Query (LINQ) means that queries can be written in C#, making the code more readable and less prone to errors. EF Core streamlines database operations, enhancing productivity and making code easier to maintain.
Wide Range of DB Providers
A standout feature of EF Core is its support for a wide range of database providers. It is compatible with popular databases such as Microsoft SQL Server, SQLite, PostgreSQL, and MySQL, among others. This versatility enables developers to target different database systems without significant changes to the data access code, providing immense flexibility in deploying applications across various environments.
In this Guided Code Lab, you will be guided through the process of integrating EF Core into an existing Library Management System (LMS) project that currently uses a repository pattern for JSON file persistence.
This lab will cover essential steps such as adding EF Core to your project, creating a database context, and implementing an EF Core-based repository.
By the end of this lab, you will have successfully migrated to a more robust and scalable data access layer using EF Core, enhancing the overall functionality and maintainability of your application.
Specifically, You'll Learn
---- You'll learn how to: - Add EF Core to a project. - Configure the `DbContext` and define entity models. - Create a repository that leverages EF Core. - Use EF Core to initialize the database. - Easily modify your project to use the new repository.To begin the lab, click the right arrows.
-
Challenge
Overview
Overview
Before getting started, take a moment to get familiar with the lab's interface.
ℹ️ Lab Environment
----Explorer Tab
Access the filesystem quickly through the Explorer tab, marked by the stacked paper icon at the top left. Open this tab whenever you need to interact with the project files.
Source Control
This lab utilizes source control to assist you in navigating changes to files.
Should you encounter any difficulties with a specific step, you can access guidance through the Source Control tab, marked with the branch icon.
Within the main branch, you'll find the final state of each file. This allows you to either open the changed file to view the necessary changes or simply use the back arrow to revert the file to its final state.
⏯️ Running the Project
----The LMS CLI application accepts several commands for managing the books within the library.
To see the list of available commands, run the following in the Terminal window:
dotnet runYou should see a list of the following commands:
Commands: list List all Books add Add a Book update Update a Book delete Delete a BookAnd the desired command can be called by first appending
--.
Such as:dotnet run -- listWhich, at the moment, you will see that the library is empty.
To add a book to your library you will need to use the
addcommand, to see the available options for this command just pass in the--helpoption.dotnet run -- add --helpKnowing the options
--titleand--author, you can now add a book as follows:dotnet run -- add --title "Getting Things Done" --author "David Allen"Now you can rerun that
listcommand and see the book has been added to your library.🔎 Application Overview
---- The LMS command line application has been designed with a clear architectural separation of its components:Opening the Program.cs file, you'll see the following:
FileBookRepositoryrepresents the file system data layer.BookServiceencapsulates the business logic.Promptsmanages the user interface interactions.
While you're welcome to explore the
BookServiceandPromptsto understand their roles, your main focus will be on transformingFileBookRepositoryto leverage EF Core for database interactions.FileBookRepository
Opening the /Data/FileBookRepository.cs file, you'll notice that it extends
BaseBookRepository. This base class handles validations and messages, providing foundational functionality.You're also welcome to explore this class, but the primary focus will be on the
overriddenmethods withinFileBookRepository.These methods are crucial, as they are providing the specific file-based implementations for each command called by the base class.
As you progress through the following steps, you'll be tasked with creating an EF Core version of this repository, adapting these implementations to a database context.
Click the right arrows to begin migrating to an EF Core Repository.
-
Challenge
EF Core Project Configuration
EF Core Project Configuration
To get started, you first need to configure your project to work with EF Core.
The following tasks will walk you through adding EF Core functionality to the LMS project.
1️⃣ Adding EF Core Packages
---- In order for your project to use EF Core, you must first add the `Microsoft.EntityFrameworkCore` packages.For this initial version and ease of use, you will also include the SQLite provider for EF Core.
These packages have already been included in the current project, but for reference for future projects, these can be added from the command line:
dotnet add package Microsoft.EntityFrameworkCore dotnet add package Microsoft.EntityFrameworkCore.Sqlite2️⃣ Creating the DbContext
---- Now that the project includes the EF Core framework, the next step is to create a `DbContext`.This context serves as the main link between your application and the database, handling tasks like querying and saving data.
A basic
LibraryContextclass has been created and now needs to be configured to extend the DbContext.Open the /Data/LibraryContext.cs file.
At the top of this file, add a
usingstatement to include the EF Core namespace:using Microsoft.EntityFrameworkCore;Now, refactor the
LibraryContextclass delcaration to extend fromDbContext. This allows yourLibraryContextto inherit all of the capabilities of EF Core'sDbContext:public class LibraryContext : DbContextRole and Functionality of DbContext
---- The **DbContext** acts as the bridge between your C# application and the database. It's essentially a class that manages the database connections and is used to query and save data to the database.Managing Database Connections:
Combined with the EF Core provider, the DbContext manages the connection to the database.
Querying the Database:
Through the DbContext, you can perform LINQ queries against your database. These queries are translated into SQL queries by the provider and sent to the database.
Change Tracking:
DbContext keeps track of changes made to added entities and those retrieved from the database. This is crucial for updating the database with changes made to the data in your application.
Saving Data:
When you modify in your application, the DbContext tracks these changes and applies them to the database when you call the
SaveChanges()method.Configuring Entity Models:
DbContext also plays a role in configuring the model. This includes mapping the classes (entities) to database tables, configuring relationships, keys, and other model configurations. This can be done using data annotations or the Fluent API.
3️⃣ Configuring for SQLite
---- The `LibraryContext` is now ready to configure the connection to the SQLite database.Inside the body of the
LibraryContextclass, override theOnConfiguringmethod to configure the database connection string.protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite("Data Source=library.sqlite");For a simple application, configuring the database connection directly within the
DbContextusing theOnConfiguringmethod might suffice.However, for more complex applications, such as those built with ASP.NET that leverage its built-in dependency injection framework, configuring the database connection using the
AddDbContextmethod is the preferable approach. This method aligns with the principles of Dependency Injection (DI) and offers greater flexibility and maintainability.4️⃣ Adding the DbSet<>
---- With the `LibraryContext` now configured to connect to the database, the next step is to define the entities that the context will manage. This is where `DbSet<>` comes into play.A
DbSet<>in Entity Framework is essentially a collection of entities that corresponds to a table in the database. For your LMS application, you need to manage a collection ofBookentities, which reflects the Books table in your database.To declare this collection, add a
DbSet<Book>property to yourLibraryContext. This should be done above theOnConfiguringmethod. Such as:public DbSet<Book> Books { get; set; }The
Booksproperty here is aDbSetofBookentities. It serves as the crucial link between your application's code and the Books table in the database. Whenever you interact with thisBooksDbSet, you are effectively working with the Books table. Adding aBookto this DbSet and saving changes will add a new row to the table, querying this DbSet fetches data from the table, and so on.This
DbSetsimplifies the process of performing database operations, allowing you to work with your data as a collection of C# objects, which the Entity Framework then translates into database operations.Click the right arrows to configure the
Program.csfile to use thisLibraryContext. -
Challenge
Initialize the Database
Initialize the Database
Now that you have declared the
LibraryContext, it can be incorporated into your application's logic in theProgram.csfile.Open Program.cs file.
1️⃣ Create a Context Instance
---- Just under the `using` statements, create an instance of the `LibraryContext`: ```c# var context = new LibraryContext(); ```2️⃣ Initialize the Database
---- The `LibraryContext` in your application plays a crucial role in managing all interactions with the application's database. It specifically handles the entities you've declared as `DbSet<>` properties within the context.These
DbSet<>properties represent the tables in your database, corresponding to different types of data your application will manage, such as books, users, etc.One essential task for
LibraryContext, especially when your application starts for the first time or when you're deploying it, is to ensure that the database and the necessary tables are correctly set up and exist. This is vital for the smooth operation of your application, as it relies on these tables to store and retrieve data.To achieve this, you can utilize the
EnsureCreated()method on your context's database property. This method checks if the database exists, and if it doesn't, it creates the database and all the tables defined in yourDbSet<>properties.Just after initializing the
contextvariable, add the following:context.Database.EnsureCreated();By calling
context.Database.EnsureCreated();, you instruct the context to establish a connection with the database and ensure that the database structure is in place as defined in your context. This method is particularly handy during development and testing phases. However, for production environments, especially when dealing with database schema migrations, it's more common to use EF Core Migrations to manage database creation and updates.⏭️ Verify the Database is Being Created
---- To verify that the database is correctly being created by `EnsureCreated()`, run the application in its current state. This will initialize the `LibraryContext` and run the `EnsureCreated()` method. ```bash dotnet run ``` To view the contents of this database file, you can use the `sqlite3` command-line tool. From the **Terminal** run the following commands: ```bash ./sqlite3 library.sqlite .tables ./sqlite3 library.sqlite ".schema books" ```These commands will show that the Books table has been created and that it's schema contains the 3
Bookproperties ofIdTitle, andAuthorAlso, notice the extra configuration of the
Idcolumn. EF Core has automatically created this field with the PRIMARY KEY AUTOINCREMENT. This is based on the naming convention ofId.BookIdwould have also worked.Now that the
LibraryContextis configured working correctly, click the right arrows to start creating theEFCoreBookRepositoryclass. -
Challenge
Create the EF Core Repository
Create the EF Core Repository
Now that you've setup your project to utilize EF Core, your next step is to develop the
EFCoreBookRepositoryThis new repository will replace the current file-basedFileBookRepository.Opening the /Data/EFCoreBookRepository.cs file, you'll find the basic class structure already in place, with placeholders to override each abstract method inherited from the
BaseBookRepository.As of now, these methods are throwing
NotImplementedException. Ready for you to implement the EF Core interaction logic.Your task is to transform these placeholders with concrete EF Core implementations. This effort will transition your LMS from its current file-based architecture to a more robust database-oriented system.
1️⃣ Inject the LibraryContext
---- The initial step to integrate EF Core functionality into this class is to enable the injection of the `LibraryContext`.This process involves two key actions:
- Declaring a private field to hold the context
- Modifying the constructor to accept the
LibraryContextas a parameter
Start by declaring the field just after the class declaration:
public class EFCoreBookRepository : BaseBookRepository { private readonly LibraryContext _context; . . . }Next, refactor the constructor to accept a
LibraryContextparameter.
In the constructor body, assign the context instance to the_contextfield:public EFCoreBookRepository(LibraryContext context) { _context = context; }With these changes, your
EFCoreBookRepositoryis now equipped to interact using the_contextLibraryContextthat was previously configured.2️⃣ Implement GetAllBooksImpl()"
---- To begin implementing the repository, start with the `GetAllBooksImpl()` method. This method's purpose is to fetch all `Book` entities that are stored in the database.As you recall, within the
LibraryContext, you declared theDbSet<Book> Books. This DbSet represents the collection of allBookentities in the database.In the
GetAllBooksImpl()method, you simply need to return this collection. The method's implementation should be:protected override IEnumerable<Book> GetAllBooksImpl() { return _context.Books; }Alternate Return Options
---- #### Return Options When retrieving data from a **DbSet** in EF Core, you have several return type options, each suited to different needs of your application.Such as:
IEnumerable<>: Utilized in the current implementation. IEnumerable<> offers deferred execution. This type will delay execution until the IEnumerable<> is enumerated. While not as important in this simple application, can lead to more efficient memory usage in more complex applications.
IQueryAble<>: This type also defers query execution but provides the added felxibility of allowing the calling method to append additional query criteria before executing against the database.
List<>: Constrasting with the deferred execution of the previous types, using List<> as a return type results in immediate query execution. Implementing it is as simple as appending
.ToList()to theBooksDbSet. This is most effective for smaller, manageable datasets where in-memory operations are not a concern.Others: Beyond these options, EF Core offers a diverse range of return types, providing flexibiltiy to suit various scenarios.
3️⃣ Implement AddBookImpl()
---- In addition to listing the available books, you also need a means of adding books to the database.This is accomplished in the method
AddBookImpl().Adding the
bookto the DbSet is very straight forward with theAdd()method which accepts aBookobject. At the top of theAddBookImpl(Book book)method add a call to theAdd(book):_context.Books.Add(book);The thing to remember about this
Add()method is that it tells theLibraryContextto begin tracking the entity. At this point the entity has not been persisted to the database.In order to save the newly added entity to the database you need to tell the context to
SaveChanges():_context.SaveChanges();Notes: Implementation Considerations: Firstly, this is a fairly naive implementation. For a real-world scenario you will want to address further enhancements and optimizations such as error handling.
Handling of Book Id, Secondly, it's crucial to note that the new
bookobject being passed into this method does not contain anId. Similiar to how theFileBookRepositorygenerated a unique identifier for eachBook, this repository also needs to ensure a unique identifier. However, unlike theFileBookRepository, EF Core simplifies this process.If you recall when calling the
EnsureCreated()method, TheIdfield of theBooktable was marked as PRIMARY KEY AUTOINCREMENT. This means that EF Core, in conjunction with the database, automatically assigns a uniqueIdto each newBookwhenSaveChanges()is called.This allows you to return the saved
bookback to the calling method which will be able to utilize the newIdas it sees fit.To complete the
AddBookImpl(), return thebookto the caller:return book;⏭️ Testing Functionality
---- You now have enough functionality to run the LMS commands `add` and `list`.Before implementing the remaining methods, take a moment to test this current functionality.
One last step remains in order to switch over to this new EF Core repository.
Going back to the /Program.cs file. You need to refactor the
bookRepositoryinitialization to use the newEFCoreBookRepository, which should now look like:IBookRepository bookRepository = new EFCoreBookRepository(context);The EF Core implementation is now ready to be tested. At least for the
addandlistcommands.From the Terminal rerun the
addbook command:dotnet run -- add --title "Getting Things Done" --author "David Allen"and then call the
listcommand to see if it has been added.dotnet run -- listYou should see that the book has been added to your SQLite database.
Congratulations on successfully migrating these two commands.
Click the right arrows to implement the remaining methods. -
Challenge
Completing the EFCoreBookRepository
Completing the EFCoreBookRepository
You have successfully implemented two of the
EFCoreBookRepositorymethods. There are just three more methods to implement:GetBookByIdImpl(),UpdateBookImpl(), andDeleteBookByIdImpl().Returning to the /Data/EFCoreBookRepository.cs file.
1️⃣ Querying a DbSet with LINQ
---- Starting with the `GetBookByIdImpl()` method.When you need to filter a collection based on specific criteria in EF Core, LINQ queries are an effective tool.
These queries are translated into SQL and executed against the database, providing a seamless and efficient way to retrieve data.
In the case of the
GetBookByIdImpl(int bookId)method, you need to query theBooksDbSet to find aBookwhoseIdmatches the providedbookId.LINQ offers several ways to perform this search, and one common approach is to use the
FirstOrDefault()method. This method iterates over theBookcollection and returns the first book that satisfies the condition.
Implement the following in theGetBookByIdImpl()method:return _context.Books.FirstOrDefault(b => b.Id == bookId);If no book matching that criteria is found,
FirstOrDefault()returns the default value for the reference type, which isnull. This behavior aligns with the method's nullable return type ofBook?.2️⃣ Updating Entities
With the implementation of `GetBookByIdImpl()` complete, the `BaseBookRepository` can now leverage this method in its `UpdateBook()` method.After retrieving the specific book, the
UpdateBook()will call theUpdateBookImpl()of this EF Core repository. This method will perform the actual persistance of the modifiedBookentity.In the
UpdateBookImpl()method, you inform theLibraryContextthat thebookentity needs to be updated and then have the contextSaveChanges(). The finalUpdateBookImpl()implementation should now look like:protected override Book? UpdateBookImpl(Book book) { _context.Books.Update(book); _context.SaveChanges(); return book; }3️⃣ Retrieving Entities by Identifier
The final functionality to implement in the current phase of the LMS project is the removal of a book from the library. This task is accomplished by the `DeleteBookByIdImpl()` method.To remove a specific book, you first need to locate it within your database. In this implemenation, you'll use the
Find()method, a convenient way to retrieve an entity by its primary key value. Replace the method's placeholder implementation with the following:protected override bool DeleteBookByIdImpl(int bookId) { var book = _context.Books.Find(bookId); _context.Books.Remove(book); _context.SaveChanges(); return true; }Since there's no book entity to return after the deletion, this method returns a boolean indicating the success or failure of the operation. This implementation is fairly naive, assuming that the book will be found and was removed successfully. For a more robust solution, you can improve this with some validation and error handling.
You have now equipped your
EFCoreBookRepositorywith the capability to support all existing commands within your Library Management System (LMS). You should thoroughly test each command to ensure they are functioning as expected with the new repository integration.Looking for an additional challenge? Consider enhancing your LMS by introducing a new command. A valuable feature to add would be a search functionality, enabling users to easily find books by their title or author. This not only tests your newly acquired skills but also significantly improves the user experience of your LMS.
To complete this lab, click the right arrows.
-
Challenge
Congratulations
Congratulations
Congratulations on implementing the
EFCoreBookRepositoryfor your Library Management System (LMS)! Through this process, you've not only equipped your LMS with essential data management capabilities but also acquired fundamental skills in integrating and utilizing Entity Framework Core in a project.This accomplishment not only enhances your project but also arms you with valuable EF Core skills that you can apply to future projects, paving the way for more advanced and efficient data management solutions.
Next Steps
If you're interested in diving deeper, here are some valuable areas to explore next:
- Advanced Schema Definition:
- Learn how to gain more control over how tables and fields are declared in your database.
- Explore techniques like the Fluent API and Data Annotations.
- Enhanced Validation:
- Implement robust validation logic to ensure data integrity and consistency.
- Approaches for Database Design
- Code-First: Used by this lab, start with C# classes and let EF Core create the database.
- Database-First: Begin with a database and use EF Core to generate the corresponding data models.
- Data Migrations: EF Core offers a powerful feature that allows you to manage changes to your database schema over time.
The best as you continue your EF Core learning journey. Looking forward to seeing what you create.
- Advanced Schema Definition:
About the author
Real skill practice before real-world application
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.