文章目錄
ASP.NET Identity 2 使用 EntityFramework 搭配 Oracle
相信開發 ASP.NET 比較久的朋友都曾經聽過甚至是用過 ASP.NET Membership - ASP.NET 2.0 時期的預設使用者權限管理系統,但也相信不少人對它深惡痛絕,不可否認以現在的觀點來看它確實有許多缺點,不過它的的確確曾經替不少工程師解決了問題,而在收集許多 ASP.NET Membership 開發者意見後打造出全新的使用者權限系統就是 - ASP.NET Identity
ASP.NET Identity 從 ASP.NET MVC 5 成為預設專案範本後陸陸續續用過幾次,只是都用的不深。最近剛好有個新專案,需求與 ASP.NET Identity 的幾個功能剛好搭得上,所以趁這個機會紀錄一下
首先因為公司主力 DB 使用 Oracle,所以就先紀錄如何讓 ASP.NET Identity 使用 EntityFramework Oracle 開始吧
建立 ASP.NET Identity 所需的資料庫物件
因為 ASP.NET Identity 預設使用 code first 模式,在實際存取 DB 時才直接建立相關資料庫 table,index,trigger,而這樣的方式大部份都會直接被 DBA 拒絕,為了與 production 一致,就從開發時期就使用 database first 來開發,因此我們必需先手動建立資料庫相關物件(建議分段執行,尤其是 TRIGGER
那段)
CREATE TABLE "AspNetRoles" (
"Id" NVARCHAR2(128) NOT NULL,
"Name" NVARCHAR2(256) NOT NULL,
PRIMARY KEY ("Id")
);
CREATE TABLE "AspNetUserRoles" (
"UserId" NVARCHAR2(128) NOT NULL,
"RoleId" NVARCHAR2(128) NOT NULL,
PRIMARY KEY ("UserId", "RoleId")
);
CREATE TABLE "AspNetUsers" (
"Id" NVARCHAR2(128) NOT NULL,
"Email" NVARCHAR2(256) NULL,
"EmailConfirmed" NUMBER(1) NOT NULL,
"PasswordHash" NVARCHAR2(256) NULL,
"SecurityStamp" NVARCHAR2(256) NULL,
"PhoneNumber" NVARCHAR2(256) NULL,
"PhoneNumberConfirmed" NUMBER(1) NOT NULL,
"TwoFactorEnabled" NUMBER(1) NOT NULL,
"LockoutEndDateUtc" TIMESTAMP(7) NULL,
"LockoutEnabled" NUMBER(1) NOT NULL,
"AccessFailedCount" NUMBER(10) NOT NULL,
"UserName" NVARCHAR2(256) NOT NULL,
PRIMARY KEY ("Id")
);
CREATE TABLE "AspNetUserClaims" (
"Id" NUMBER(10) NOT NULL,
"UserId" NVARCHAR2(128) NOT NULL,
"ClaimType" NVARCHAR2(256) NULL,
"ClaimValue" NVARCHAR2(256) NULL,
PRIMARY KEY ("Id")
);
CREATE SEQUENCE "AspNetUserClaims_SEQ";
CREATE OR REPLACE TRIGGER "AspNetUserClaims_INS_TRG"
BEFORE INSERT ON "AspNetUserClaims"
FOR EACH ROW
BEGIN
SELECT "AspNetUserClaims_SEQ".NEXTVAL INTO :NEW."Id" FROM DUAL;
END;
CREATE TABLE "AspNetUserLogins" (
"LoginProvider" NVARCHAR2(128) NOT NULL,
"ProviderKey" NVARCHAR2(128) NOT NULL,
"UserId" NVARCHAR2(128) NOT NULL,
PRIMARY KEY ("LoginProvider", "ProviderKey", "UserId")
);
CREATE UNIQUE INDEX "RoleNameIndex" ON "AspNetRoles" ("Name");
CREATE INDEX "IX_AspNetUserRoles_UserId" ON "AspNetUserRoles" ("UserId");
CREATE INDEX "IX_AspNetUserRoles_RoleId" ON "AspNetUserRoles" ("RoleId");
CREATE UNIQUE INDEX "UserNameIndex" ON "AspNetUsers" ("UserName");
CREATE INDEX "IX_AspNetUserClaims_UserId" ON "AspNetUserClaims" ("UserId");
CREATE INDEX "IX_AspNetUserLogins_UserId" ON "AspNetUserLogins" ("UserId");
ALTER TABLE "AspNetUserRoles"
ADD CONSTRAINT "FK_UserRoles_Roles" FOREIGN KEY ("RoleId") REFERENCES "AspNetRoles" ("Id")
ON DELETE CASCADE;
ALTER TABLE "AspNetUserRoles"
ADD CONSTRAINT "FK_UserRoles_Users" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id")
ON DELETE CASCADE;
ALTER TABLE "AspNetUserClaims"
ADD CONSTRAINT "FK_UserClaims_Users" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id")
ON DELETE CASCADE;
ALTER TABLE "AspNetUserLogins"
ADD CONSTRAINT "FK_UserLogins_Users" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id")
ON DELETE CASCADE;
安裝 ASP.NET Identity
透過安裝 Microsoft.AspNet.Identity.EntityFramework
會將 EntityFramework
及 Microsoft.AspNet.Identity.Core
一併安裝進來
安裝 Oracle provider
透過安裝 Oracle.ManagedDataAccess.EntityFramework
連帶安裝 Oracle.ManagedDataAccess
修改 web.config 的 ConnectionString
- Oracle server 的 ip 及帳密
- providerName 為
Oracle.ManagedDataAccess.Client
範例如下
<add name="DefaultConnection" providerName="Oracle.ManagedDataAccess.Client" connectionString="User Id=TestIdentity;Password=password;Data Source=localhost:1521/XE"/>
將 db table 與程式 class 綁定
修改 ApplicationDbContext
,加入 override OnModelCreating
方法
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//這必需在第一行
base.OnModelCreating(modelBuilder);
//schema 名稱,如果建立時沒有刻意指定小寫,預設就是大寫
modelBuilder.HasDefaultSchema("TESTIDENTITY");
//以下依實際情境來調整 table name
modelBuilder.Entity<ApplicationUser>().ToTable("AspNetUsers");
modelBuilder.Entity<IdentityRole>().ToTable("AspNetRoles");
modelBuilder.Entity<IdentityUserRole>().ToTable("AspNetUserRoles");
modelBuilder.Entity<IdentityUserClaim>().ToTable("AspNetUserClaims");
modelBuilder.Entity<IdentityUserLogin>().ToTable("AspNetUserLogins");
}
如果出現
ORA-01918: user 'xx' does not exist
請先檢查 HasDefaultSchema 設定完整程式碼
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false) { } public static ApplicationDbContext Create() { return new ApplicationDbContext(); } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //這必需在第一行 base.OnModelCreating(modelBuilder); //schema 名稱,如果建立時沒有刻意指定小寫,預設就是大寫 modelBuilder.HasDefaultSchema("TESTIDENTITY"); //以下依實際情境來調整 table name modelBuilder.Entity<ApplicationUser>().ToTable("AspNetUsers"); modelBuilder.Entity<IdentityRole>().ToTable("AspNetRoles"); modelBuilder.Entity<IdentityUserRole>().ToTable("AspNetUserRoles"); modelBuilder.Entity<IdentityUserClaim>().ToTable("AspNetUserClaims"); modelBuilder.Entity<IdentityUserLogin>().ToTable("AspNetUserLogins"); } }
設定完成
直接 Register
順利登入也成功將資料寫至 Oracle
心得
之前工作開發主要搭配的 database 都是 MS-SQL,使用的習慣跟工具的熟悉度都很高,加上與微軟其他開發工具充份整合,讓開發可以 focus 在核心功能的打造,從 MS-SQL 改用 Oracle 難免還是比較不熟悉,很多工具、語法、設定都要重新適應,有時候卡了很久才發現是一個小小的動作,的確會讓人比較沮喪些,但終究是搞定了第一步,接著才是更大的挑戰呀
參考資訊
文章作者 Yowko Tsai
上次更新 2021-11-03
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。