欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

ASP.NET?Core調(diào)用?Web?API?備份數(shù)據(jù)庫(kù)的詳細(xì)過(guò)程

 更新時(shí)間:2025年06月03日 08:53:22   作者:東邪獨(dú)孤  
這篇文章主要介紹了ASP.NET?Core調(diào)用Web?API備份數(shù)據(jù)庫(kù)的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

老周不經(jīng)意間翻了一下博客列表,上一篇水文竟然在 一個(gè)月前。啊,大海啊,全是水;時(shí)間啊,你跑得真快!過(guò)了一個(gè)月老周竟沒(méi)感覺(jué)出來(lái),可是這一個(gè)月里,好像啥事也沒(méi)干成,就改了一下幾個(gè)舊項(xiàng)目。也許是現(xiàn)在大環(huán)境真的不好,新項(xiàng)目不好找。新的活有是有,比較簡(jiǎn)單,卻很奇怪,比那貨難的項(xiàng)目都做過(guò),偏偏這貨沒(méi)頭緒。這東西需求就是畫(huà)圖——是用程序來(lái)畫(huà)圖,類似甘特圖。莫名其妙的是,這活會(huì)卡在窗口排版上。按照需求,這貨窗口特多。點(diǎn)一下這里,彈一個(gè)對(duì)話框出來(lái)可以修改;點(diǎn)一下那里,又要彈一個(gè)出來(lái)允許修改;右鍵單擊一下,彈出上百個(gè)選項(xiàng)的菜單(Check 模式),簡(jiǎn)直離譜,那么多指標(biāo)項(xiàng),我都懷疑他們老板根本不會(huì)去看的,但他們非要做全面。更鬧心的是主窗口,想想現(xiàn)在的顯示屏分辨率又高又大,一個(gè)窗口全屏放在那里,可上面又沒(méi)幾個(gè)控件,70% 的地方就是畫(huà)進(jìn)度圖,每幾秒刷新一下。雖然簡(jiǎn)單易用最好,但這窗口是看著空洞了一些。免得那些不懂編程的人說(shuō)老周這項(xiàng)目根本沒(méi)干啥活,老周還打算給它弄個(gè)高清《美少女戰(zhàn)士》或《劉姥姥進(jìn)大觀園》當(dāng)背景圖,這樣看起來(lái)就不空洞了。

今天咱們聊一個(gè)很單的主題,寫(xiě)一個(gè) Web API,客戶端可以調(diào)用它來(lái)備份 SQL Server 數(shù)據(jù)庫(kù)。不知道大伙伴們有沒(méi)有做過(guò)這活。相信做過(guò)的人會(huì)比老周更明白,畢竟,老熟人都知道,老周有兩大弱項(xiàng):SQL、匯編。匯編呢,是學(xué)生時(shí)代沒(méi)好好學(xué),想當(dāng)年很輕松地就拿下了二級(jí)C++,偏就沒(méi)學(xué)會(huì)匯編;而 SQL 呢,本來(lái)就學(xué)得一般,再加上用得少,忘得差不多了,所以別人給老周安排的項(xiàng)目基本不包括寫(xiě) SQL 的,最終導(dǎo)致 SQL 方面越來(lái)越弱。

EF Core 不僅能用 LINQ 和實(shí)體類型配合操作,確實(shí)能讓你在80%的情況下不用寫(xiě)SQL語(yǔ)句,但,為了靈活,EF Core 和早期 EF 和 ADO.NET 一樣,是可以直接執(zhí)行 SQL 語(yǔ)句的。這意味著,備份和還原數(shù)據(jù)庫(kù)不在話下。

這個(gè)功能的實(shí)現(xiàn)并不難,但有兩小坑,老周接下來(lái)會(huì)慢慢講。

備份數(shù)據(jù)庫(kù)用的是 BACKUP DATABASE 語(yǔ)句,比如這樣

BACKUP DATABASE <你的數(shù)據(jù)庫(kù)名>
    TO DISK = 'D:\backups\dbs.bak'

DISK 后的表達(dá)式不一定指定磁盤(pán),更多時(shí)候是備份的文件名。如果還要備份日志,可以接著執(zhí)行 BACKUP LOG 語(yǔ)句。

