Đăng bởi Để lại phản hồi

Cài Redmine trên Ubuntu + Nginx + MySQL

í dụ dưới đây dùng:

  • Ubuntu 24.04
  • MySQL 8
  • Ruby 3.2
  • Nginx
  • Passenger
  • Redmine 6

Tài liệu chính thức: Redmine Installation Guide


1. Cập nhật hệ thống

sudo apt update && sudo apt upgrade -y

2. Cài package cần thiết

sudo apt install -y \
build-essential \
zlib1g-dev \
libssl-dev \
libreadline-dev \
libyaml-dev \
libcurl4-openssl-dev \
libffi-dev \
libgdbm-dev \
libncurses5-dev \
libmariadb-dev \
imagemagick \
apache2-utils \
git \
curl \
gnupg2

3. Cài Ruby + Bundler

Ubuntu 24 đã có Ruby 3.2:

sudo apt install -y ruby-full

Kiểm tra:

ruby -v

Cài bundler:

sudo gem install bundler

Kiểm tra:

bundle -v

4. Cài MySQL

Cài:

sudo apt install -y mysql-server

Secure:

sudo mysql_secure_installation

5. Tạo database cho Redmine

Đăng nhập MySQL:

sudo mysql

Tạo DB:

CREATE DATABASE redmine CHARACTER SET utf8mb4;

Tạo user:

CREATE USER 'redmine'@'localhost' IDENTIFIED BY 'StrongPassword123!';

Cấp quyền:

GRANT ALL PRIVILEGES ON redmine.* TO 'redmine'@'localhost';
FLUSH PRIVILEGES;
EXIT;

6. Download Redmine

Trang chính thức:
Redmine Downloads

Ví dụ:

cd /var/www
sudo wget https://www.redmine.org/releases/redmine-6.0.3.tar.gz
sudo tar -xzf redmine-6.0.3.tar.gz
sudo mv redmine-6.0.3 redmine

7. Tạo user chạy Redmine

sudo adduser --system --group --home /var/www/redmine redmine

Set quyền:

sudo chown -R redmine:redmine /var/www/redmine

8. Cấu hình database.yml

Copy file mẫu:

cd /var/www/redmine/config
sudo cp database.yml.example database.yml

Edit:

sudo nano database.yml

Sửa phần production:

production:
adapter: mysql2
database: redmine
host: localhost
username: redmine
password: "StrongPassword123!"
encoding: utf8mb4

9. Install gem

Đăng nhập user redmine:

sudo su - redmine

Vào thư mục:

cd /var/www/redmine

Config bundle local:

bundle config set --local path 'vendor/bundle'
bundle config set --local without 'development test'

Install:

bundle install

10. Generate secret + migrate DB

bundle exec rake generate_secret_token
RAILS_ENV=production bundle exec rake db:migrate

Load default data:

RAILS_ENV=production bundle exec rake redmine:load_default_data

Chọn language:

en

11. Cài Nginx + Passenger

Cài:

sudo apt install -y nginx libnginx-mod-http-passenger

Neu loi cai theo cach nay

1. Xoá config Passenger cũ

sudo rm -f /etc/apt/sources.list.d/passenger.list
sudo rm -f /usr/share/keyrings/passenger.gpg

2. Cài package cần thiết

sudo apt update

sudo apt install -y \
curl \
gnupg2 \
ca-certificates \
apt-transport-https

3. Add GPG key mới

curl -fsSL https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key-2025.txt \
| sudo gpg --dearmor -o /usr/share/keyrings/passenger.gpg

4. Add repo Ubuntu 24 (noble)

echo "deb [signed-by=/usr/share/keyrings/passenger.gpg] https://oss-binaries.phusionpassenger.com/apt/passenger noble main" \
| sudo tee /etc/apt/sources.list.d/passenger.list

5. Update package list

sudo apt update

6. Kiểm tra package Passenger

apt search passenger

Bạn phải thấy:

libnginx-mod-http-passenger

7. Cài Passenger

sudo apt install -y libnginx-mod-http-passenger

8. Restart nginx

sudo systemctl restart nginx

Kiểm tra passenger:

sudo nginx -V 2>&1 | grep passenger

12. Cấu hình Nginx

Tạo config:

