Data Access in ASP.NET Core using EF Core (Database First)

You are currently viewing Data Access in ASP.NET Core using EF Core (Database First)

In my previous tutorial Data Access in ASP.NET Core 5 using EF Core 5 (Code First), I covered the basics of Entity Framework and DbContext and showed you how to use the Code First technique to generate a new database using the entity models. You can use Code First approach if you are starting a new project but it is not always the case in the real world. Sometimes you already have a database and you have to use Entity Framework with an existing database. Entity Framework Core support this approach as well and in this tutorial, I will demonstrate different techniques for generating DbContext and model classes from an existing database.

Getting Started with EF Core using Existing Database

For this tutorial, I will be using the following database that has the Customers, Products, Orders, and ProductOrders table with a standard parent-child relationship.

Entity-Framework-Core-Entity-Model-Db-Design

Create a standard ASP.NET Core 5 MVC Web Application and install Entity Framework Core and SQL Server Data Provider by installing Microsoft.EntityFrameworkCore.SqlServer NuGet package. We also need to install Microsoft.EntityFrameworkCore.Design and Microsoft.EntityFrameworkCore.Tools to perform some Entity Framework Core design-time development tasks such as generating the DbContext and Entity Models from an existing database.

Generating Models from an Existing Database using EF Core

Entity Framework Core doesn’t support the visual designer (.edmx file) for creating models which we were using in the old version of Entity Framework (EF 6). In Entity Framework Core, we need to generate Model classes from an existing database using the following two options.

  1. If you are using Visual Studio, you can use the Package Manager Console (PMC) tools for Entity Framework Core
  2. If you are not using Visual Studio, you can use the cross-platform EF Core Command-line Tools from a command prompt

For this tutorial, I am using Package Manager Console Tools so the main command you need to reverse engineer the models from the existing database schema is Scaffold-DbContext. Please note that for Scaffold-DbContext to generate an entity type, the database table must have a primary key. This command has many options to customize the generated code. The following table shows all available parameters for Scaffold-DbContextcommand.

READ ALSO:  Implement Features Management in ASP.NET Core Apps
ParameterDescription
-Connection <String>This parameter specifies the connection string to the database. If you are using ASP.NET Core 2.x or higher version, then you can also give the value as name=<name of connection string> in which case the name comes from the configuration sources that are set up for the project. This is a positional parameter and is required.
-Provider <String>This parameter specifies the database provider we want to use. We normally provide the name of the NuGet package, for example, Microsoft.EntityFrameworkCore.SqlServer can be used for the SQL Server database. This is a positional parameter and is required.
-OutputDir <String>This command specifies the directory to put the generated files in. This path is relative to the project directory.
-ContextDir <String>This parameter specifies the directory to put the DbContext file in. This path is also relative to the project directory.
-Namespace <String>The parameter is added in Entity Framework Core 5.0 and it specifies the namespace to use for all generated classes. By default, the root namespace and the output directory will be used.
-ContextNamespace <String>The parameter is also added in Entity Framework Core 5.0 and it specifies the namespace to use for the generated DbContext class. This parameter overrides –Namespace which means you can have a different namespace for DbContext generated class than the generated models.
-Context <String>This parameter specifies the name of the DbContext class to generate.
-Schemas <String[]>The schemas of tables to generate entity types for. If this parameter is omitted, all schemas are included.
-Tables <String[]>The tables to generate entity types for. If this parameter is omitted, all tables are included. The example for generating code of specific tables is –Tables “Product, Customer”
-DataAnnotationsThis parameter specifies whether we want to use Data Annotation attributes to configure the models. If this parameter is omitted, the generated code will use fluent API to configure models.
-UseDatabaseNamesUse table and column names exactly as they appear in the database. If this parameter is omitted, database names are changed to more closely conform to C# name style conventions.
-ForceOverwrite existing files.
-NoOnConfiguringThe parameter is added in Entity Framework Core 5.0 and it specifies whether we want to generate DbContext.OnConfiguring method in our generated DbContext class or not.
-NoPluralizeThe parameter is added in Entity Framework Core 5.0 and it specifies if we want to pluralize the DbSet classes e.g. Product or Products

Let’s try to use some of the parameters from the above table. Open the Package Manager Console of the project and copy/paste the following command and press Enter.