BACKUP LOG <數(shù)據(jù)庫(kù)名> TO DISK = 'E:\WrtMsn\oodo-log.bak'

如果要把備份放到另一臺(tái)服務(wù)器上,可以用共享路徑。

BACKUP DATABASE <數(shù)據(jù)庫(kù)名> TO DISK = '\\GaoXServer\shares\team\abc.bak'

這里就有第一個(gè)坑——權(quán)限,不管是你的程序進(jìn)程還是 MSSQL 進(jìn)程,有些目錄是沒(méi)有寫(xiě)入的權(quán)限的。比較懶的做法是在放置備份的目錄上給個(gè) everyone 的完全控制權(quán)限。老周不推薦這樣,太不厚道了。若是不太好確定用哪個(gè)用戶(畢竟有時(shí)候不一定是某個(gè)用戶,而是系統(tǒng)服務(wù)),Windows 里面有一個(gè)好用的名稱,叫Authenticated Users,看字面意思就已經(jīng)過(guò)驗(yàn)證的用戶,就不是單指某個(gè)用戶了。這個(gè)比 everyone 靠譜多了,起碼匿名的不允許。

簡(jiǎn)單說(shuō)說(shuō)操作,在目錄上右擊,打開(kāi)“屬性”窗口,切換到“安全”頁(yè)。點(diǎn)擊下面的“高級(jí)”按鈕。

點(diǎn)擊“添加”按鈕。

點(diǎn)擊“選擇主體”,找到 Authenticated Users 并添加。最起碼給予寫(xiě)入和修改權(quán)限。

然后一路確定、應(yīng)用即可。

============================================================================================

好了,理論知識(shí)完畢,下面可以動(dòng)手了。

先建個(gè)庫(kù)用來(lái)測(cè)試。這里老周建了個(gè)實(shí)體,名為 Movie,表示一部大片。

public class Movie
{
    /// <summary>
    /// 編號(hào)
    /// </summary>
    public int Id { get; set; }
    /// <summary>
    /// 電影標(biāo)題
    /// </summary>
    public string? Subject {  get; set; }
    /// <summary>
    /// 導(dǎo)演是誰(shuí)
    /// </summary>
    public string? Director {  get; set; }
    /// <summary>
    /// 哪年上映的
    /// </summary>
    public int? Year { get; set; }
    /// <summary>
    /// 講了啥故事
    /// </summary>
    public string? Desc {  get; set; }
}

接下來(lái)寫(xiě) DbContext。

public class TestDBContext:DbContext
{
    public TestDBContext(DbContextOptions<TestDBContext> options)
        : base(options)
    { }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 主鍵
        modelBuilder.Entity<Movie>().HasKey(x => x.Id);
        // 影片名為必填
        modelBuilder.Entity<Movie>().Property(x => x.Subject).IsRequired();
        // 填充初始數(shù)據(jù)
        modelBuilder.Entity<Movie>().ToTable("t_movies").HasData(
                new Movie { Id = 1, Subject = "狗二總裁", Year = 2026, Director = "大頭蘇", Desc = "二五仔二次創(chuàng)業(yè)的勵(lì)志故事" },
new Movie { Id = 2, Subject = "子夜實(shí)驗(yàn)室", Year = 2025, Director = "丁小丁", Desc = "某大學(xué)的實(shí)驗(yàn)室總是在子夜時(shí)分莫名發(fā)生火災(zāi),校方懷疑有人惡意縱火,于是,學(xué)校成立專項(xiàng)小組進(jìn)行調(diào)查……" }
          );
        // 下面這行可以省略,Id 屬性默認(rèn)是自增長(zhǎng)標(biāo)識(shí)
        //modelBuilder.Entity<Movie>().Property(c => c.Id).ValueGeneratedOnAdd();
    }
    public DbSet<Movie> Movies { get; set; }
}

