警告:這是一個入門級日志,如果你很了解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'."}
程序包管理器控制台主機版本 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、自定義連接字符串方式
參考(https://blogs.msdn.microsoft.com/davidobando/2012/08/14/changing-efs-default-connection-factory-from-localdb-to-sql-server/)
參考(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>-->
然後直接跑代碼
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>
嘿嘿,可想而知,是可以成功的!
PM> SqlLocalDB info MSSQLLocalDB ProjectsV13 v11.0 PM>
找到對應的實例名稱v11.0,我們使用SSMS連接指定數據庫實例
到此為止後面的過程就不用在說了吧!只要找到對應數據庫刪除就好了,對於默認連接方式就是圖中所示的DefaultConnection了!
文章很長,也很臭,說了一大堆沒能解決問題的思路,但是這是我實際解決問題的思路,就當日志吧!看看就好!
Log: 修改結構重新提交