設計與使用ASP.NET Core Web API 1-使用Entity Framework Core存取資料

by vivid 28. 十月 2020 02:07

.NET Magazine國際中文電子雜誌
作 者:許薰尹
文章編號: N201022302
出刊日期: 2020/10/28

在這個《設計與使用ASP.NET Core Web API》系列的文章中,將介紹如何使用Visual Studio 2019建立ASP.NET Core Web API專案(使用.NET Core 3.x版),透過Entity Framework Core的資料庫優先方式(Database First)存取SQL Server Express Pubs範例資料庫的資料。為了便於開發人員了解ASP.NET Core Web API提供的功能,我們將利用Swagger為ASP.NET Core Web API設計線上文件,便於程式設計人員進行Web API功能測試,同時搭配NSwagStudio工具程式自動產生叫用ASP.NET Core Web API的用戶端類別,然後將用戶端類別加入ASP.NET Core MVC的專案,並在控制器中呼叫它們,設計資料的新增、刪除、修改、查詢的用戶端程式介面。。

第一篇文章我們就從建立ASP.NET Core Web API專案,並整合Entity Framework Core讀取資料庫資料開始。

建立ASP.NET Core Web API專案

啟動Visual Studio 2019開發工具,直接點選「Start」視窗下方的「Continue without code」選項,進入Visual Studio 2019開發工具編輯畫面。點選Visual Studio 2019開發工具的「File」-「New」-「Project」項目,在「Create a New Project」對話盒中,選取「ASP.NET Core Web Application」範本專案,請參考下圖所示:

clip_image002

圖 1:建立ASP.NET Core 應用程式專案。

在「Configure your new project」對話盒中,設定專案名稱與專案存放路徑,然後按下「Create」鍵,請參考下圖所示:

clip_image004

圖 2:設定專案名稱與專案存放路徑。

在「Create a new ASP.NET Core Web Application」對話盒中,確認左上方的清單選取「.NET Core」,右上方的清單ASP.NET Core版本為「ASP.NET Core 3.x」,選取下方的「API」樣版專案,勾選「Configure for HTTPS」、清除勾選「Enable Docker Support」核取方塊,確定右方的「Authentication」項目設定為「No Authentication」,然後按下「Create」按鈕建立專案,請參考下圖所示:

clip_image006

圖 3:選取下方的「API」樣版專案。