重寫(xiě)OnModelCreating 方法是對(duì)模型做一些自定義設(shè)置,如果你只需保留默認(rèn),可以不重寫(xiě)此方法。由于表示主鍵的屬性名為 Id,EF Core 會(huì)自動(dòng)認(rèn)為是主鍵,且啟用自增長(zhǎng)標(biāo)識(shí)。上面代碼老周用 ToTable 方法映射到的數(shù)據(jù)表名 t_movies。如果你要求表名和實(shí)體類名一樣,那可以忽略。

另外,代碼還調(diào)用了 HasData 方法,其用途是插入一些初始數(shù)據(jù)。如果你的數(shù)據(jù)表的初始狀態(tài)允許空白,可以忽略。如果要插入種子數(shù)據(jù)(即初始數(shù)據(jù)),這里有第二個(gè)坑,一定要顯式地指定 Id 屬性的值,這里比較特殊。因?yàn)檫@是種子數(shù)據(jù),必須保證每個(gè)字段的值是靜態(tài)的,也就是說(shuō),不管你的程序在哪臺(tái)機(jī)器上運(yùn)行,得保證插入的數(shù)據(jù)是相同的。Id 字段雖然是自增長(zhǎng)的,可你無(wú)法保證實(shí)際使用時(shí)數(shù)據(jù)庫(kù)會(huì)從1開(kāi)始增長(zhǎng),也不保證步長(zhǎng)值一定是1。說(shuō)不定人家是 100、101、102 呢。正因?yàn)槿绱?,如果你忽?Id 的值就會(huì)拋出異常。有一種方案就是使用負(fù)值,這個(gè)不如硬編碼一個(gè)整數(shù)值保險(xiǎn)一些。

完工后,要在 ASP.NET Core 應(yīng)用程序的服務(wù)容器中注冊(cè)。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
// 配置數(shù)據(jù)庫(kù)
string? connectionStr = builder.Configuration.GetConnectionString("prj_cns");
builder.Services.AddDbContext<TestDBContext>(ob =>
{
    ob.UseSqlServer(connectionStr);
}); 
var app = builder.Build();

數(shù)據(jù)庫(kù)的連接字符串在配置文件中(appsettings.json)。

{
  "Logging": {
    ……
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "prj_cns": "Data Source=.\\SQLTEST;Initial Catalog=Demo;Integrated Security=True;Persist Security Info=False;Pooling=False;Encrypt=True;Trust Server Certificate=True"
  }
}

在配置文件中使用 ConnectionStrings 節(jié)點(diǎn)有特殊含義,把數(shù)據(jù)庫(kù)連接字符串放在此節(jié)點(diǎn)下,代碼中獲取時(shí)可以簡(jiǎn)化一些。

builder.Configuration.GetConnectionString("prj_cns");

就是 GetSection("ConnectionStrings")["prj_cns"] 的簡(jiǎn)化版。

======================================================================================

最后要實(shí)現(xiàn)的是 Web API。對(duì)于有明確的模塊功能的代碼我們盡量用 MVC Controller 去實(shí)現(xiàn),Mini-API 只適合沒(méi)啥模塊化的簡(jiǎn)單代碼,也避免把 Main 方法的代碼搞得又長(zhǎng)又臭。

public class TestController : Controller
{
    private readonly TestDBContext _db;
    public TestController(TestDBContext db)
    {
        _db = db;
    }
    [HttpGet("/")]
    public ActionResult Index()
    {
        _db.Database.EnsureCreated();
        return View("~/Views/home.cshtml", _db.Movies.ToArray());
    }
    [HttpPost("/demo/new")]
    public async Task<JsonResult> NewData([FromBody]Movie mvi)
    {
        bool hasany = await _db.Movies.AnyAsync(m => m.Subject!.Equals(mvi.Subject));
        if(hasany)
        {
            return Json( new 
            {
                Code = 2,
                Message = "此電影已存在"
            } );
        }
        await _db.Movies.AddAsync(mvi);
        int i = await _db.SaveChangesAsync();
        if(i <= 0)
        {
            return Json(new { Code = 3, Message = "添加失敗" });
        }
        return Json(new 
        {
            Code = 0,
            Message = "OK"
        });
    }
    [HttpGet("/demo/getall")]
    public IEnumerable<Movie> GetAllMovies()
    {
        return _db.Movies.ToArray();
    }
    [HttpPost("/demo/backup")]
    public ActionResult Backup([FromBody]string filename)
    {
        // 取得當(dāng)前目錄
        IWebHostEnvironment env = HttpContext.RequestServices.GetRequiredService<IWebHostEnvironment>();
        string curDir = env.ContentRootPath;
        string bkfilePath = Path.Combine(curDir, filename);
        string dbname = _db.Database.GetDbConnection().Database;
        if (System.IO.File.Exists(bkfilePath))
        {
            System.IO.File.Delete(bkfilePath);
        }
        string sql = $"BACKUP DATABASE {dbname} TO DISK = '{bkfilePath}'";
        try
        {
            // 不必理會(huì)返回值
            _ = _db.Database.ExecuteSqlRaw(sql);
        }
        catch
        {
            return Json(new
            {
                Code = 1,
                Message = "備份失敗"
            });
        }
        return Json(new
        {
            Code = 0,
            Message = "OK"
        });
    }
}

