警告:這是一個入門級日誌,如果你很瞭解CodeFirst,那請繞道 背景:這篇日誌記錄我使用Entity FrameWork CodeFirst時出現的錯誤和解決問題的過程,雖然有點曲折……勿噴 備註:這確實算是Entity FrameWork CodeFirst的問題個人也不知道應該給文章加什麼樣 ...
警告:這是一個入門級日誌,如果你很瞭解CodeFirst,那請繞道
背景:這篇日誌記錄我使用Entity FrameWork CodeFirst時出現的錯誤和解決問題的過程,雖然有點曲折……勿噴
備註:這確實算是Entity FrameWork CodeFirst的問題個人也不知道應該給文章加什麼樣的關鍵字和標題,方便各位朋友搜索
一、問題出現
當我參考 洞庭夕照 博客 ASP.NET MVC5 網站開發實踐 - 概述 按照代碼一點點嘗試CodeFirst(雖然這不是一個針對CodeFirst的教程),當添加到註冊頁面執行並點擊註冊按鈕後,出現了這樣的錯誤(菜鳥考慮問題的思維):
出現原因補充:這個錯誤在CodeFirst第一次執行的時候是沒有問題的,當你刪除了CodeFirst自動生成的資料庫db文件,再重新嘗試運行就會出現問題了,不會再重新生成資料庫文件!
用戶代碼未處理DataException,初始化資料庫時出現異常。請參見內部異常的詳細信息。 InnerException:基礎提供程式在open上失敗,{"The underlying provider failed on Open."} InnerException:{"Cannot attach the file 'E:\\ProjectOwn\\PlantGarden\\MvcWeChat\\App_Data\\MvcWeChatDb20160812114343.mdf' as database 'MvcWeChatDb20160812114343'."}
二、考慮過程第一點
因為是菜鳥,對Entity 和CodeFirst都不甚瞭解,所以太不上自己深究,於是乎我採取的第一個解決方式和大家一樣,百度或者谷歌,當然,解決方案不是那麼容易找到,雖然很多一樣的錯誤,但是通常出現的場合,並不能解決問題。雖然如此,我們確實有所借鑒,在錯誤提示的搜索過程中,我發現很多文章提及到 PM 的 Update-Database 命令,看含義應該是用來手動更新資料庫的,但是既然我的代碼使用了CodeFirst沒有生成成功資料庫,是不是這個命令會提示我什麼有價值的東西呢?且看下麵代碼:
程式包管理器控制台主機版本 2.8.60318.667 PM> Update-Database Specify the '-Verbose' flag to view the SQL statements being applied to the target database. No migrations configuration type was found in the assembly 'MvcWeChat'. (In Visual Studio you can use the Enable-Migrations command from Package Manager Console to add a migrations configuration).
PM> Update-Database -Verbose Using StartUp project 'MvcWeChat'. Using NuGet project 'MvcWeChat'. Specify the '-Verbose' flag to view the SQL statements being applied to the target database. System.Data.Entity.Migrations.Infrastructure.MigrationsException: No migrations configuration type was found in the assembly 'MvcWeChat'. (In Visual Studio you can use the Enable-Migrations command from Package Manager Console to add a migrations configuration). 在 System.Data.Entity.Utilities.TypeFinder.FindType(Type baseType, String typeName, Func`2 filter, Func`2 noType, Func`3 multipleTypes, Func`3 noTypeWithName, Func`3 multipleTypesWithName) 在 System.Data.Entity.Migrations.Utilities.MigrationsConfigurationFinder.FindMigrationsConfiguration(Type contextType, String configurationTypeName, Func`2 noType, Func`3 multipleTypes, Func`3 noTypeWithName, Func`3 multipleTypesWithName) 在 System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.FindConfiguration() 在 System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.GetMigrator() 在 System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run() 在 System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate) 在 System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate) 在 System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner) 在 System.Data.Entity.Migrations.Design.ToolingFacade.Update(String targetMigration, Boolean force) 在 System.Data.Entity.Migrations.UpdateDatabaseCommand.<>c__DisplayClass2.<.ctor>b__0() 在 System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command) No migrations configuration type was found in the assembly 'MvcWeChat'. (In Visual Studio you can use the Enable-Migrations command from Package Manager Console to add a migrations configuration). PM> Enable-Migrations No context type was found in the assembly 'MvcWeChat'. PM> Enable-Migrations Checking if the context targets an existing database... Code First Migrations enabled for project MvcWeChat.DAL. PM> Update-Database Specify the '-Verbose' flag to view the SQL statements being applied to the target database. No pending explicit migrations. Unable to update database to match the current model because there are pending changes and automatic migration is disabled. Either write the pending model changes to a code-based migration or enable automatic migration. Set DbMigrationsConfiguration.AutomaticMigrationsEnabled to true to enable automatic migration. You can use the Add-Migration command to write the pending model changes to a code-based migration. PM> Update-Database -Verbose Using StartUp project 'MvcWeChat'. Using NuGet project 'MvcWeChat.DAL'. Specify the '-Verbose' flag to view the SQL statements being applied to the target database. Target database is: 'MvcWeChatDb20160812114343' (DataSource: (LocalDb)\v11.0, Provider: System.Data.SqlClient, Origin: Configuration). No pending explicit migrations. Unable to update database to match the current model because there are pending changes and automatic migration is disabled. Either write the pending model changes to a code-based migration or enable automatic migration. Set DbMigrationsConfiguration.AutomaticMigrationsEnabled to true to enable automatic migration. You can use the Add-Migration command to write the pending model changes to a code-based migration. PM>
我們一步步分析:
首先Update-Database:
PM> Update-Database Specify the '-Verbose' flag to view the SQL statements being applied to the target database. No migrations configuration type was found in the assembly 'MvcWeChat'. (In Visual Studio you can use the Enable-Migrations command from Package Manager Console to add a migrations configuration).
提示我在 MvcWeChat 中沒有找到遷移配置,就是並沒有找到資料庫更新的什麼配置,於是先使用提示中的 Update-Database -Verbose看詳情,錯誤還是差不多,於是考慮使用第二個提示命令Enable-Migrations :
PM> Enable-Migrations No context type was found in the assembly 'MvcWeChat'.
結果發現在項目MvcWeChat中沒有 context,這我差不多就理解了,我的項目結構中DbContext是在DAL中的,並不是在主項目MvcWeChat中的 (DbContext 是數據上下文,Entity資料庫交互關鍵類型 ,也是CodeFirst的關鍵,我也沒有理解多透徹,不懂的自己百度呢)
於是更改項目:
在執行代碼:
PM> Enable-Migrations Checking if the context targets an existing database... Code First Migrations enabled for project MvcWeChat.DAL.
按照代碼的講法,為項目 MvcWeChat.DAL 啟用了Code First 遷移,看起來不錯。再看看代碼,項目MvcWeChat.DAL中被添加了一個類
namespace MvcWeChat.DAL.Migrations { using System; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; internal sealed class Configuration : DbMigrationsConfiguration<MvcWeChat.DAL.MvcWeChatDbContext> { public Configuration() { AutomaticMigrationsEnabled = false; } protected override void Seed(MvcWeChat.DAL.MvcWeChatDbContext context) { // This method will be called after migrating to the latest version. // You can use the DbSet<T>.AddOrUpdate() helper extension method // to avoid creating duplicate seed data. E.g. // // context.People.AddOrUpdate( // p => p.FullName, // new Person { FullName = "Andrew Peters" }, // new Person { FullName = "Brice Lambson" }, // new Person { FullName = "Rowan Miller" } // ); // } } }
上面的代碼應該是更新資料庫時候用,且不管
這個時候我再執行 :
PM> Update-Database Specify the '-Verbose' flag to view the SQL statements being applied to the target database. No pending explicit migrations. Unable to update database to match the current model because there are pending changes and automatic migration is disabled. Either write the pending model changes to a code-based migration or enable automatic migration. Set DbMigrationsConfiguration.AutomaticMigrationsEnabled to true to enable automatic migration. You can use the Add-Migration command to write the pending model changes to a code-based migration. PM>
沒有掛起顯式遷移。
無法更新資料庫,以匹配當前的模型,因為有掛起的更改和自動遷移被禁用。掛起模式更改寫入代碼基於遷移或啟用自動遷移。設置對啟用自動遷移到 DbMigrationsConfiguration.AutomaticMigrationsEnabled。
您可以使用添加遷移命令掛起模式更改寫入基於代碼的遷移。
嘿嘿,聯想起之前的代碼:的亮點是 AutomaticMigrationsEnabled = false; 於是我們該為 true 再執行:
PM> Update-Database Specify the '-Verbose' flag to view the SQL statements being applied to the target database. No pending explicit migrations. Applying automatic migration: 201608180628454_AutomaticMigration. System.Data.SqlClient.SqlException (0x80131904): Cannot attach the file 'E:\ProjectOwn\PlantGarden\MvcWeChat\App_Data\MvcWeChatDb20160812114343.mdf' as database 'MvcWeChatDb20160812114343'. 在 System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection) 在 System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection) 在 System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection) 在 System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions) 在 System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions) 在 System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry) 在 System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry) 在 System.Data.SqlClient.SqlConnection.Open() 在 System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher.<Open>b__36(DbConnection t, DbConnectionInterceptionContext c) 在 System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext](TTarget target, Action`2 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed) 在 System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher.Open(DbConnection connection, DbInterceptionContext interceptionContext) 在 System.Data.Entity.Migrations.DbMigrator.ExecuteStatementsInternal(IEnumerable`1 migrationStatements, DbConnection connection) 在 System.Data.Entity.Migrations.DbMigrator.<>c__DisplayClass30.<ExecuteStatements>b__2e() 在 System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<>c__DisplayClass1.<Execute>b__0() 在 System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation) 在 System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Action operation) 在 System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable`1 migrationStatements, DbTransaction existingTransaction) 在 System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable`1 migrationStatements) 在 System.Data.Entity.Migrations.Infrastructure.MigratorBase.ExecuteStatements(IEnumerable`1 migrationStatements) 在 System.Data.Entity.Migrations.DbMigrator.ExecuteOperations(String migrationId, VersionedModel targetModel, IEnumerable`1 operations, IEnumerable`1 systemOperations, Boolean downgrading, Boolean auto) 在 System.Data.Entity.Migrations.DbMigrator.AutoMigrate(String migrationId, VersionedModel sourceModel, VersionedModel targetModel, Boolean downgrading) 在 System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.AutoMigrate(String migrationId, VersionedModel sourceModel, VersionedModel targetModel, Boolean downgrading) 在 System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId) 在 System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId) 在 System.Data.Entity.Migrations.DbMigrator.UpdateInternal(String targetMigration) 在 System.Data.Entity.Migrations.DbMigrator.<>c__DisplayClassc.<Update>b__b() 在 System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(Action mustSucceedToKeepDatabase) 在 System.Data.Entity.Migrations.Infrastructure.MigratorBase.EnsureDatabaseExists(Action mustSucceedToKeepDatabase) 在 System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration) 在 System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration) 在 System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run() 在 System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate) 在 System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate) 在 System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner) 在 System.Data.Entity.Migrations.Design.ToolingFacade.Update(String targetMigration, Boolean force) 在 System.Data.Entity.Migrations.UpdateDatabaseCommand.<>c__DisplayClass2.<.ctor>b__0() 在 System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command) ClientConnectionId:a2fd50b1-37f8-48d2-bb4b-3690cb8a3fc1 Error Number:1832,State:1,Class:14 Cannot attach the file 'E:\ProjectOwn\PlantGarden\MvcWeChat\App_Data\MvcWeChatDb20160812114343.mdf' as database 'MvcWeChatDb20160812114343'.
這個時候,終於報了和本文一開始遇到的錯誤一樣的錯誤信息了!
呵呵,錯誤到此本思路結束了,但是並沒有解決問題啊!
三、考慮過程第二點
在第一段中,我們發現是我的DB沒有找到!DB的配置在Web.config 中
<configuration> <configSections> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> </configSections> <connectionStrings> <add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=MvcWeChatDb20160812114343;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\MvcWeChatDb20160812114343.mdf" /> </connectionStrings>
這段代碼是添加 DbContext 的時候 entityFramework 自動生成的連接字元串
錯誤在連接字元串,我就考慮查找 entityFramework 連接的問題,據一番搜索,我瞭解到Entity Framework連接的兩種形式
1、Entity Framework預設連接方式
2、自定義連接字元串方式
參考(http://www.cnblogs.com/kenshincui/p/3286103.html)
當我沒有添加自定義的連接字元串(connectionStrings)的時候,Entity Framework使用預設連接方式,我們給一個空項目通過NuGet添加Entity Framework之後,web.config中會出現配置:
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> </configSections> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="v13.0" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework> </configuration>
重點是下麵的小節entityFramework,有一個預設的連接方式defaultConnectionFactory,LocalDbConnectionFactory
下麵提供預設連接配置方式:
LocalDb
<entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="v11.0"/> </parameters> </defaultConnectionFactory> </entityFramework>
Sql Server
<entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework"> <parameters> <parameter value="Data Source=.; Integrated Security=True; MultipleActiveResultSets=True"/> </parameters> </defaultConnectionFactory> </entityFramework>
我們首先嘗試這種預設的方式,先註釋掉上面的那段自定義連接語句
<!--<connectionStrings> <add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=MvcWeChatDb20160812114343;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\MvcWeChatDb20160812114343.mdf" /> </connectionStrings>-->
然後直接跑代碼
這個問題呢百度上倒是能搜索到相關解決方案 http://www.cnblogs.com/summit7ca/p/4559694.html
這篇博客貌似有點問題,代碼寫錯了,查看LoaclDB版本用:
PM> sqllocaldb info
MSSQLLocalDB
v11.0
PM>
再結合我之前貼出的代碼:
<entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="v13.0" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework>
發現版本號不一樣,於是改版本號為v11.0,然後執行:
PM> sqllocaldb create v11.0
已使用版本 11.0 創建 LocalDB 實例“v11.0”。
PM>
再跑代碼,出現了和開頭一樣的錯誤,這說明使用預設連接和自定義連接字元串方式沒有多大不同
{"An exception occurred while initializing the database. See the InnerException for details."}
{"The underlying provider failed on Open."}
{"Cannot attach the file 'E:\\ProjectOwn\\PlantGarden\\MvcWeChat\\App_Data\\DefaultConnection.mdf' as database 'DefaultConnection'."}
四、考慮過程第三點
經過上面種種問題,我才意識到了文章開頭我補充的那句話:
出現原因:這個錯誤在CodeFirst第一次執行的時候是沒有問題的,當你刪除了CodeFirst自動生成的資料庫db文件,再重新嘗試運行就會出現問題了,不會再重新生成資料庫文件!
因為我發現,在我運行過程中,有過三次成功,都是第一次,一次是預設連接,一次是自定義連接,一次是修改了自定義連接資料庫名之後,於是於是,我再一次的修改了自定義連接資料庫名稱:
<connectionStrings> <add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=MvcWeChatDb20160811;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\MvcWeChatDb20160811.mdf" /> </connectionStrings>
嘿嘿,可想而知,是可以成功的!
到這裡我意識到了一個問題,EntityFramework CodeFirst 機制:當資料庫存在時候連接存在的資料庫,當不存在時,建立資料庫,我使用的是 LocalDb 方式,雖然是刪除了資料庫文件,但是資料庫並沒有刪除啊!這是點到點了,那麼怎麼刪除 LocalDb 資料庫呢?
方式有兩種,SSDT模式和SSMS模式,
一、SSDT模式是 VS-》視圖-》SQL Server 對象資源管理器,直接通過管理器管理,當然如果你和我一樣運氣不好,只能使用第二種了
貌似是vs2015中的SSDT和vs2012中的SSDT版本衝突,我的VS2015中的可以用,vs2012中的不可以
二、SSMS方式解決
和之前查看LocalDb版本命令一樣:SqlLocalDB
首先是命令介紹:
路徑:%ProgramFiles%\Microsoft SQL Server\110\Tools\Binn\SqlLocalDB.exe
使用方法:
CMD下
- Cd %ProgramFiles%\Microsoft SQL Server\110\Tools\Binn
- SqlLocalDB /?
PM下:
直接使用 SqlLocalDB /?
通過使用 SqlLocalDB info 查看資料庫實例
PM> SqlLocalDB info
MSSQLLocalDB
ProjectsV13
v11.0
PM>
找到對應的實例名稱v11.0,我們使用SSMS連接指定資料庫實例
到此為止後面的過程就不用在說了吧!只要找到對應資料庫刪除就好了,對於預設連接方式就是圖中所示的DefaultConnection了!
文章很長,也很臭,說了一大堆沒能解決問題的思路,但是這是我實際解決問題的思路,就當日誌吧!看看就好!
Log: 修改結構重新提交