預設專案中就包含了一個天氣預測的ASP.NET Core Web API服務:「WeatherForecastController」,參考以下範例程式碼,從程式中我們可以得知一個Web API便是一個繼承自「ControllerBase」的類別,當你送HTTP GET請求給此Web API時,將執行「WeatherForecastController」類別「Get」方法,此方法將回傳各城市預估的天氣預報:

  • MyWebAPI\Controllers\WeatherForecastController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace MyWebAPI.Controllers {
  [ApiController]
  [Route( "[controller]" )]
  public class WeatherForecastController : ControllerBase {
    private static readonly string[] Summaries = new[]
    {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController( ILogger<WeatherForecastController> logger ) {
      _logger = logger;
    }

    [HttpGet]
    public IEnumerable<WeatherForecast> Get( ) {
      var rng = new Random( );
      return Enumerable.Range( 1, 5 ).Select( index => new WeatherForecast {
        Date = DateTime.Now.AddDays( index ),
        TemperatureC = rng.Next( -20, 55 ),
        Summary = Summaries[ rng.Next( Summaries.Length ) ]
      } )
      .ToArray( );
    }
  }
}

 

選取Visual Studio 2019開發工具「Build」-「Build Solution」項目,編譯目前的專案,確認程式碼能正確編譯。完成後可以執行網站,只要在Visual Studio 2019開發工具,按「CTRL」+「F5」組合鍵來執行網站應用程式,此時會啟動瀏覽器,以及開發用的IIS Express伺服器,自動指定一個埠號(port)來執行網站應用程式。執行結果參考如下,因為「WeatherForecastController」類別上方有套用「[Route( "[controller]" )]」Attribute,因此只要在瀏覽器網址輸入「https://localhost:埠號/weatherforecast」就會自動叫用Web API,並顯示執行結果:

clip_image008

圖 4:weatherforecast Web API執行結果。

使用Entity Framewrok Core建立資料存取程式

要在ASP.NET Core Web API專案之中使用Entity Framewrok Core提供的類別來建立SQL Server Express資料庫的資料存取程式之前,需要在專案中使用 Nuget套件管理員下載、安裝以下套件:

  • · 「Microsoft.EntityFrameworkCore.SqlServer」套件。
  • · 「Microsoft.EntityFrameworkCore.Tools」套件。

在「Solution Explorer」視窗選取ASP.NET Core Web API專案名稱,按一下滑鼠右鍵,從選單選取「Manage NuGet Packages」項目,在「Nuget:MyWebAPI」視窗右上方文字方塊中輸入「Microsoft.EntityFrameworkCore.SqlServer」關鍵字搜尋套件,找到套件之後,從「Version」下拉式清單方塊中選取適當版本之後按「Install」按鈕進行安裝,參考下圖所示:

clip_image010

圖 5:安裝「Microsoft.EntityFrameworkCore.SqlServer」套件。

接下來重複上一個步驟的動作,在「Nuget:MyWebAPI」視窗右上方文字方塊中輸入「Microsoft.EntityFrameworkCore.Tools」關鍵字搜尋套件,找到套件之後,從「Version」下拉式清單方塊中選取適當版本之後按「Install」按鈕安裝,參考下圖所示:

clip_image012

圖 6:安裝「Microsoft.EntityFrameworkCore.Tools」套件。

下一步是從現有的Pubs資料庫來建立Enity Framework Core實體模型(Entity Model)。Pubs資料庫可以從微軟網站下載下來安裝:「https://docs.microsoft.com/zh-tw/dotnet/framework/data/adonet/sql/linq/downloading-sample-databases」。

從Visual Studio 2019開發工具「Tools」- 「Nuget Package Manager」項目開啟選單,從選單選擇「Package Manager Console」選項,開啟「Package Manager Console」對話盒,直接輸入並執行以下指令來指定資料庫連接字串、資料提供者(Provider),以進行反向工程步驟,這樣就可這以從現有的Pubs資料庫來建立模型:

Scaffold-DbContext "Server=.\sqlexpress;Database=Pubs;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models

指令後的「-OutputDir」參數用來指定將產生的類別存放的資料夾,以本例來說為「Models」資料夾,若資料夾不存在,則會自動建立。這個命令執行結果請參考下圖所示:

clip_image014

圖 7:進行反向工程。

若不想要建立所有資料表的模型,我們可以利用「-Tables」參數來設定想使用的資料表。指令執行完成後,專案中將會產生一個「PubsContext.cs」檔案,用來讓Entity Framework Core存取資料庫,資料庫的每一個資料表,會產生一個對應的模型類別,參考下圖為執行結果:

clip_image016

圖 8:工具自動產生模型類別。

檢視工具產生出來的「Stores.cs」檔案程式碼,其中包含「Stores」類別對應到「Pubs」資料庫「Stores」資料表中的一筆資料:

  • MyWebAPI\Models\Store.cs

using System;
using System.Collections.Generic;

namespace MyWebAPI.Models {
  public partial class Stores {
    public Stores( ) {
      Sales = new HashSet<Sales>( );
    }

    public string StorId { get; set; }
    public string StorName { get; set; }
    public string StorAddress { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }

    public virtual ICollection<Sales> Sales { get; set; }
  }
}

 

就名稱而言命名時使用英文的複數不能夠正確的表達:此類別建立的一個物件,只對應到資料表中的一筆資料,我們可以透過「Bricelam.EntityFrameworkCore.Pluralizer」套件來處理英文名稱單、複數的對應問題。

實體類別命名

在「Solution Explorer」視窗選取專案名稱,按一下滑鼠右鍵,從選單選取「Manage NuGet Packages」項目,在視窗右上方文字方塊中輸入「Bricelam.EntityFrameworkCore.Pluralizer」關鍵字搜尋套件,找到套件之後,從「Version:」下拉式清單方塊,選取適當版本之後按「Install」按鈕安裝,請參考下圖所示:

clip_image018

圖 9:安裝「Bricelam.EntityFrameworkCore.Pluralizer」套件。

這個套件只要安裝完就會生效。讓我們先刪除目前專案中「Models」資料夾中所有檔案,以免跟上一個步驟建立的檔案混淆在一起。接著從Visual Studio 2019開發工具「Tools」- 「Nuget Package Manager」-從選單選擇「Package Manager Console」選項,開啟「Package Manager Console」對話盒,直接執行以下指令,利用「–force」參數重新產生實體類別:

Scaffold-DbContext "Server=.\sqlexpress;Database=Pubs;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models –force

執行結果請參考下圖所示:

clip_image020

圖 10:進行反向工程。

這次檢視產生出的CS檔案的檔名,變成以英文的單數命名:

clip_image022

圖 11:工具自動產生模型類別。

再檢視工具產生出來的「Stores.s」檔案,其中的類別名稱為「Store」類別符合英文單數命名原則:

  • MyWebAPI\Models\Store.cs

using System;
using System.Collections.Generic;

namespace MyWebAPI.Models {
  public partial class Store {
    public Store( ) {
      Sales = new HashSet<Sale>( );
    }

    public string StorId { get; set; }
    public string StorName { get; set; }
    public string StorAddress { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }

    public virtual ICollection<Sale> Sales { get; set; }
  }
}

 

設計控制器

接下來我們將利用Visual Studio 2019工具,快速建立新增、刪除、修改、查詢「Stores」資料表資料的程式碼。從Visual Studio 2019開發工具 -「Solution Explorer」視窗 -從專案名稱下「Controllers」資料夾上方,按滑鼠右鍵,從快捷選單選擇「Add」- 「Controller」項目,請參考下圖所示:

clip_image024

圖 12:新增控制器。

在「Add New Scaffolded Item」對話盒中選取「API Controller with actions, using Entity Framework」項目,然後按下「Add 」按鈕,請參考下圖所示:

clip_image026

圖 13:選取控制器範本。

在「Add API Controller with actions, using Entity Framework」對話盒,將「Model class」設定為「Store」類別;「Data context class」為「PubsContext」類別,然後設定控制器名稱(Controller name)為「StoresController」,接著按下「Add」按鈕,請參考下圖所示:

clip_image028

圖 14:新增「StoresController」控制器。

接著Visual Studio 2019便會在「Controllers」資料夾下,新增控制器程式碼:「StoresController.cs」。為了方便於測試,我們先對自動產生的「StoresController」類別程式碼進行一些修改動作。

首先修改「StoresController」路由為「[Route( "[controller]" )]」,此外為了讓稍後要介紹的「Swagger」為Web API產生較易讀的使用文件以及產生用戶端叫用的程式碼,建議在方法上方套用「HttpGet」、「HttpPut」、「HttpPost」、「HttpDelete」Attribute,並定義一個有意義的名稱,請參考以下程式列表:

  • MyWebAPI\Controllers\StoresController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MyWebAPI.Models;

namespace MyWebAPI.Controllers {
  [Route( "[controller]" )]
  [ApiController]
  public class StoresController : ControllerBase {
    private readonly PubsContext _context;

    public StoresController( PubsContext context ) {
      _context = context;
    }

    // GET: api/Stores
    [HttpGet( "GetStores" )]
    public async Task<ActionResult<IEnumerable<Store>>> GetStores( ) {
      return await _context.Stores.ToListAsync( );
    }

    // GET: api/Stores/5
    [HttpGet( "GetStoreById/{id}" )]
    public async Task<ActionResult<Store>> GetStore( string id ) {
      var store = await _context.Stores.FindAsync( id );

      if ( store == null ) {
        return NotFound( );
      }

      return store;
    }

    // PUT: api/Stores/5
    // To protect from overposting attacks, enable the specific properties you want to bind to, for
    // more details, see https://go.microsoft.com/fwlink/?linkid=2123754.
    [HttpPut( "UpdateStore/{id}" )]
    public async Task<IActionResult> PutStore( string id, Store store ) {
      if ( id != store.StorId ) {
        return BadRequest( );
      }

      _context.Entry( store ).State = EntityState.Modified;

      try {
        await _context.SaveChangesAsync( );
      }
      catch ( DbUpdateConcurrencyException ) {
        if ( !StoreExists( id ) ) {
          return NotFound( );
        }
        else {
          throw;
        }
      }

      return NoContent( );
    }

    // POST: api/Stores
    // To protect from overposting attacks, enable the specific properties you want to bind to, for
    // more details, see https://go.microsoft.com/fwlink/?linkid=2123754.
    [HttpPost("CreateStore")]
    public async Task<ActionResult<Store>> PostStore( Store store ) {
      _context.Stores.Add( store );
      try {
        await _context.SaveChangesAsync( );
      }
      catch ( DbUpdateException ) {
        if ( StoreExists( store.StorId ) ) {
          return Conflict( );
        }
        else {
          throw;
        }
      }

      return CreatedAtAction( "GetStore", new { id = store.StorId }, store );
    }

    // DELETE: api/Stores/5
    [HttpDelete( "DeleteStore/{id}" )]
    public async Task<ActionResult<Store>> DeleteStore( string id ) {
      var store = await _context.Stores.FindAsync( id );
      if ( store == null ) {
        return NotFound( );
      }

      _context.Stores.Remove( store );
      await _context.SaveChangesAsync( );

      return store;
    }

    private bool StoreExists( string id ) {
      return _context.Stores.Any( e => e.StorId == id );
    }
  }
}

 

在「Startup」類別設定與使用服務

修改專案中的「Startup.cs」檔案,在「ConfigureServices」方法加入以下程式,叫用「IServiceCollection」實體的「AddDbContext」方法,指定連線字串,註冊「PubsContext」物件。通常包含機密資料的連線字串建議不要寫死在程式之中,比較推薦在「appsettings.json」檔案設定資料庫連接字串,在此為簡單起見,暫不討論。

  • MyWebAPI\Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MyWebAPI.Models;

namespace MyWebAPI {
  public class Startup {
    public Startup( IConfiguration configuration ) {
      Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices( IServiceCollection services ) {
      services.AddControllers( );
      services.AddDbContext<PubsContext>( options =>
           options.UseSqlServer( "Server=.\\sqlexpress;Database=Pubs;Trusted_Connection=True;" ) );

    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure( IApplicationBuilder app, IWebHostEnvironment env ) {
      if ( env.IsDevelopment( ) ) {
        app.UseDeveloperExceptionPage( );
      }

      app.UseHttpsRedirection( );

      app.UseRouting( );

      app.UseAuthorization( );

      app.UseEndpoints( endpoints => {
        endpoints.MapControllers( );
      } );
    }
  }
}

 

測試與執行

選取Visual Studio 開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯。在Visual Studio開發工具,按CTRL+F5執行網站首頁(請注意:埠號可能會依據實際上的操作而有所不同,請修改為實際的埠號),然後在瀏覽器輸入以下網址(URL)

https://localhost:44311/stores/GetStores

執行結果請參考下圖所示,在Chrome瀏覽器測試時,「Pubs」資料庫「Stores」資料表資料將以JSON格式呈現:

clip_image030

圖 15:查詢「Stores」資料表資料。

在Chrome瀏覽器輸入以下網址(URL)將可查詢編號為「6380」的商店資料:

https://localhost:44311/stores/GetStoreById/6380

執行結果請參考下圖所示:

clip_image032

圖 16:查詢編號為「6380」的商店資料。

Tags:

.NET Magazine國際中文電子雜誌 | C# | ASP.NET Web API | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List