[ASP.net Core] 使用Entity Framework Core Database First方式 ...

文章推薦指數: 80 %
投票人數:10人

access data through Entity Framework Core Database First in separate classlibrary project. 嚴格來說,標題應該取名Code First from Database. accessdatathrough EntityFrameworkCore DatabaseFirstinseparateclasslibraryproject嚴格來說,標題應該取名CodeFirstfromDatabase.Netframework時期就有類似的玩意兒↓  前言趁著.netCore2剛推出,把最近從恆逸資訊上課的記憶抄寫下來,畢竟發現.netCore好多事情要自己手動處理以前.NetFramework要透過EntityFramework存取資料,大概步驟如下:1.專案先透過NuGet加入EntityFramework套件參考2.專案加入*.edmx(EFDesignerfromDatabaseFirst開發方式)3.把自己的XXXContext物件(有繼承DbContext類別的那個)new出來即可存取資料。

以上對話框點一點選一選就完成了XD然而在.netCore變成....1.專案先透過NuGet加入Microsoft.EntityFrameworkCore.SqlServer套件參考(此為專門存取SqlServer用的Provider)還有Microsoft.EntityFrameworkCore.Tools(此為從資料庫建立模型Class的用途)如果是ASP.netCore2的專案,上述兩個預設都已經加好在Microsoft.AspNetCore.All套件底下如果是一般ClassLibrary專案則要自己手動加入那兩個套件。

2..NetCore沒有*.edmx檔,取而代之必須下指令產生一堆*.cs模型3.各種地方相依性注入(連線字串、XXXDbContext....等等)4.由於已透過相依性注入取得物件,所以不必每次存取資料前都把XXXContextnew出來,這一點倒是在Controller減少了些程式碼實作本文環境:Win10、VisualStudio201715.5.5、SQLServer2014DeveloperEdition以下StepbyStep,照著做應該就可以建立出ASP.netCore2網站透過另一個類別庫專案的EntityFrameworkCore2來存取資料。

※ASP.netCore3、.Net5之後的專案大同小異,只差別在Startup.cs基本程式碼不一樣,未來就不累述了1.建立一個ASP.netCore2乾淨的空白Web專案上述的Web應用程式範本為.netCore2新推出的RazorPage架構,很像以前的asp,只是多了PageModel可以資料繫結而它右邊的Web應用程式(模型-檢視-控制器)範本才是熟悉的MVC架構,這裡選「空白」,別讓VisualStudio多加其他有的沒的程式碼混淆視聽XD 2.對著方案右鍵,加入新增.NETStandard類別庫專案(如此建置出來的類別庫才能供.netCore和.netframework兩邊使用)※如果你很確定會用到這個DBAccess的專案都是.NetCore的話,類別庫專案也是可以選擇.NetCore而非.NetStandard此類別庫專案就是專門存放資料模型的專案※.Net5的話,目標Framework就一律選.Net5吧預設多出來的Class1.cs檔案可以刪除,然後對著ClassLibrary類別庫專案右鍵>管理NuGet套件依序加入「Microsoft.EntityFrameworkCore.SqlServer」、「Microsoft.EntityFrameworkCore.Tools」這兩個套件Microsoft.EntityFrameworkCore.Tools和Microsoft.EntityFrameworkCore.Tools.DotNet兩者都可以把DB轉成Class模型只是差在後續Console指令下得不一樣既然微軟官方寫Microsoft.EntityFrameworkCore.Tools那就裝Microsoft.EntityFrameworkCore.Tools就好了微軟官網:在ASP.NETCore上使用EFCore搭配現有資料庫的使用者入門※2019-03-19追記:透過Nuget加入兩個套件「Microsoft.EntityFrameworkCore.SqlServer」、「Microsoft.EntityFrameworkCore.Tools」記得版本都選擇相同↓版本不同的話,應該會發生如下錯誤↓ 3.確認資料庫的資料準備好了4.VisualStudo最上方的選單工具>開啟套件管理器主控台,準備下指令在主控台Consoel要下的指令↓Scaffold-DbContext"Server=.\sqlexpress2014;Database=Blogging;Trusted_Connection=True;"Microsoft.EntityFrameworkCore.SqlServer-OutputDirModels-Force-UseDatabaseNames-TablesBlog,PostScaffold-DbContext為剛剛安裝Tools套件的指令,DB連線字串依自己情況決定-OutputDir指定要輸出到專案根目錄下哪個資料夾,本文為Models資料夾-Force為工具產出的.cs檔要強制覆寫現存檔案(DB欄位Schema異動後,就給這個)-Tables參數,指定只需要載入哪些資料表(以逗號區隔),留意大小寫最好和DB裡一樣避免出現黃字警告,沒給-Tables參數的話,預設抓DB全部的Table-UseDatabaseNames,程式碼產生出來的類別名稱要和資料庫裡的一模一樣其它參數請見官網說明:EntityFrameworkCore工具的參考-VisualStudio中的套件管理員主控台以下是執行完指令後的結果↑上面得留意主控台的預設專案要選對專案,這會影響Model(.cs檔)輸出到哪個專案※2019-03-19追記:整個程式碼路徑不可以有中文或特殊符號,否則會報錯錯誤訊息可能是 「Unabletoresolvestartupproject''.Usingproject'DBClassLibrary'asthestartupproject.Thespecifiedframeworkversion'2.1'couldnotbeparsedThespecifiedframework'Microsoft.NETCore.App',version'2.1'wasnotfound. -Checkapplicationdependenciesandtargetaframeworkversioninstalledat:   C:\ProgramFiles\dotnet\ -Installing.NETCoreprerequisitesmighthelpresolvethisproblem:   https://go.microsoft.com/fwlink/?LinkID=798306&clcid=0x409 -The.NETCoreframeworkandSDKcanbeinstalledfrom:   https://aka.ms/dotnet-download -Thefollowingversionsareinstalled:   2.1.9at[C:\ProgramFiles\dotnet\shared\Microsoft.NETCore.App]」或「Scaffold-DbContext:以"1"引數呼叫"GetFullPath"時發生例外狀況:"不合法的路徑格式。

