Featured resource
2025 Tech Upskilling Playbook
Tech Upskilling Playbook

Build future-ready tech teams and hit key business milestones with seven proven plays from industry leaders.

Check it out
  • Lab
    • Libraries: If you want this lab, consider one of these libraries.
    • Core Tech
Labs

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 platform
Lab Info
Level
Intermediate
Last updated
Dec 22, 2025
Duration
45m

Contact sales

By clicking submit, you agree to our Privacy Policy and Terms of Use, and consent to receive marketing emails from Pluralsight.
Table of Contents
  1. 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.

  2. 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 run
    

    You should see a list of the following commands:

    Commands:
      list    List all Books
      add     Add a Book
      update  Update a Book
      delete  Delete a Book
    

    And the desired command can be called by first appending --.
    Such as:

    dotnet run -- list
    

    Which, at the moment, you will see that the library is empty.

    To add a book to your library you will need to use the add command, to see the available options for this command just pass in the --help option.

    dotnet run -- add --help
    

    Knowing the options --title and --author, you can now add a book as follows:

    dotnet run -- add --title "Getting Things Done" --author "David Allen"
    

    Now you can rerun that list command 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:

    • FileBookRepository represents the file system data layer.
    • BookService encapsulates the business logic.
    • Prompts manages the user interface interactions.

    While you're welcome to explore the BookService and Prompts to understand their roles, your main focus will be on transforming FileBookRepository to 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 overridden methods within FileBookRepository.

    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.

  3. 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.Sqlite 
    
    2️⃣ 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 LibraryContext class 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 using statement to include the EF Core namespace:

    using Microsoft.EntityFrameworkCore;
    

    Now, refactor the LibraryContext class delcaration to extend from DbContext. This allows your LibraryContext to inherit all of the capabilities of EF Core's DbContext:

    public class LibraryContext : DbContext
    
    Role 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 LibraryContext class, override the OnConfiguring method 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 DbContext using the OnConfiguring method 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 AddDbContext method 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 of Book entities, which reflects the Books table in your database.

    To declare this collection, add a DbSet<Book> property to your LibraryContext. This should be done above the OnConfiguring method. Such as:

    public DbSet<Book> Books { get; set; }
    

    The Books property here is a DbSet of Book entities. It serves as the crucial link between your application's code and the Books table in the database. Whenever you interact with this Books DbSet, you are effectively working with the Books table. Adding a Book to 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 DbSet simplifies 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.cs file to use this LibraryContext.

  4. Challenge

    Initialize the Database

    Initialize the Database

    Now that you have declared the LibraryContext, it can be incorporated into your application's logic in the Program.cs file.

    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 your DbSet<> properties.

    Just after initializing the context variable, 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 Book properties of Id Title, and Author

    Also, notice the extra configuration of the Id column. EF Core has automatically created this field with the PRIMARY KEY AUTOINCREMENT. This is based on the naming convention of Id. BookId would have also worked.

    Now that the LibraryContext is configured working correctly, click the right arrows to start creating the EFCoreBookRepository class.

  5. 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 EFCoreBookRepository This new repository will replace the current file-based FileBookRepository.

    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:

    1. Declaring a private field to hold the context
    2. Modifying the constructor to accept the LibraryContext as 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 LibraryContext parameter.
    In the constructor body, assign the context instance to the _context field:

    public EFCoreBookRepository(LibraryContext context)
    {
        _context = context;
    }
    

    With these changes, your EFCoreBookRepository is now equipped to interact using the _context LibraryContext that 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 the DbSet<Book> Books. This DbSet represents the collection of all Book entities 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 the Books DbSet. 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 book to the DbSet is very straight forward with the Add() method which accepts a Book object. At the top of the AddBookImpl(Book book) method add a call to the Add(book):

    _context.Books.Add(book);
    

    The thing to remember about this Add() method is that it tells the LibraryContext to 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 book object being passed into this method does not contain an Id. Similiar to how the FileBookRepository generated a unique identifier for each Book, this repository also needs to ensure a unique identifier. However, unlike the FileBookRepository, EF Core simplifies this process.

    If you recall when calling the EnsureCreated() method, The Id field of the Book table was marked as PRIMARY KEY AUTOINCREMENT. This means that EF Core, in conjunction with the database, automatically assigns a unique Id to each new Book when SaveChanges() is called.

    This allows you to return the saved book back to the calling method which will be able to utilize the new Id as it sees fit.

    To complete the AddBookImpl(), return the book to 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 bookRepository initialization to use the new EFCoreBookRepository, which should now look like:

    IBookRepository bookRepository = new EFCoreBookRepository(context);
    

    The EF Core implementation is now ready to be tested. At least for the add and list commands.

    From the Terminal rerun the add book command:

    dotnet run -- add --title "Getting Things Done" --author "David Allen"
    

    and then call the list command to see if it has been added.

    dotnet run -- list
    

    You 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.

  6. Challenge

    Completing the EFCoreBookRepository

    Completing the EFCoreBookRepository

    You have successfully implemented two of the EFCoreBookRepository methods. There are just three more methods to implement: GetBookByIdImpl(), UpdateBookImpl(), and DeleteBookByIdImpl().

    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 the Books DbSet to find a Book whose Id matches the provided bookId.

    LINQ offers several ways to perform this search, and one common approach is to use the FirstOrDefault() method. This method iterates over the Book collection and returns the first book that satisfies the condition.
    Implement the following in the GetBookByIdImpl() 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 is null. This behavior aligns with the method's nullable return type of Book?.

    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 the UpdateBookImpl() of this EF Core repository. This method will perform the actual persistance of the modified Book entity.

    In the UpdateBookImpl() method, you inform the LibraryContext that the book entity needs to be updated and then have the context SaveChanges(). The final UpdateBookImpl() 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 EFCoreBookRepository with 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.

  7. Challenge

    Congratulations

    Congratulations

    Congratulations on implementing the EFCoreBookRepository for 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.

About the author

Jeff Hopper is a polyglot solution developer with over 20 years of experience across several business domains. He has enjoyed many of those years focusing on the .Net stack.

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.

Get started with Pluralsight