由於 .NET Core 大量的使用 DI 技術,所有註冊到 DI 容器(ServiceCollection)的服務,幾乎都可以用「注入」的方式取得物件。只不過有些類別不容易用建構式注入的方式取得服務,這篇文章將分享如何在 EF Core 3.1 的模型驗證方法中注入 ServiceCollection 裡的任何服務。
簡介模型驗證
假設我們有個 LoginUser
實體模型如下:
using System.ComponentModel.DataAnnotations;
namespace api1.Controllers
{
public class LoginUser
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
}
}
在 ASP․NET Core 與 Entity Framework Core 中,提供了兩種驗證方式:
-
輸入驗證 (Input Validation)
這種類型的驗證主要針對「單一欄位」進行驗證。
以上述範例來說,我們套用了 [Required]
輸入驗證屬性,用以驗證每個屬性在做 模型繫結(Model Bindings) 時,必須填寫內容。
-
模型驗證 (Model Validation)
當所有欄位都已經驗證完畢,整個實體模型在 模型繫結(Model Bindings) 的過程中會將該物件所有屬性填滿,此時就會進行 模型驗證 驗證物件資料的完整性與正確性。
有時候一筆資料的正確性並無法透過「單一欄位」來判斷,如果你要判斷多重欄位的相關性才能確認該物件是否有效,就需要利用 模型驗證 來進行檢查。
以下是模型驗證的使用範例:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace api1.Controllers
{
public class LoginUser : IValidatableObject
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
throw new System.NotImplementedException();
}
}
}
模型驗證 不單單只能驗證同一個 模型類別 (Model Class) 下的多重欄位而已,有時候還可以透過 Entity Framework Core 的 DbContext 對資料庫做出其他資料查詢,並藉此做出資料正確性比對。有時候也可以呼叫外部的 Web API 取得驗證結果。這個時候,就很有可能需要透過 DI 注入其他服務!不過,模型類別並無法使用「建構式注入」來取得「服務」,所以肯定要換個方式取得服務。
透過 ValidationContext 注入服務
還好 ValidationContext 類別有個成員 ValidationContext.GetService(Type) Method 可以用來取得任何註冊在 ServiceCollection 裡面服務,所以我們透過這個 API 就可以順利注入所需的服務物件。
以下是實際的使用範例:
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using api1.Models;
using Microsoft.EntityFrameworkCore;
namespace api1.Controllers
{
public class LoginUser : IValidatableObject
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var db = (ContosouniversityContext)validationContext.GetService(typeof(ContosouniversityContext));
if (!db.User.Any())
{
yield break;
}
if (!db.User.FromSqlInterpolated($"SELECT 1 FROM dbo.User WHERE Username={Username} AND Password={Password}").Any())
{
yield return new ValidationResult($"您登入的帳號或密碼錯誤", new string[] { "Username", "Password" });
}
}
}
}
如果你自訂 ValidationAttribute 的話,也可以透過實作 IsValid(Object, ValidationContext) 來取得 ValidationContext 物件,因此你一樣可以取得任何你想獲得的服務物件!👍
相關連結