"位於線路:1字元:1+Scaffold-DbContext"Server=.\sqlexpress2014;Database=MyDB;Trusted_Connect...+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  +CategoryInfo     :NotSpecified:(:)[Scaffold-DbContext],MethodInvocationException  +FullyQualifiedErrorId:ArgumentException,Scaffold-DbContext」↓我專案路徑含有[  ]就這樣報錯.....※2021-04-09追記:貌似到了.Net5要把類別庫專案設定為啟動專案再輸入指令,Web專案才不會出現錯誤訊息無法參考「Microsoft.EntityFrameworkCore.Design」的樣子↓如果出現黃字請忽略它,程式還是可以正常執行,只是DB中原本不允許NULL的bool欄位變成C#的bool?型別(容我日後有空再想辦法處理)主控台指令完畢後,接著到XXXContext.cs裡,把OnConfiguring方法刪除,微軟建議改使用相依性注入方式設定DB連線字串完整程式碼如下:usingSystem; usingMicrosoft.EntityFrameworkCore; usingMicrosoft.EntityFrameworkCore.Metadata; namespaceDBClassLibrary.Models { publicpartialclassBloggingContext:DbContext { publicvirtualDbSetBlog{get;set;} publicvirtualDbSetPost{get;set;} //不知道從哪一版開始,工具就會自動加入建構子,沒加的話,自己加上吧~ publicBloggingContext(DbContextOptionsoptions) :base(options) {} protectedoverridevoidOnModelCreating(ModelBuildermodelBuilder) { modelBuilder.Entity(entity=> { entity.Property(e=>e.Url).IsRequired(); }); modelBuilder.Entity(entity=> { entity.Property(e=>e.Title).HasColumnName("TITLE"); entity.HasOne(d=>d.Blog) .WithMany(p=>p.Post) .HasForeignKey(d=>d.BlogId); }); } } } 2019.8.20追記上述談到把OnConfiguring方法刪除,但如果專案中有寫到類似以下代碼,在執行階段就會報錯Anunhandledexceptionoccurredwhileprocessingtherequest.InvalidOperationException:NodatabaseproviderhasbeenconfiguredforthisDbContext.AprovidercanbeconfiguredbyoverridingtheDbContext.OnConfiguringmethodorbyusingAddDbContextontheapplicationserviceprovider.IfAddDbContextisused,thenalsoensurethatyourDbContexttypeacceptsaDbContextOptionsobjectinitsconstructorandpassesittothebaseconstructorforDbContext.publicclassMyDb_Utility { publicstaticvoidAddRecord() { //非DI方式使用DbContext↓ using(BloggingContextdb=newBloggingContext()) { //...略 db.SaveChanges(); } } }有時候真的就是想在某某Utility類別或某某Service類別中存取資料庫,而且不是DI方式注入DbContext,這時XXXContext.cs的OnConfiguring方法改成以下//每次重新GenerateCode都要手動修改↓ protectedoverridevoidOnConfiguring(DbContextOptionsBuilderoptionsBuilder) { if(!optionsBuilder.IsConfigured) { //須事先透過Nuget加入Microsoft.Extensions.Configuration.Json組件參考 IConfigurationconfig=newConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json",optional:true,reloadOnChange:true) .Build(); optionsBuilder.UseSqlServer(config.GetConnectionString("DefaultConnection")); } }↑解法參考:'NodatabaseproviderhasbeenconfiguredforthisDbContext'onSignInManager.PasswordSignInAsync5.至目前為止,Model類別庫專案建置完成,中間可能會遇到一些問題,請見文章最下方解決接著ASP.netCore2Web專案加入該類別庫專案的參考,準備在網站中存取資料。