其他代碼你可以不看,重點(diǎn)關(guān)注 Backup 方法即可,這才是核心。數(shù)據(jù)上下文(我們從 DbContext 類派生了)有一個(gè) Database 屬性,可以對(duì)數(shù)據(jù)庫(kù)進(jìn)行額外的操作,比如EnsureCreated 方法可以檢查數(shù)據(jù)庫(kù)是否存在,不存在就自動(dòng)創(chuàng)建;EnsureDeleted 方法可以檢查數(shù)據(jù)庫(kù)是否已存在,若是,刪除它;你還可以用EnsureDeleted 方法啟動(dòng)一個(gè)事務(wù)。咱們用到的是ExecuteSqlRaw 方法,它的功能是執(zhí)行原始 SQL 語(yǔ)句。何謂“原始”?就是你給啥SQL就是啥,不做參數(shù)化處理,也不過(guò)濾SQL注入攻擊。所以,這個(gè)方法你要在安全的情況下調(diào)用。本例的代碼調(diào)用是安全的,因?yàn)橹皇菆?zhí)行 BACKUP 語(yǔ)句,客戶端不能傳入其他無(wú)關(guān)的數(shù)據(jù)——僅僅需要提供備份的文件名。出于安全考慮,你甚至可以不用客戶端提供文件名,而是直接生成(用日期時(shí)間 + GUID 方式就行)。 當(dāng)然了,在實(shí)際使用中,你應(yīng)該對(duì) API 的調(diào)用做一下驗(yàn)證,比如,客戶端必須傳一個(gè)有效的 token 表明身份等。

由于執(zhí)行 BACKUP 語(yǔ)句不屬于常規(guī)的“增刪改查”操作,無(wú)論執(zhí)行成功還是失敗,返回的整數(shù)值都是負(fù)值。因此,咱們不能依據(jù)返回的數(shù)值來(lái)判斷是否備份成功。不過(guò),倒可以用 try 語(yǔ)句塊來(lái)簡(jiǎn)單判斷。因?yàn)槿绻麄浞莩鲥e(cuò)會(huì)拋異常。把ExecuteSqlRaw 方法調(diào)用寫(xiě)在 try 語(yǔ)句塊中,若拋異常,說(shuō)明備份過(guò)程不順利。

為了讓客戶端能方便管理數(shù)據(jù)維護(hù),你還可以加一個(gè) API,讓客戶端下載備份的文件。不過(guò),如果操作的環(huán)境不夠安全,或沒(méi)有有效的安全措施,有泄露數(shù)據(jù)庫(kù)的風(fēng)險(xiǎn)。

============================================================================================

本示例備份的文件是放在應(yīng)用程序目錄中的,所以,你得事先給程序目錄設(shè)置一下權(quán)限,允許寫(xiě)入文件(參考前文)。這是為了程序部署方便,如果服務(wù)器環(huán)境允許,可以在服務(wù)器上選擇一個(gè)專用的目錄來(lái)存放備份??梢园崖窂脚渲玫?appsettings.json 文件中,程序中直接讀取,這樣你在部署項(xiàng)目后可以手動(dòng)在 appsettings.json 文件中編輯正確的目錄。