sudo nano /etc/nginx/sites-available/redmine

Nội dung:

server {
listen 80;
server_name YOUR_DOMAIN_OR_IP;

root /var/www/redmine/public;

passenger_enabled on;
passenger_ruby /usr/bin/ruby;

client_max_body_size 20m;

location / {
try_files $uri/index.html $uri @app;
}

location @app {
passenger_enabled on;
}
}

Enable site:

sudo ln -s /etc/nginx/sites-available/redmine /etc/nginx/sites-enabled/

Disable default:

sudo rm /etc/nginx/sites-enabled/default

Test:

sudo nginx -t

Restart:

sudo systemctl restart nginx

13. Mở firewall

sudo ufw allow 80/tcp

Nếu HTTPS:

sudo ufw allow 443/tcp

14. Truy cập

Mở:

http://YOUR_SERVER_IP

Login mặc định:

admin
admin

15. Cài HTTPS bằng Let’s Encrypt

Cài certbot:

sudo apt install -y certbot python3-certbot-nginx

Chạy:

sudo certbot --nginx

16. Service cần kiểm tra

systemctl status nginx
systemctl status mysql

17. Log khi lỗi

Redmine:

tail -f /var/www/redmine/log/production.log

Nginx:

sudo tail -f /var/log/nginx/error.log

18. Backup MySQL

mysqldump -u redmine -p redmine > redmine.sql
Đăng bởi 1 phản hồi

Pipeline with dotnet public git

pipeline {
agent any

// If using the .NET SDK plugin, wrap steps in this block
/* 
tools {
    dotnetsdk 'dotnet-8' 
}
*/

stages {
    stage('Checkout') {
        steps {
            git  branch: 'main',
                url:'https://github.com/congduan2811/DemoJenkins.git'
        }
    }

   stage('Restore') {
        steps {
            sh 'dotnet restore' // Use 'bat' for Windows
        }
    }
    stage('Build') {
        steps {
            sh 'dotnet build --configuration Release'
        }
    }
    stage('Test') {
        steps {
            sh 'dotnet test'
        }
    }
    stage('Publish') {
        steps {
            sh 'dotnet publish -c Release -o ./publish'
        }
    }
}

}

Đăng bởi Để lại phản hồi

Cài đặt jenkins trên Ubuntu

Installation of Java

sudo apt update
sudo apt install fontconfig openjdk-21-jre
java -version

Installation of Jenkins

sudo wget -O /etc/apt/keyrings/jenkins-keyring.asc \
https://pkg.jenkins.io/debian/jenkins.io-2026.key
echo "deb [signed-by=/etc/apt/keyrings/jenkins-keyring.asc]" \
https://pkg.jenkins.io/debian binary/ | sudo tee \
/etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt update
sudo apt install jenkins

Unlocking Jenkins

Browse to http://localhost:8080 (or whichever port you configured for Jenkins when installing it) and wait until the Unlock Jenkins page appears.

Đăng bởi Để lại phản hồi

Thể hiện ràng buộc khóa ngoại (Foreign Key) trong Entity Framework (EF / EF Core)

✅ Cách 1 (Khuyến nghị): Convention (Theo quy ước EF)

EF tự nhận diện Foreign Key nếu bạn đặt tên đúng quy ước.

Ví dụ: Quan hệ 1–n (User – Orders)

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }

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

public class Order
{
    public int Id { get; set; }

    // Foreign Key
    public int UserId { get; set; }

    // Navigation property
    public User User { get; set; }
}

👉 EF sẽ tự hiểu:

  • UserId là khóa ngoại
  • Order.User liên kết tới User.Id

📌 Không cần cấu hình thêm


✅ Cách 2: Data Annotation [ForeignKey]

Dùng khi tên FK không theo quy ước hoặc muốn rõ ràng hơn.

using System.ComponentModel.DataAnnotations.Schema;

public class Order
{
    public int Id { get; set; }

    public int CreatedBy { get; set; }

    [ForeignKey("CreatedBy")]
    public User User { get; set; }
}

Hoặc ngược lại:

public class Order
{
    public int Id { get; set; }

    [ForeignKey(nameof(User))]
    public int UserId { get; set; }

    public User User { get; set; }
}