Scaffold-DbContext -Connection "Server=DESKTOP-6SDKTRC; Database=OnlineShopDb; Trusted_Connection=True; MultipleActiveResultSets=true;" -Provider Microsoft.EntityFrameworkCore.SqlServer -OutputDir "Models" -ContextDir "Data" -Context "OnlineShopDbContext"
Scaffold-DbContext command in Package Manager Console in Visual Studio

I am providing connection string as per my database so you need to change the server and database name in the connection string as per your machine. I also specified SQL Server provider because I am using SQL Server database. I want to generate all my model classes in the Models folder and I want my DbContext to be generated in the Data folder with the name OnlineShopDbContext. Once the command will execute you will see the generated classes in Solution Explorer.

READ ALSO:  ASP.NET Core Unit Testing with FluentAssertions
Generated Models and DbContext classes using EF Core Database First

The generated model classes such as Product, Customer, etc. are simple C# classes with the properties mapped to database table columns. Here is the example of a Customer class generated from the Customer table in the database.

public partial class Customer
{
    public Customer()
    {
        Orders = new HashSet<Order>();
    }

    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
    public string Mobile { get; set; }

    public virtual ICollection<Order> Orders { get; set; }
}

The code generated in the OnlineShopDbContext class is more interesting. The class is inheriting from DbContext class as expected and it has DbSet<T> properties for each table in the database.

public partial class OnlineShopDbContext : DbContext
{
    public OnlineShopDbContext()
    {
    }

    public OnlineShopDbContext(DbContextOptions<OnlineShopDbContext> options)
        : base(options)
    {
    }

    public virtual DbSet<Customer> Customers { get; set; }
    public virtual DbSet<Order> Orders { get; set; }
    public virtual DbSet<Product> Products { get; set; }
    public virtual DbSet<ProductOrder> ProductOrders { get; set; }

    ...
}

There is also an overridden method OnConfiguring which is configuring the Entity Framework using the UseSqlServer method. You can see that a warning is also generated telling you that you should not save sensitive information such as connection string in the code.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    if (!optionsBuilder.IsConfigured)
    {
        #warning To protect potentially sensitive information in your connection string, 
        you should move it out of source code. You can avoid scaffolding the connection 
        string by using the Name= syntax to read it from configuration - see 
        https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on 
        storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
        
        optionsBuilder.UseSqlServer("Server=DESKTOP-6SDKTRC; Database=OnlineShopDb; Trusted_Connection=True; MultipleActiveResultSets=true;");
    }
}

Let’s first fix this issue as we want to follow the best practices. Run the following command with two additional parameters –NoOnConfiguring and –Force and you will see that this time OnConfiguring method will not be generated.

Scaffold-DbContext -Connection "Server=DESKTOP-6SDKTRC; Database=OnlineShopDb; Trusted_Connection=True; MultipleActiveResultSets=true;" -Provider Microsoft.EntityFrameworkCore.SqlServer -OutputDir "Models" -ContextDir "Data" -Context "OnlineShopDbContext" -NoOnConfiguring –Force

You can specify the connection string in appsettings.json file as follows

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=DESKTOP-6SDKTRC; Database=OnlineShopDb; Trusted_Connection=True; MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Entity Framework provider can be configured in ConfigureServices method of Startup.cs file as shown below:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

    services.AddDbContext<OnlineShopDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}

If you want to learn more about ASP.NET Core Configuration then read my post A Step by Step Guide for ASP.NET Core Configuration.

READ ALSO:  Full Stack CRUD using Angular 8 and ASP.NET Core 5 Web API

The next overridden method in the OnlineShopDbContext class is OnModelCreating which is using the Fluent API to configure and map database tables and columns with classes and properties. If you want Entity Framework to configure mappings using DataAnnotations then you can use the –DataAnnotations parameter of Scaffold-DbContext command.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasAnnotation("Relational:Collation", "SQL_Latin1_General_CP1_CI_AS");

    modelBuilder.Entity<Customer>(entity =>
    {
        entity.Property(e => e.FirstName).IsRequired();

        entity.Property(e => e.LastName).IsRequired();
    });

    ...

    OnModelCreatingPartial(modelBuilder);
}

Extending EF Core Generated DbContext Class

In a real project, you would probably add more database tables or columns during development and every time you will generate model classes and DbContext class using Scaffold-DbContext, you will lose all your custom changes and configurations you have done in the OnModelCreating method. It is not recommended to add custom code in overridden method OnModelCreating and that’s why the Scaffold-DbContext command generates a partial method for us.

