警告:这是一个入门级日志,如果你很了解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: 修改结构重新提交