6.對著ASP.netCore專案右鍵新增>加入項目,選ASP.net組態檔(appsettings.json)要把本機開發時期的DB連線字串填進去,網站正式上線時,此檔案內容要記得修改↑因為是json格式,所以反斜線兩次跳脫字元7.DB連線字串配置好後,要在網站啟動時,讀取該連線字串,並註冊服務要使用資料庫XXXContext物件開啟Startup.cs檔,完整程式碼和說明如下:Asp.netCore2↓usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Threading.Tasks; usingMicrosoft.AspNetCore.Builder; usingMicrosoft.AspNetCore.Hosting; usingMicrosoft.AspNetCore.Http; usingMicrosoft.Extensions.DependencyInjection; /*引用*/ usingMicrosoft.Extensions.Configuration; usingMicrosoft.EntityFrameworkCore; namespaceCoreWebSite { publicclassStartup { //使用相依性注入方式取得物件 privateIConfiguration_config; publicStartup(IConfigurationconfig) { this._config=config; } //註冊服務 publicvoidConfigureServices(IServiceCollectionservices) { //↓待會在Controller便可使用相依性注入取得BloggingContext物件 services.AddDbContext(options=> options.UseSqlServer(this._config.GetConnectionString("DefaultConnection"))); services.AddMvc(); } publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv) { if(env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } //HelloWorld是VisualStudio產生的程式碼,要拿掉 //app.Run(async(context)=> //{ //awaitcontext.Response.WriteAsync("HelloWorld!"); //}); app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); } } } Asp.netCore3的Startup.cs↓usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Threading.Tasks; usingMicrosoft.AspNetCore.Authentication.Cookies; usingMicrosoft.AspNetCore.Builder; usingMicrosoft.AspNetCore.Hosting; usingMicrosoft.AspNetCore.Http; usingMicrosoft.AspNetCore.Mvc.Infrastructure; usingMicrosoft.EntityFrameworkCore; usingMicrosoft.Extensions.Configuration; usingMicrosoft.Extensions.DependencyInjection; usingMicrosoft.Extensions.DependencyInjection.Extensions; usingMicrosoft.Extensions.Hosting; namespaceWeb_Event { publicclassStartup { privatereadonlyIConfiguration_config; publicStartup(IConfigurationconfig) { this._config=config; } publicvoidConfigureServices(IServiceCollectionservices) { services.AddSession(options=> { //Setashorttimeoutforeasytesting. options.IdleTimeout=TimeSpan.FromMinutes(30); options.Cookie.HttpOnly=true; //Makethesessioncookieessential options.Cookie.IsEssential=true; }); //↓待會在Controller便可使用相依性注入取得BloggingContext物件 services.AddDbContext(options=> options.UseSqlServer(this._config.GetConnectionString("DefaultConnection"))); services.AddControllersWithViews().AddNewtonsoftJson(); } publicvoidConfigure(IApplicationBuilderapp,IWebHostEnvironmentenv) { if(env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection();//Controller、Action不必再加上[RequireHttps]屬性 app.UseSession(); app.UseStaticFiles(); app.UseRouting(); //留意先執行驗證... app.UseAuthentication(); app.UseAuthorization();//Controller、Action才能加上[Authorize]屬性 app.UseEndpoints(endpoints=> { endpoints.MapControllerRoute( name:"default", pattern:"{controller=Home}/{action=Index}/{id?}"); }); } } }  8.由於一開始我建立的是空白專案,所以要自己新增Controllers和Views兩個資料夾然後在Controllers資料夾按右鍵加入控制器,選第一個即可剛剛在Startup.cs裡的Configure方法,我使用的路由是app.UseMvcWithDefaultRoute();方便起見,新增的Controller就命名為HomeController9.HomeController裡的Action方法如下usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Threading.Tasks; usingMicrosoft.AspNetCore.Mvc; /*引用*/ usingSystem.Text; namespaceCoreWebSite.Controllers { publicclassHomeController:Controller { //使用相依性注入來取得XXXContext物件 privatereadonlyDBClassLibrary.Models.BloggingContext_context; publicHomeController(DBClassLibrary.Models.BloggingContextcontext) { this._context=context; } publicIActionResultIndex() { //查詢資料 varquery=this._context.Blog.AsQueryable(); StringBuildersb=newStringBuilder(); if(query.Any()) {//查有資料 sb.Append("

    "); foreach(DBClassLibrary.Models.Blogiteminquery) { sb.Append($@"
  • {item.BlogId}-{item.Url}
  • "); } sb.Append("
"); } //懶得加View,所以用Content回傳,意思有到就好XD returnContent(sb.ToString(),"text/html",Encoding.UTF8); } } }執行結果完成↓※2019-05-06追記,在View(*.cshtml)注入DbContext方式@injectDBClassLibrary.Models.BloggingContextcontext  ※2019-05-07追記,如何在ActionFilter中存取DbContextUseDbContextinActionFilterMiddleware  ※2021-03-19追記,如果編譯無誤執行時期發生InvalidOperationException:Theentitytype'YourClassName'requiresaprimarykeytobedefined.Ifyouintendedtouseakeylessentitytypecall'HasNoKey()'.這種錯誤,應該是你的資料庫Table裡有取名兩個以上欄位叫XXXA_ID、XXXB_IDEntityFrameworkCore無法識別哪一個欄位是PrimaryKey,這種情況要到YourClass.cs把PKProperty加上[Key]就可以了↓usingSystem; usingSystem.Collections.Generic; //引用這個↓ usingSystem.ComponentModel.DataAnnotations; namespaceWeb_Event.DBModels { publicpartialclassYourClass { //↓加這個 [Key] publiclongsysid{get;set;} publiclongUserCouponCode_SystemID{get;set;} publicstringvouch_no{get;set;} } } 補充如果想更新資料庫模型檔案(*.cs)的話,就直接在套件管理器主控台再下同樣的Scaffold-DbContext指令(記得加上-force覆蓋原始檔案)即可。

若是手動誤刪Models資料夾把底下所有模型都刪除,而想再重新產生模型檔案,可能會出現錯誤訊息:Buildfailed.解決方法如下先把其它專案都卸載,然後類別庫專案設為起始專案再下指令會發生另一錯誤Startupproject'DBClassLibrary'targetsframework'.NETStandard'.Thereisnoruntimeassociatedwiththisframework,andprojectstargetingitcannotbeexecuteddirectly.TousetheEntityFrameworkCorePackageManagerConsoleToolswiththisproject,addanexecutableprojecttargeting.NETFrameworkor.NETCorethatreferencesthisproject,andsetitasthestartupproject;or,updatethisprojecttocross-target.NETFrameworkor.NETCore.大意是說,想使用EFCore的話,要有一個可直接執行的專案(例如:ASP.netCore)參考此類別庫專案並設為起始專案才行(但我們剛剛已試過會發生Buildfailed,所以此法行不通)或另一辦法,修改此專案為跨平台專案,如下:編輯類別庫專案的.csproj檔 netcoreapp2.0;net461;netstandard2.0 2.0.5 填netcoreapp2.0是因為我另一專案為ASP.netCore2net461是因為Microsoft.EntityFrameworkCore.SqlServer和Microsoft.EntityFrameworkCore.Tools這兩個2.0.1版本支援.Netframework版本從4.6.1起跳區段要填什麼則看Microsoft.NETCore.App最新版是哪個版本,我另一專案是.netCore2,就最好填2.0.0~2.x.x之間區段影響的是NuGet安裝套件相依版本區段影響的是.NETCore世界裡Microsoft.NETCore.App的執行階段版本詳細見StackOverflow討論:What'sthedifferencebetweenand?修改完.csproj後,在套件管理器主控台再下同樣Scaffold-DbContext指令應該就可以把那一堆模型類別檔加回來,加回來後記得剛剛.csproj的設定也改回去之後原本卸載的ASP.netCore專案再重新載入並設為起始專案,按下Ctrl+F5鍵就都可以正常執行↑手動刪除模型類別檔案所造成的問題,我想未來改版應該會改進,畢竟之前.netframework加入*.edmx檔案的方式太便利也沒什麼差錯2018.3.5補充如果同一方案下有一個.netframewok專案也想使用.netStandard專案裡的EFCore的話(我的情況是WebServiceWebApplication專案,因為ASP.netCore2還沒有WebService只有WebAPI.....)請參考以下....還有.netCore、.netframework專案想共用EF的話,.netStandard專案得加入參考EFCore而不是以前的Entityframework,因為.netStandard專案不相容Entityframework .netStandard專案的.csproj檔需要修改一下 netcoreapp2.0;net461;netstandard2.0 2.0.3 接下來.netframework專案加入.netStandard專案參考和透過NuGet加入Microsoft.EntityFrameworkCore參考然後一執行有可能出現以下問題System.IO.FileLoadException:Couldnotloadfileorassembly'System.ValueTuple,Version=0.0.0.0,Culture=neutral,PublicKeyToken=cc7b13ffcd2ddd51'oroneofitsdependencies.Thelocatedassembly'smanifestdefinitiondoesnotmatchtheassemblyreference.(ExceptionfromHRESULT:0x80131040)研究許久,.netframework專案引用.netStandard專案時,還須要解決BindingRedirect問題當重建.netframework專案,出現黃色警告,雙擊兩下為Web.config檔加入bindingredirect如此一來.netframework專案即可正常引用.netStandard專案並執行了~如果剛好,好死不死,那個.netframework專案是WebSite的話,可能要把NuGetPackage全部安裝(不確定,我沒親自試過XD)看此篇討論所寫的:Issueswith.NETStandard2.0with.NETFramework&NuGet#481※話說有人的環境會複雜到同時存在.netframework和.netCore嗎XD結語目前.netCore技術跟.netframework比起來感覺缺東缺西,很多事情要手動處理,看來再等個.netCore3版本出來說不定會更靠譜原本最後打算寫個ASP.netCore2網站如何部署IIS主要把握兩點:1.安裝.NETCoreWindowsServerHosting(載點在官網:HostASP.NETCoreonWindowswithIIS)讓IIS出現ASPNETCoreModule模組可以處理ASP.netCore網站,個人猜測未來WindowsServer2016之後的作業系統IIS如果有內建的話,或許此步驟可省略(?)2.網站要先經過發行Publish動作,像以往直接複製檔案到正式機部署是行不通的既然網路上已有人把部署ASP.netCore寫得很詳細,這邊就不多寫了IIS-運行ASP.NETCore網站 回首頁 本頁段落 Anunhandledexceptionoccurredwhileprocessingtherequest.



請為這篇文章評分?