✅ Cách 3: Fluent API (Chuẩn & mạnh nhất)

Dùng khi:

  • Quan hệ phức tạp
  • Composite Key
  • Cấu hình cascade delete, required, optional…

Ví dụ 1–n:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Order>()
        .HasOne(o => o.User)
        .WithMany(u => u.Orders)
        .HasForeignKey(o => o.UserId)
        .OnDelete(DeleteBehavior.Cascade);
}

🔹 Quan hệ 1–1

modelBuilder.Entity<User>()
    .HasOne(u => u.Profile)
    .WithOne(p => p.User)
    .HasForeignKey<UserProfile>(p => p.UserId);

🔹 Quan hệ n–n (EF Core 5+)

modelBuilder.Entity<Student>()
    .HasMany(s => s.Courses)
    .WithMany(c => c.Students);

Hoặc có bảng trung gian:

modelBuilder.Entity<StudentCourse>()
    .HasKey(sc => new { sc.StudentId, sc.CourseId });
Đăng bởi Để lại phản hồi

Checklist bắt buộc cho Razor Pages upload file lớn

Nếu thiếu 1 bướcupload vẫn fail

1️⃣ Program.cs (Razor Pages)

using Microsoft.AspNetCore.Http.Features;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.Configure<FormOptions>(options =>
{
    options.MultipartBodyLengthLimit = 1024L * 1024L * 1024L; // 1GB
});

2️⃣ Kestrel – cực kỳ quan trọng

builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestBodySize = 1024L * 1024L * 1024L; // 1GB
});

⚠️ Nếu bạn chạy IIS Express → bước này KHÔNG đủ


3️⃣ Razor Page Model – PHẢI CÓ attribute

❌ Sai (rất nhiều người mắc)

public class UploadModel : PageModel
{
    public async Task<IActionResult> OnPostAsync(IFormFile file)
    {
        ...
    }
}

✅ ĐÚNG

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

[RequestSizeLimit(1024L * 1024L * 1024L)]
[RequestFormLimits(MultipartBodyLengthLimit = 1024L * 1024L * 1024L)]
public class UploadModel : PageModel
{
    [BindProperty]
    public IFormFile File { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (File == null)
            return Page();

        var path = Path.Combine("wwwroot/uploads", File.FileName);
        using var stream = new FileStream(path, FileMode.Create);
        await File.CopyToAsync(stream);

        return Page();
    }
}

📌 Razor Pages bắt buộc attribute phải đặt trên PageModel class,
đặt trên method đôi khi không có tác dụng


4️⃣ Razor View (.cshtml) – rất hay bị quên

<form method="post" enctype="multipart/form-data">
    <input type="file" asp-for="File" />
    <button type="submit">Upload</button>
</form>

❌ Thiếu enctypeupload luôn fail


5️⃣ Nếu chạy IIS / IIS Express (90% lỗi nằm ở đây)

web.config

<configuration>
  <system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="1073741824" />
      </requestFiltering>
    </security>
  </system.webServer>
</configuration>

📌 IIS mặc định chỉ 30MB

Đăng bởi Để lại phản hồi

Cách chạy lệnh cmd trên c#

Bạn có thể gọi lệnh CMD trong C# bằng cách dùng lớp System.Diagnostics.Process. Dưới đây là các cách phổ biến (từ đơn giản → nâng cao).


1. Chạy 1 lệnh CMD đơn giản (không cần lấy kết quả)

Ví dụ: chạy lệnh ipconfig

using System.Diagnostics;

Process.Start("cmd.exe", "/c ipconfig");

👉 /c nghĩa là: chạy lệnh xong thì đóng CMD.


2. Chạy lệnh CMD và lấy kết quả trả về

Ví dụ: lấy kết quả của dir

using System;
using System.Diagnostics;

class Program
{
    static void Main()
    {
        Process p = new Process();
        p.StartInfo.FileName = "cmd.exe";
        p.StartInfo.Arguments = "/c dir";
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;

        p.Start();

        string output = p.StandardOutput.ReadToEnd();
        p.WaitForExit();

        Console.WriteLine(output);
    }
}

3. Chạy nhiều lệnh CMD cùng lúc

Dùng &&:

Process.Start("cmd.exe", "/c cd C:\\ && dir");

4. Mở CMD và giữ cửa sổ không tắt

Process.Start("cmd.exe");

Hoặc:

Process.Start("cmd.exe", "/k dir");

👉 /k = chạy lệnh nhưng không đóng CMD.


5. Chạy file .bat

Process.Start("cmd.exe", "/c test.bat");

Hoặc trực tiếp:

Process.Start("test.bat");

Lưu ý quan trọng ⚠️

  • Nếu chương trình cần quyền admin, bạn phải chạy app C# bằng quyền Administrator.
  • Không nên chạy lệnh CMD từ input người dùng nếu không kiểm soát (dễ lỗi & mất an toàn).

Nếu bạn muốn:

  • chạy CMD ngầm
  • chạy PowerShell
  • hoặc chạy trong WinForms / WPF

👉 nói mình biết bạn đang dùng loại project nào nhé 👍

Toi muon an cmd duoc khong

Có nhé 👍 Bạn hoàn toàn có thể ẩn cửa sổ CMD khi gọi lệnh từ C#.

Dưới đây là cách chuẩn và hay dùng nhất.


✅ Ẩn CMD khi chạy lệnh (khuyến nghị)

using System.Diagnostics;

Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c ipconfig"; // lệnh CMD
p.StartInfo.CreateNoWindow = true;      // ẨN CMD
p.StartInfo.UseShellExecute = false;    // BẮT BUỘC để ẩn

p.Start();
p.WaitForExit();

👉 CMD sẽ chạy ngầm, người dùng không thấy gì cả.


✅ Ẩn CMD + lấy kết quả

using System.Diagnostics;

Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c dir";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;

p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();

❌ Cách KHÔNG ẩn được CMD

Process.Start("cmd.exe", "/c dir");

⚠️ Vì mặc định UseShellExecute = true → CMD sẽ hiện ra.

CÁCH 1 (NHANH – PHỔ BIẾN): Chạy ngầm bằng Process (giống service)

👉 Phù hợp nếu:

  • App đã là Worker / Web API
  • Không cần tích hợp sâu với Windows Service
  • Muốn start / stop bằng C#

🔹 Code C# chạy ngầm

using System.Diagnostics;

Process serviceProcess;

void StartService()
{
    serviceProcess = new Process();
    serviceProcess.StartInfo.FileName = "dotnet";
    serviceProcess.StartInfo.Arguments = "Storage.GiamKichSan.dll";
    serviceProcess.StartInfo.WorkingDirectory = @"C:\App\Storage";
    serviceProcess.StartInfo.UseShellExecute = false;
    serviceProcess.StartInfo.CreateNoWindow = true;
    serviceProcess.StartInfo.RedirectStandardOutput = true;
    serviceProcess.StartInfo.RedirectStandardError = true;

    serviceProcess.Start();
}

void StopService()
{
    if (serviceProcess != null && !serviceProcess.HasExited)
    {
        serviceProcess.Kill(); // dừng service
    }
}

📌 Ứng dụng sẽ:

  • chạy ngầm
  • không hiện CMD
  • chạy liên tục cho tới khi bị kill

⚠️ Nhược điểm:

  • Không tự chạy khi Windows boot
  • Không thấy trong services.msc

✅ CÁCH 2 (CHUẨN WINDOWS): Chạy như Windows Service thật

👉 Khuyến nghị nếu chạy lâu dài / production

🔹 Bước 1: App phải là Worker Service

Nếu Storage.GiamKichSan.dllWorker Service (.NET) → OK luôn
Nếu chưa, cần tạo dạng Worker:

dotnet new worker -n Storage.GiamKichSan

🔹 Bước 2: Cho phép chạy như Windows Service

Trong Program.cs:

using Microsoft.Extensions.Hosting;

Host.CreateDefaultBuilder(args)
    .UseWindowsService()
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
    })
    .Build()
    .Run();

🔹 Bước 3: Publish

dotnet publish -c Release -o C:\Services\Storage

🔹 Bước 4: Tạo Windows Service

Mở CMD Run as Administrator:

sc create StorageGiamKichSan binPath= "dotnet C:\Services\Storage\Storage.GiamKichSan.dll"

Start service:

sc start StorageGiamKichSan

Stop service:


		
Đăng bởi Để lại phản hồi

IEmailSender trong netcore

Nó hoạt động theo cơ chế Dependency Injection (DI)bạn phải tự cài đặt cách gửi email.

👉 Đây là interface, Identity chỉ biết gọi, không biết gửi bằng cách nào.

public interface IEmailSender
{
    Task SendEmailAsync(string email, string subject, string htmlMessage);
}

2. Luồng hoạt động tổng quát

Controller / Razor Page
        ↓
_emailSender.SendEmailAsync(...)
        ↓
EmailSender (class bạn viết)
        ↓
SMTP / SendGrid / MailKit
        ↓
Email được gửi

👉 Identity không can thiệp vào bước này.


3. Cách _emailSender được inject (DI)

Đăng ký trong Program.cs

builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();

👉 Khi ASP.NET Core tạo Controller:

  • Nó thấy cần IEmailSender
  • Nó tạo SmtpEmailSender
  • Gán vào _emailSender

4. Ví dụ implement _emailSender bằng SMTP

Ví dụ này chỉ minh họa, không có nội dung nhạy cảm.

using System.Net;
using System.Net.Mail;

public class SmtpEmailSender : IEmailSender
{
    private readonly IConfiguration _config;

    public SmtpEmailSender(IConfiguration config)
    {
        _config = config;
    }

    public async Task SendEmailAsync(string email, string subject, string htmlMessage)
    {
        var smtpClient = new SmtpClient(
            _config["Email:SmtpServer"],
            int.Parse(_config["Email:Port"])
        )
        {
            Credentials = new NetworkCredential(
                _config["Email:Username"],
                _config["Email:Password"]
            ),
            EnableSsl = true
        };

        var mail = new MailMessage
        {
            From = new MailAddress(_config["Email:From"]),
            Subject = subject,
            Body = htmlMessage,
            IsBodyHtml = true
        };

        mail.To.Add(email);

        await smtpClient.SendMailAsync(mail);
    }
}

5. Cách gọi _emailSender trong Controller

await _emailSender.SendEmailAsync(
    user.Email,
    "Xác nhận email",
    "Vui lòng nhấn link để xác nhận tài khoản."
);

👉 Controller chỉ gọi, không quan tâm SMTP.


6. _emailSender KHÔNG tự chạy

⚠️ Quan trọng:

  • _emailSender không tự động gửi email
  • Identity không gọi nó
  • Bạn phải gọi thủ công

Ví dụ:

  • Sau khi đăng ký
  • Khi reset password
  • Khi resend email

4. Identity kết hợp _emailSender thế nào

Bước 1: Tạo User

await _userManager.CreateAsync(user, password);

Bước 2: Tạo token xác nhận email

var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);

👉 Token được tạo bởi Data Protection API, không phải EmailStore.


Bước 3: Tạo link xác nhận

var link = Url.Action(
    "ConfirmEmail",
    "Account",
    new { userId = user.Id, token = token },
    Request.Scheme);

Bước 4: Gửi email

await _emailSender.SendEmailAsync(
    user.Email,
    "Xác nhận email",
    $"Click vào link: <a href='{link}'>Confirm</a>");

Bước 5: User click link → xác nhận

await _userManager.ConfirmEmailAsync(user, token);

👉 Lúc này:

  • UserManager gọi
  • IUserEmailStore.SetEmailConfirmedAsync(...)
  • Lưu EmailConfirmed = true vào DB

Identity chỉ hỗ trợ:

  • Tạo token
  • Xác nhận token
  • Lưu trạng thái email

8. Vì sao Microsoft thiết kế như vậy?

✔ Không ép dùng SMTP
✔ Dùng được cho API / Mobile
✔ Thay SendGrid → SMTP không đổi code
✔ Dễ test (mock IEmailSender)

Đăng bởi Để lại phản hồi

IUserEmailStore trong netcore

chỉ lưu trữ & quản lý dữ liệu email của User, không gửi email đi đâu cả

✅ Nó LÀM:

  • Lưu email vào database
  • Lấy email của user
  • Lưu trạng thái EmailConfirmed
  • Tìm user theo email

