EntityFramework.psm1 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. # Copyright (c) Microsoft Corporation. All rights reserved.
  2. $InitialDatabase = '0'
  3. $installPath = $args[0]
  4. $knownExceptions = @(
  5. 'System.Data.Entity.Migrations.Infrastructure.MigrationsException',
  6. 'System.Data.Entity.Migrations.Infrastructure.AutomaticMigrationsDisabledException',
  7. 'System.Data.Entity.Migrations.Infrastructure.AutomaticDataLossException'
  8. )
  9. <#
  10. .SYNOPSIS
  11. Enables Code First Migrations in a project.
  12. .DESCRIPTION
  13. Enables Migrations by scaffolding a migrations configuration class in the project. If the
  14. target database was created by an initializer, an initial migration will be created (unless
  15. automatic migrations are enabled via the EnableAutomaticMigrations parameter).
  16. .PARAMETER EnableAutomaticMigrations
  17. Specifies whether automatic migrations will be enabled in the scaffolded migrations configuration.
  18. If ommitted, automatic migrations will be disabled.
  19. .PARAMETER ProjectName
  20. Specifies the project that the scaffolded migrations configuration class will
  21. be added to. If omitted, the default project selected in package manager
  22. console is used.
  23. .PARAMETER Force
  24. Specifies that the migrations configuration be overwritten when running more
  25. than once for a given project.
  26. #>
  27. function Enable-Migrations
  28. {
  29. [CmdletBinding(DefaultParameterSetName = 'ProjectName')]
  30. param (
  31. [alias("Auto")]
  32. [switch] $EnableAutomaticMigrations,
  33. [string] $ProjectName,
  34. [switch] $Force
  35. )
  36. try
  37. {
  38. $commands = New-MigrationsCommandsNoConfiguration $ProjectName
  39. $commands.EnableMigrations($EnableAutomaticMigrations, $Force)
  40. }
  41. catch [Exception]
  42. {
  43. $exception = $_.Exception
  44. $exceptionType = $exception.GetType()
  45. if ($exceptionType.FullName -eq 'System.Data.Entity.Migrations.Design.ToolingException')
  46. {
  47. if ($knownExceptions -notcontains $exception.InnerType)
  48. {
  49. Write-Host $exception.InnerStackTrace
  50. }
  51. }
  52. elseif (!(Test-TypeInherits $exceptionType 'System.Data.Entity.Migrations.Infrastructure.MigrationsException'))
  53. {
  54. Write-Host $exception
  55. }
  56. throw $exception.Message
  57. }
  58. }
  59. <#
  60. .SYNOPSIS
  61. Scaffolds a migration script for any pending model changes.
  62. .DESCRIPTION
  63. Scaffolds a new migration script and adds it to the project.
  64. .PARAMETER Name
  65. Specifies the name of the custom script.
  66. .PARAMETER Force
  67. Specifies that the migration user code be overwritten when re-scaffolding an
  68. existing migration.
  69. .PARAMETER ProjectName
  70. Specifies the project that contains the migration configuration type to be
  71. used. If ommitted, the default project selected in package manager console
  72. is used.
  73. .PARAMETER StartUpProjectName
  74. Specifies the configuration file to use for named connection strings. If
  75. omitted, the specified project's configuration file is used.
  76. .PARAMETER ConfigurationTypeName
  77. Specifies the migrations configuration to use. If omitted, migrations will
  78. attempt to locate a single migrations configuration type in the target
  79. project.
  80. .PARAMETER ConnectionStringName
  81. Specifies the name of a connection string to use from the application's
  82. configuration file.
  83. .PARAMETER ConnectionString
  84. Specifies the the connection string to use. If omitted, the context's
  85. default connection will be used.
  86. .PARAMETER ConnectionProviderName
  87. Specifies the provider invariant name of the connection string.
  88. #>
  89. function Add-Migration
  90. {
  91. [CmdletBinding(DefaultParameterSetName = 'ConnectionStringName')]
  92. param (
  93. [parameter(Position = 0,
  94. Mandatory = $true)]
  95. [string] $Name,
  96. [switch] $Force,
  97. [string] $ProjectName,
  98. [string] $StartUpProjectName,
  99. [string] $ConfigurationTypeName,
  100. [parameter(ParameterSetName = 'ConnectionStringName')]
  101. [string] $ConnectionStringName,
  102. [parameter(ParameterSetName = 'ConnectionStringAndProviderName',
  103. Mandatory = $true)]
  104. [string] $ConnectionString,
  105. [parameter(ParameterSetName = 'ConnectionStringAndProviderName',
  106. Mandatory = $true)]
  107. [string] $ConnectionProviderName)
  108. try
  109. {
  110. $commands = New-MigrationsCommands $ProjectName $StartUpProjectName $ConfigurationTypeName $ConnectionStringName $ConnectionString $ConnectionProviderName
  111. $commands.AddMigration($Name, $Force)
  112. }
  113. catch [Exception]
  114. {
  115. $exception = $_.Exception
  116. $exceptionType = $exception.GetType()
  117. if ($exceptionType.FullName -eq 'System.Data.Entity.Migrations.Design.ToolingException')
  118. {
  119. if ($knownExceptions -notcontains $exception.InnerType)
  120. {
  121. Write-Host $exception.InnerStackTrace
  122. }
  123. }
  124. elseif (!(Test-TypeInherits $exceptionType 'System.Data.Entity.Migrations.Infrastructure.MigrationsException'))
  125. {
  126. Write-Host $exception
  127. }
  128. throw $exception.Message
  129. }
  130. }
  131. <#
  132. .SYNOPSIS
  133. Applies any pending migrations to the database.
  134. .DESCRIPTION
  135. Updates the database to the current model by applying pending migrations.
  136. .PARAMETER SourceMigration
  137. Only valid with -Script. Specifies the name of a particular migration to use
  138. as the update's starting point. If ommitted, the last applied migration in
  139. the database will be used.
  140. .PARAMETER TargetMigration
  141. Specifies the name of a particular migration to update the database to. If
  142. ommitted, the current model will be used.
  143. .PARAMETER Script
  144. Generate a SQL script rather than executing the pending changes directly.
  145. .PARAMETER Force
  146. Specifies that data loss is acceptable during automatic migration of the
  147. database.
  148. .PARAMETER ProjectName
  149. Specifies the project that contains the migration configuration type to be
  150. used. If ommitted, the default project selected in package manager console
  151. is used.
  152. .PARAMETER StartUpProjectName
  153. Specifies the configuration file to use for named connection strings. If
  154. omitted, the specified project's configuration file is used.
  155. .PARAMETER ConfigurationTypeName
  156. Specifies the migrations configuration to use. If omitted, migrations will
  157. attempt to locate a single migrations configuration type in the target
  158. project.
  159. .PARAMETER ConnectionStringName
  160. Specifies the name of a connection string to use from the application's
  161. configuration file.
  162. .PARAMETER ConnectionString
  163. Specifies the the connection string to use. If omitted, the context's
  164. default connection will be used.
  165. .PARAMETER ConnectionProviderName
  166. Specifies the provider invariant name of the connection string.
  167. #>
  168. function Update-Database
  169. {
  170. [CmdletBinding(DefaultParameterSetName = 'ConnectionStringName')]
  171. param (
  172. [string] $SourceMigration,
  173. [string] $TargetMigration,
  174. [switch] $Script,
  175. [switch] $Force,
  176. [string] $ProjectName,
  177. [string] $StartUpProjectName,
  178. [string] $ConfigurationTypeName,
  179. [parameter(ParameterSetName = 'ConnectionStringName')]
  180. [string] $ConnectionStringName,
  181. [parameter(ParameterSetName = 'ConnectionStringAndProviderName',
  182. Mandatory = $true)]
  183. [string] $ConnectionString,
  184. [parameter(ParameterSetName = 'ConnectionStringAndProviderName',
  185. Mandatory = $true)]
  186. [string] $ConnectionProviderName)
  187. # TODO: If possible, convert this to a ParameterSet
  188. if ($SourceMigration -and !$script)
  189. {
  190. throw '-SourceMigration can only be specified with -Script.'
  191. }
  192. try
  193. {
  194. $commands = New-MigrationsCommands $ProjectName $StartUpProjectName $ConfigurationTypeName $ConnectionStringName $ConnectionString $ConnectionProviderName
  195. $commands.UpdateDatabase($SourceMigration, $TargetMigration, $Script, $Force)
  196. }
  197. catch [Exception]
  198. {
  199. $exception = $_.Exception
  200. $exceptionType = $exception.GetType()
  201. if ($exceptionType.FullName -eq 'System.Data.Entity.Migrations.Design.ToolingException')
  202. {
  203. if ($knownExceptions -notcontains $exception.InnerType)
  204. {
  205. Write-Host $exception.InnerStackTrace
  206. }
  207. }
  208. elseif (!(Test-TypeInherits $exceptionType 'System.Data.Entity.Migrations.Infrastructure.MigrationsException'))
  209. {
  210. Write-Host $exception
  211. }
  212. throw $exception.Message
  213. }
  214. }
  215. <#
  216. .SYNOPSIS
  217. Displays the migrations that have been applied to the target database.
  218. .DESCRIPTION
  219. Displays the migrations that have been applied to the target database.
  220. .PARAMETER ProjectName
  221. Specifies the project that contains the migration configuration type to be
  222. used. If ommitted, the default project selected in package manager console
  223. is used.
  224. .PARAMETER StartUpProjectName
  225. Specifies the configuration file to use for named connection strings. If
  226. omitted, the specified project's configuration file is used.
  227. .PARAMETER ConfigurationTypeName
  228. Specifies the migrations configuration to use. If omitted, migrations will
  229. attempt to locate a single migrations configuration type in the target
  230. project.
  231. .PARAMETER ConnectionStringName
  232. Specifies the name of a connection string to use from the application's
  233. configuration file.
  234. .PARAMETER ConnectionString
  235. Specifies the the connection string to use. If omitted, the context's
  236. default connection will be used.
  237. .PARAMETER ConnectionProviderName
  238. Specifies the provider invariant name of the connection string.
  239. #>
  240. function Get-Migrations
  241. {
  242. [CmdletBinding(DefaultParameterSetName = 'ConnectionStringName')]
  243. param (
  244. [string] $ProjectName,
  245. [string] $StartUpProjectName,
  246. [string] $ConfigurationTypeName,
  247. [parameter(ParameterSetName = 'ConnectionStringName')]
  248. [string] $ConnectionStringName,
  249. [parameter(ParameterSetName = 'ConnectionStringAndProviderName',
  250. Mandatory = $true)]
  251. [string] $ConnectionString,
  252. [parameter(ParameterSetName = 'ConnectionStringAndProviderName',
  253. Mandatory = $true)]
  254. [string] $ConnectionProviderName)
  255. try
  256. {
  257. $commands = New-MigrationsCommands $ProjectName $StartUpProjectName $ConfigurationTypeName $ConnectionStringName $ConnectionString $ConnectionProviderName
  258. $commands.GetMigrations()
  259. }
  260. catch [Exception]
  261. {
  262. $exception = $_.Exception
  263. $exceptionType = $exception.GetType()
  264. if ($exceptionType.FullName -eq 'System.Data.Entity.Migrations.Design.ToolingException')
  265. {
  266. if ($knownExceptions -notcontains $exception.InnerType)
  267. {
  268. Write-Host $exception.InnerStackTrace
  269. }
  270. }
  271. elseif (!(Test-TypeInherits $exceptionType 'System.Data.Entity.Migrations.Infrastructure.MigrationsException'))
  272. {
  273. Write-Host $exception
  274. }
  275. throw $exception.Message
  276. }
  277. }
  278. function New-MigrationsCommandsNoConfiguration($ProjectName)
  279. {
  280. $project = Get-MigrationsProject $ProjectName
  281. Build-Project $project
  282. Load-EntityFramework
  283. try
  284. {
  285. return New-Object 'System.Data.Entity.Migrations.MigrationsCommands' @(
  286. $project,
  287. $project,
  288. $null,
  289. $null,
  290. $null,
  291. $null,
  292. $PSCmdlet )
  293. }
  294. catch [System.Management.Automation.MethodInvocationException]
  295. {
  296. throw $_.Exception.InnerException
  297. }
  298. }
  299. function New-MigrationsCommands($ProjectName, $StartUpProjectName, $ConfigurationTypeName, $ConnectionStringName, $ConnectionString, $ConnectionProviderName)
  300. {
  301. $project = Get-MigrationsProject $ProjectName
  302. $startUpProject = Get-MigrationsStartUpProject $StartUpProjectName
  303. Build-Project $project
  304. Build-Project $startUpProject
  305. Load-EntityFramework
  306. try
  307. {
  308. return New-Object 'System.Data.Entity.Migrations.MigrationsCommands' @(
  309. $project,
  310. $startUpProject,
  311. $ConfigurationTypeName,
  312. $ConnectionStringName,
  313. $ConnectionString,
  314. $ConnectionProviderName,
  315. $PSCmdlet )
  316. }
  317. catch [System.Management.Automation.MethodInvocationException]
  318. {
  319. throw $_.Exception.InnerException
  320. }
  321. }
  322. function Get-MigrationsProject($name)
  323. {
  324. if ($name)
  325. {
  326. return Get-SingleProject $name
  327. }
  328. $project = Get-Project
  329. Write-Verbose ('Using NuGet project ''' + $project.Name + '''.')
  330. return $project
  331. }
  332. function Get-MigrationsStartUpProject($name)
  333. {
  334. if ($name)
  335. {
  336. return Get-SingleProject $name
  337. }
  338. $startupProjectPaths = $DTE.Solution.SolutionBuild.StartupProjects
  339. if (!$startupProjectPaths)
  340. {
  341. throw 'No start-up project found. Please use the -StartupProject parameter.'
  342. }
  343. if ($startupProjectPaths.Length -gt 1)
  344. {
  345. throw 'More than one start-up project found. Please use the -StartUpProject parameter.'
  346. }
  347. $startupProjectPath = $startupProjectPaths[0]
  348. if (!(Split-Path -IsAbsolute $startupProjectPath))
  349. {
  350. $solutionPath = Split-Path $DTE.Solution.Properties.Item('Path').Value
  351. $startupProjectPath = Join-Path $solutionPath $startupProjectPath -Resolve
  352. }
  353. $startupProject = $DTE.Solution.Projects | ?{
  354. $fullName = $_.FullName
  355. if ($fullName -and $fullName.EndsWith('\'))
  356. {
  357. $fullName = $fullName.Substring(0, $fullName.Length - 1)
  358. }
  359. return $fullName -eq $startupProjectPath
  360. }
  361. Write-Verbose ('Using StartUp project ''' + $startupProject.Name + '''.')
  362. return $startupProject
  363. }
  364. function Get-SingleProject($name)
  365. {
  366. $project = Get-Project $name
  367. if ($project -is [array])
  368. {
  369. throw "More than one project '$name' was found. Specify the full name of the one to use."
  370. }
  371. return $project
  372. }
  373. function Load-EntityFramework()
  374. {
  375. [System.AppDomain]::CurrentDomain.SetShadowCopyFiles()
  376. [System.Reflection.Assembly]::LoadFrom((Join-Path $installPath 'lib\net40\EntityFramework.dll')) | Out-Null
  377. [System.Reflection.Assembly]::LoadFrom((Join-Path $installPath 'tools\EntityFramework.PowerShell.dll')) | Out-Null
  378. }
  379. function Build-Project($project)
  380. {
  381. $configuration = $DTE.Solution.SolutionBuild.ActiveConfiguration.Name
  382. $DTE.Solution.SolutionBuild.BuildProject($configuration, $project.UniqueName, $true)
  383. if ($DTE.Solution.SolutionBuild.LastBuildInfo)
  384. {
  385. throw 'The project ''' + $project.Name + ''' failed to build.'
  386. }
  387. }
  388. function Test-TypeInherits($type, $baseTypeName)
  389. {
  390. if ($type.FullName -eq $baseTypeName)
  391. {
  392. return $true
  393. }
  394. $baseType = $type.BaseType
  395. if ($baseType)
  396. {
  397. return Test-TypeInherits $baseType $baseTypeName
  398. }
  399. return $false
  400. }
  401. Export-ModuleMember @( 'Enable-Migrations', 'Add-Migration', 'Update-Database', 'Get-Migrations' ) -Variable 'InitialDatabase'