Link Search Menu Expand Document

EF Core & PostgreSQL

Trong bài này, chúng ta xây dựng một ứng dụng .NET console theo hướng tiếp cận Code First. Ứng dụng sử dụng EF Core để tạo model với các thực thể Account, Payment, Address cùng mối quan hệ tương ứng. Dựa trên model đó, áp dụng dotnet ef tự động tạo ra database trên PostgreSQL server.

Các bước thực hiện:

  1. Cài đặt
  2. Xây dựng Model
  3. Thực hiện Migrations
  4. Thao tác dữ liệu

Cài đặt

dotnet tool install --global dotnet-ef

  • Sử dụng .NET Core CLI để tạo dự án:
dotnet new console -o Accounts.Domain
cd Accounts.Domain
  • Cài đặt EF Core packages
dotnet add package Microsoft.EntityFrameworkCore 
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Newtonsoft.Json

Xây dựng Model

Giải thích

Trong EF Core, Model bao gồm tập hợp domain classes biểu diễn các thực thể nghiệp vụ - entities và đối tượng ngữ cảnh - database context đại diện cho phiên làm việc giữa client và database server.

Trong folder Accounts.Domain, tạo file Model.cs chứa các classes:

  • AccountsContext: cung cấp kết nối đến cơ sở dữ liệu Postgresql
  • Account, Address, Payment: entities trong một domain model
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace Accounts.Domain
{
    public class AccountsContext : DbContext
    {
        public DbSet<Account> Accounts { get; set; }
        public DbSet<Account> Addresses { get; set; }
        public DbSet<Account> Payments { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            _ = options.UseNpgsql("Host=localhost;Database=Uber;Username=postgres;Password=password");
        }
    }

    public class Account
    {
        public Guid AccountId { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string MiddleName { get; set; }

        public string Email { get; set; }
        public string PhoneNumber { get; set; }
        public Address Address { get; set; }

        public List<Payment> Payments { get; set; }
    }

    public class Address
    {
        public Guid AddressId { get; set; }

        public Guid AccountId { get; set; }
        public Account Account { get; set; }

        public string StreetName { get; set; }
        public string City { get; set; }
        public int ZipCode { get; set; }
        public string State { get; set; }
        public string Country { get; set; }
    }

    public class Payment
    {
        public Guid PaymentId { get; set; }

        public Guid AccountId { get; set; }
        public Account Account { get; set; }

        public string PaymentType { get; set; }
        public bool IsDefault { get; set; }

        public string CardNumber { get; set; }
        public int ExpireMonth { get; set; }
        public int ExpiredYear { get; set; }
    }
}

Chúng ta có các thuật ngữ mô tả mối quan hệ giữa entities.

  • Thực thể - entity Account và Payment có mối quan hệ 1-N với các vai trò:
    • Account: Principal entity, đóng vai trò parent
    • Payment: Depenent entity, đóng vai trò child
  • Trong Account entity:
    • AccountId: Principal Key, khoá chính duy nhất của Principal
  • Trong Payment entity:
    • AccountId: Foreign Key, khoá ngoài liên kết đến khoá chính của Account entity
  • Các kiểu quan hệ được biễu diễn qua các thuộc tính - Navigation property
    • Collection navigation property (Account.Payments) - liên kết đến tập hợp entities
    • Reference navigation property (Payment.Account) - liên kết một entity khác
    • Inverse navigation property (Payment.Account) - liên kết ngược trong quan hệ

Theo mặc định, EF Core sử dụng Navigation property để tạo quan hệ ánh xạ giữa các bảng tương ứng trong database.

Thực hiện Migrations

EF Core sử dụng Migrations để duy trì sự đồng bộ giữa sự thay đổi của entities và database schema.

Để khởi tạo migration đầu tiên, sử dụng lệnh:

dotnet ef migrations add InitialCreateDb

Folder Migrations tạo ra bao gồm các files:

  • [Your-datetime]_InitialCreateDb.Designer.cs
  • [Your-datetime]_InitialCreateDb.cs
  • AccountsContextModelSnapshot.cs

Dựa trên InitialCreateDb, tạo Uber database:

dotnet ef database update

Output:

Build started...
Build succeeded.
Applying migration '[Your-datetime]_InitialCreateDb'.
Done.

Sử dụng Navicat để hiển thị các bảng trong Uber database: Navicat -> View -> ER Diagram

Relationship Ouput

Thao tác dữ liệu

Trong file Program.cs, bổ sung logic cho phép bổ sung và đọc các bản ghi từ Account table.

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;

namespace Accounts.Domain
{
    class Program
    {
        static void Main(string[] args)
        {
            using var db = new AccountsContext();

            Console.WriteLine("Applying migrations");
            db.Database.EnsureCreated();

            Console.WriteLine("Writing a new record");
            db.Add(new Account
            {
                FirstName = "Minh",
                MiddleName = "Duc",
                LastName = "Le",
                Email = "NotAValidEmail",
                PhoneNumber = "NotAValidPhoneNumber",
                Address = new Address
                {
                    Country = "Australia",
                    State = "Victoria",
                    ZipCode = 3055,
                    City = "Melbourne",
                    StreetName = "Mincha"
                },
                Payments = new List<Payment>
            {
                new Payment()
                {
                    PaymentType = "Visa",
                    IsDefault = true,
                    CardNumber = "4000 0082 6000 0000",
                    ExpireMonth = 10,
                    ExpiredYear = 2040
                }
            }
            });
            db.SaveChanges();

            Console.WriteLine("Reading a record");
            var account = db.Accounts.FirstOrDefault();
            if (account != null)
            {
                Console.WriteLine(JsonConvert.SerializeObject(account, new JsonSerializerSettings
                {
                    Formatting = Formatting.Indented,
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                }));
            }

            Console.WriteLine("Clean up database");
            db.Accounts.RemoveRange(db.Accounts);
            db.Addresses.RemoveRange(db.Addresses);
            db.Payments.RemoveRange(db.Payments);
            db.SaveChanges();
        }
    }
}

Kết quả hiển thị với lệnh dotnet run:

Applying migrations
Writing a new record
Reading a record
{
  "AccountId": "6e9e115a-c5b7-4005-a9e8-1b2352429053",
  "FirstName": "Minh",
  "LastName": "Le",
  "MiddleName": "Duc",
  "Email": "NotAValidEmail",
  "PhoneNumber": "NotAValidPhoneNumber",
  "Address": {
    "AddressId": "1d6dd0cd-e2f4-4a39-8379-f2c7d9e2e249",
    "AccountId": "6e9e115a-c5b7-4005-a9e8-1b2352429053",
    "StreetName": "Mincha",
    "City": "Melbourne",
    "ZipCode": 3055,
    "State": "Victoria",
    "Country": "Australia"
  },
  "Payments": [
    {
      "PaymentId": "ff6d60fc-bbb9-4df1-bff6-c35b52236227",
      "AccountId": "6e9e115a-c5b7-4005-a9e8-1b2352429053",
      "PaymentType": "Visa",
      "IsDefault": true,
      "CardNumber": "4000 0082 6000 0000",
      "ExpireMonth": 10,
      "ExpiredYear": 2040
    }
  ]
}
Clean up database

Copyright © 2019-2022 Tuan Anh Le.