❌ Nó KHÔNG LÀM:

  • Gửi email
  • Tạo nội dung email
  • Gửi link xác nhận
  • Gửi OTP / reset password

1. IUserEmailStore<TUser> là gì?

IUserEmailStore<TUser>interface dùng để quản lý Email của User trong ASP.NET Core Identity.

Nó cho phép hệ thống:

  • Lưu email người dùng
  • Lấy email
  • Kiểm tra email đã xác nhận chưa
  • Set trạng thái xác nhận email

👉 Identity dùng interface này để không phụ thuộc vào database cụ thể (SQL, MongoDB, In-memory, v.v.).


2. IUserEmailStore dùng để làm gì?

Một số chức năng chính:

  • Đăng nhập bằng email
  • Xác nhận email (Email Confirmation)
  • Reset mật khẩu qua email
  • Kiểm tra email có tồn tại chưa

3. Các method quan trọng

public interface IUserEmailStore<TUser> : IUserStore<TUser>
{
    Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken);
    Task<string> GetEmailAsync(TUser user, CancellationToken cancellationToken);

    Task<bool> GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken);
    Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken);

    Task<TUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken);
    Task<string> GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken);
    Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken);
}

4. UserEmailStore nằm ở đâu trong project thực tế?

Nếu bạn dùng Identity mặc định (Entity Framework):

services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>();

👉 Lúc này:

  • UserManager tự động dùng UserStore
  • UserStore đã implement sẵn IUserEmailStore

Bạn không cần tự viết UserEmailStore.

5. Khi nào cần tự implement IUserEmailStore?

Bạn cần tự implement nếu:

  • Không dùng Entity Framework
  • Dùng database riêng (MongoDB, API, file, v.v.)
  • Muốn custom cách lưu User

Ví dụ:

public class CustomUserStore :
    IUserStore<ApplicationUser>,
    IUserEmailStore<ApplicationUser>
{
    public Task SetEmailAsync(ApplicationUser user, string email, CancellationToken ct)
    {
        user.Email = email;
        return Task.CompletedTask;
    }

    public Task<string> GetEmailAsync(ApplicationUser user, CancellationToken ct)
    {
        return Task.FromResult(user.Email);
    }

    public Task<bool> GetEmailConfirmedAsync(ApplicationUser user, CancellationToken ct)
    {
        return Task.FromResult(user.EmailConfirmed);
    }

    public Task SetEmailConfirmedAsync(ApplicationUser user, bool confirmed, CancellationToken ct)
    {
        user.EmailConfirmed = confirmed;
        return Task.CompletedTask;
    }

    // Các method khác bắt buộc phải implement...
}

6. Quan hệ với UserManager

UserManager<TUser> sẽ gọi IUserEmailStore ở bên dưới:

await _userManager.SetEmailAsync(user, "test@gmail.com");
await _userManager.IsEmailConfirmedAsync(user);

👉 Bạn không gọi trực tiếp UserEmailStore, mà gọi qua UserManager.

Đăng bởi Để lại phản hồi

Giới thiệu SQLite

SQLite là một hệ quản trị cơ sở dữ liệu quan hệ, khác với các hệ quản trị CSDL khác nó không cần client–server database engine (một dịch vụ, ứng dụng cài đặt đầy đủ thông qua nó kết nối, tương tác với các CSDL), nó được nhúng vào rất nhiều chương trình khác nhau, từ destop, mobile đến website. Hầu hết ngôn ngữ lập trình có các thư viện hỗ trợ kết nối, truy vấn đến SQLite như: PHP CJavaC#JavaScript… Nó được nhúng vào các trình duyệt, vào các hệ thống nhúng, hệ điều hành như AndroidIOS … đều đã nhúng mặc định.

Vì SQLite không cần client–server database engine nên có thể hiểu file CSDL đã tích hợp mọi thứ, sử dụng ngay không cần phải cài đặt thêm thứ gì.

SQLite tương thích với chuẩn SQL

Việc kết nối và thi các lệnh SQL với SQLite thật sự rất đơn giản, ví dụ:

SQLite với PHP

Ví dụ bạn cần thi hành một lệnh SQL trên CSDL SQLite với file có tên db.sqlite. Giả sử câu lệnh đó là:

SELECT * FROM Danhmuc