partial void OnModelCreatingPartial(ModelBuilder modelBuilder);

You can also see that this method is called from inside OnModelCreating method as follows:

OnModelCreatingPartial(modelBuilder);

To extend generated DbContext class, you can create a partial class in your project with the same name and define your custom configurations inside the overridden OnModelCreatingPartial method. Add a file named _OnlineShopDbContext.cs in the Data folder and inside the file define the partial OnlineShopDbContext class by inheriting the same DbContext class. Inside OnModelCreatingPartial you can specify the custom configurations as shown in the code snippet below:

public partial class OnlineShopDbContext : DbContext
{
    partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Customer>(entity =>
        {
            entity.Property(e => e.Phone).IsRequired();
        });
    }
}

Displaying Data in ASP.NET Core using EF Core

In this final section of the tutorial, I will show you how you can use the generated OnlineShopDbContext in ASP.NET Core MVC to display data from the existing database. This is the same technique I have demonstrated in my previous tutorial Data Access in ASP.NET Core using EF Core (Code First)

First of all, we need to inject OnlineShopDbContext class in our HomeController constructor and then we can call the ToListAsync method on Products DbSet. This will automatically fetch data from the existing database. Once the data is available, we will pass the data to Razor View.  

public class HomeController : Controller
{
    private readonly OnlineShopDbContext _context;

    public HomeController(OnlineShopDbContext context)
    {
        _context = context;
    }

    public async Task<IActionResult> Index()
    {
        return View(await _context.Products.ToListAsync());
    }
}

The Index Razor View declares the model as IEnumerable<Product> and then it has a standard foreach loop that will iterate over the list of Products and will generate an HTML table on the page.

@model IEnumerable<Product>

@{
    ViewData["Title"] = "Home Page";
}
<h2>Products</h2>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Id)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Id)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Price)
                </td>
            </tr>
        }
    </tbody>
</table>

If you will run the project now, you will see the following page showing all the products from the database.

Products-Table-Data-Display-in-MVC-View-using-Entity-Framework-Core-Code-First

Summary

In this tutorial, I have shown you how to generate model classes and DbContext from an existing database using Entity Framework commands and tools. I have also covered how to generate code with Fluent API or Data Annotations and finally, I gave you an example of extending the generated DbContext. If you like this post, please share it with your friends or colleagues to spread the knowledge further.

This Post Has 8 Comments

  1. Russ Sharp

    Do you have a version of this tutorial that implements ASP.Net Core RazorPages,
    instead of ASP.Net Core MVC?

  2. M

    Nice article with proper steps given. But I have one question:
    My requirement is, my application is built on Asp.Net core 5 is displaying information from two different databases(both Sql). One database is application’s own database where all the information will be stored/added. but there is another database already designed and being used by another application. I want to read some master data from this database to use in my application. So I want to connect to the second database is just as a read access to read master data and display it in application pages. For the first database connectivity I have created a separate entity project using Entity Framework Core 5.0 Code first approach.
    How do I access second database(existing) just for read data purpose, Which would be the feasible approach for this. I was thinking to create another entity project with EF Core 5 DB first approach, but with this approach it creates DBContext class and all DbSets objects for each table. Which is not required I feel.
    Can you please suggest which would be a better approach for this? DB first approach or Ado.Net Vanilla method?

  3. Shan

    Hi Waqas Anwar sir,

    I have one doubt. if I alter the table or create another new table, again should I take Scaffold ? and how to use stored procedure in this project?

  4. Tahir Alvi

    Great post, but I think you must share the sample code on github for practice.

    1. Waqas Anwar

      Thanks for liking my post. I uploaded the source code and you can download it using the “Download Source Code” button shown at the start of this post.

  5. Craig

    Thank you very much for providing this tutorial. I used this as the basis of a new project for work and it was all going very smoothly until I used -Tables “[dbo].[Table1], [dbo].[Table2]”, and also -Tables “dbo.Table1, dbo.Table2”, and also -Tables “Table1, Table2” yet it failed bringing them both over. I can only think that it is because only one is a true table but the other is a view?

    I did test one at a time and it worked perfectly, but still wont do both!

    Thank you for sharing, it has led me onto the next stage of my project in either case.

Leave a Reply