{
  ……
  "AllowedHosts": "*",
  "ConnectionStrings": {
    ……
  },
  "backup_dir": "D:\\backups"
}

修改程序代碼,從 backup_dir 配置項(xiàng)讀取目錄。

// 取得當(dāng)前目錄
   IWebHostEnvironment env = HttpContext.RequestServices.GetRequiredService<IWebHostEnvironment>();
   // 讀取配置
   IConfiguration config = HttpContext.RequestServices.GetRequiredService<IConfiguration>();
   // 如果找不到配置的目錄,那就放在應(yīng)用程序目錄下
   string bkfilePath = Path.Combine(bkDir, filename);
   string dbname = _db.Database.GetDbConnection().Database;
   if (System.IO.File.Exists(bkfilePath))
   {
       System.IO.File.Delete(bkfilePath);
   }
   string sql = $"BACKUP DATABASE {dbname} TO DISK = '{bkfilePath}'";
    ……

你不能保證配置 backup_dir 一定有效,如果沒(méi)有配置,就把備份文件放在應(yīng)用程序目錄下。

在運(yùn)行程序后,你要訪問(wèn)一次根 URL(/),目的是讓 EF Core 自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)。如果在 VS 中調(diào)試運(yùn)行,默認(rèn)會(huì)自動(dòng)打?yàn)g覽器的。

創(chuàng)建的數(shù)據(jù)庫(kù)如下:

現(xiàn)在,你可以用任何方式,任何 API 測(cè)試工具來(lái)驗(yàn)證一下,能不能備份。不想用第三方工具,可以用.NET 官方的 httprepl 工具,安裝命令:

dotnet tool install -g microsoft.dotnet-httprepl

安裝好后,在 cmd 中直接輸入 httprepl 就能進(jìn)入對(duì)話狀態(tài)。是的,交互式的。

為了后面在 POST JSON 數(shù)據(jù)時(shí)方便編輯,咱們?cè)O(shè)置一個(gè)默認(rèn)的文本編譯器。免得折騰,你可以直接用 VS Code。

 pref set editor.command.default "C:\Users\Admin\Softs\VSCode\Code.exe"
 pref set editor.command.default.argument "-w"

第一條命令是設(shè)置 VS Code 的路徑,注意,是 VSC 的可執(zhí)行文件路徑。

第二條命令是設(shè)置命令行參數(shù) -w,此參數(shù)是讓 VSC 等待打開(kāi)的文件順利保存并關(guān)閉才退出進(jìn)程。因?yàn)?httprepl 工具在啟動(dòng) VSC 時(shí)會(huì)創(chuàng)建一個(gè)臨 時(shí)文件,并讓 VSC 打開(kāi)來(lái)編輯。

好,現(xiàn)在咱們測(cè)試。

1、運(yùn)行示例程序。

2、CMD中進(jìn)入 httprepl 會(huì)話。

3、連接根URL。

connect http://localhost:5276

4、調(diào)用 /demo/backup 接口。

post /demo/backup -h content-type=application/json

-h 指定HTTP頭。

5、此時(shí),VSC 會(huì)啟動(dòng),我們編輯一下。

注意這里,不用輸入大括號(hào),直接輸入 Javascript 字符串就行,因?yàn)?filename 參數(shù)不是復(fù)雜對(duì)象,而是一個(gè)基礎(chǔ)類型。

6、保存,關(guān)閉 VSC。

7、回到 CMD 窗口,就會(huì)看到備份成功的響應(yīng)。

到備份目錄下,就能看到剛備份的文件了。大小約 3.46 MB 。

要驗(yàn)證備份是否真有效,可以把這個(gè)文件還原回 SQL Server,如果還原后,看到數(shù)據(jù),說(shuō)明備份是沒(méi)有問(wèn)題的。

好了,今天就水到這里了。

到此這篇關(guān)于ASP.NET Core調(diào)用 Web API 備份數(shù)據(jù)庫(kù)的文章就介紹到這了,更多相關(guān)ASP.NET Core調(diào)用 Web API 備份數(shù)據(jù)庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論