Chạy thử

SQLite với .NET

Đối với .NET thư viên truy cập SQLite được cung cấp tại http://sqlite.org/download.html, vào đó tìm đúng phiên bản binary cho .NET cần dùng, tải về và cài đặt. Sau đó thêm System.Data.SQLite ở thư mục cài đặt C:\Program Files\SQLite.NET\bin vào dự án của bạn để sử dụng.

Cách thứ 2 cài System.Data.SQLite vào dự án bằng Package Manager Console trong Visual Studio

Mở cửa sổ lệnh Package Manager Console, rồi gõ lệnh sau để cài:

PM> Install-Package Microsoft.EntityFrameworkCore.Sqlite
Install-Package Microsoft.EntityFrameworkCore.Tools
Install-Package Microsoft.EntityFrameworkCore.Design

Add-Migration InitialCreate
Update-Database

Nếu cài đặt thành công, khai báo sử dụng thư viện trong file code:

using System.Data.SQLite;

Dưới đây là đoạn mã, kết nối và truy vấn đến SQLite bằng C#

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;

public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    public string DbPath { get; }

    public BloggingContext()
    {
        var folder = Environment.SpecialFolder.LocalApplicationData;
        var path = Environment.GetFolderPath(folder);
        DbPath = System.IO.Path.Join(path, "blogging.db");
    }

    // The following configures EF to create a Sqlite database file in the
    // special "local" folder for your platform.
    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlite($"Data Source={DbPath}");
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; } = new();
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}



using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;

using var db = new BloggingContext();

// Note: This sample requires the database to be created before running.
Console.WriteLine($"Database path: {db.DbPath}.");

// Create
Console.WriteLine("Inserting a new blog");
db.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
await db.SaveChangesAsync();

// Read
Console.WriteLine("Querying for a blog");
var blog = await db.Blogs
    .OrderBy(b => b.BlogId)
    .FirstAsync();

// Update
Console.WriteLine("Updating the blog and adding a post");
blog.Url = "https://devblogs.microsoft.com/dotnet";
blog.Posts.Add(
    new Post { Title = "Hello World", Content = "I wrote an app using EF Core!" });
await db.SaveChangesAsync();

// Delete
Console.WriteLine("Delete the blog");
db.Remove(blog);
await db.SaveChangesAsync();

SQLiteStudio - Giao diện trực quan làm việc với SQLite
Để thực hành, học về SQL nói chung bạn có thể sử dụng CSDL mẫu bằng SQLite để thực hành, với mục đích tương tác với CSDL mà chưa cần áp dụng cho loại ngôn ngữ lập trình nào. Bạn có thể cài đặt công cụ SQLiteStudio, bạn có thể tải về cho macOS, Windows hay Linux.

Sau đó tải luôn dữ liệu mẫu tôi chuẩn bị sẵn, lưu file dữ liệu mẫu tên db.sqlite CSDL SQLite mẫu vào folder nào bạn thích.
Chạy file: SQLiteStudio, giao diện chương trình có dạng

SQLite Studio
Giao diện SQLiteStudio
<img src="https://raw.githubusercontent.com/xuanthulabnet/sql-learning/master/docs/sql-001.png" alt="SQLite Studio">
Bạn bắt đầu có thể tạo mới CSDL SQLite, thi hành các lệnh SQL trên giao diện này, ở đây bạn đã có file dữ liệu mẫu db.sqlite nên chỉ việc thêm nó vào và thực hành

Nhấn CTRL-0 thêm file db.sqlite mẫu vừa tải về để làm việc
Nhấn ALT+E mở cửa sổ soạn thảo lệnh SQL ví dụ nhập lệnh select * from Danhmuc
Nhấn F9 để chạy lệnh SQL nhập vào, kết quả trả về được thông báo hiện thị trực quan trong cửa sổ chương trình.
Đây là cách mà các bài hướng dẫn về SQL gợi ý dùng để thực hành trên website này. Ngoài ra bạn có thể dùng thẳng các chương trình khác như: MySQL console

Bạn có thể cài đặt XAMPP hoặc wampserver để có luôn cơ sở dữ liệu MariaDB, MySQL và công cụ PHPAdmin để tương tác trực quan với CSD