$ErrorActionPreference = 'Stop' # # Add-Migration # Register-TabExpansion Add-Migration @{ OutputDir = { <# Disabled. Otherwise, paths would be relative to the solution directory. #> } Context = { param($x) GetContextTypes $x.Project $x.StartupProject } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Adds a new migration. .DESCRIPTION Adds a new migration. .PARAMETER Name The name of the migration. .PARAMETER OutputDir The directory to put files in. Paths are relative to the project directory. Defaults to "Migrations". .PARAMETER Context The DbContext to use. .PARAMETER Project The project to use. .PARAMETER StartupProject The startup project to use. Defaults to the solution's startup project. .PARAMETER Namespace The namespace to use. Matches the directory by default. .PARAMETER Args Arguments passed to the application. .LINK Remove-Migration Update-Database about_EntityFrameworkCore #> function Add-Migration { [CmdletBinding(PositionalBinding = $false)] param( [Parameter(Position = 0, Mandatory = $true)] [string] $Name, [string] $OutputDir, [string] $Context, [string] $Project, [string] $StartupProject, [string] $Namespace, [string] $Args) WarnIfEF6 'Add-Migration' $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject $params = 'migrations', 'add', $Name, '--json' if ($OutputDir) { $params += '--output-dir', $OutputDir } if ($Namespace) { $params += '--namespace', $Namespace } $params += GetParams $Context # NB: -join is here to support ConvertFrom-Json on PowerShell 3.0 $result = (EF $dteProject $dteStartupProject $params $Args) -join "`n" | ConvertFrom-Json Write-Host 'To undo this action, use Remove-Migration.' if (!(IsCpsProject $dteProject) -or (GetCpsProperty $dteProject 'EnableDefaultItems') -ne 'true' -or (GetCpsProperty $dteProject 'EnableDefaultCompileItems') -ne 'true') { $dteProject.ProjectItems.AddFromFile($result.migrationFile) | Out-Null $dteProject.ProjectItems.AddFromFile($result.metadataFile) | Out-Null $dteProject.ProjectItems.AddFromFile($result.snapshotFile) | Out-Null } $DTE.ItemOperations.OpenFile($result.migrationFile) | Out-Null ShowConsole } # # Bundle-Migration # Register-TabExpansion Bundle-Migration @{ Context = { param($x) GetContextTypes $x.Project $x.StartupProject } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Creates an executable to update the database. .DESCRIPTION Creates an executable to update the database. .PARAMETER Output The path of executable file to create. .PARAMETER Force Overwrite existing files. .PARAMETER SelfContained Also bundle the .NET runtime so it doesn't need to be installed on the machine. .PARAMETER TargetRuntime The target runtime to bundle for. .PARAMETER Framework The target framework. Defaults to the first one in the project. .PARAMETER Context The DbContext to use. .PARAMETER Project The project to use. .PARAMETER StartupProject The startup project to use. Defaults to the solution's startup project. .PARAMETER Args Arguments passed to the application. .LINK Script-Migration Update-Database about_EntityFrameworkCore #> function Bundle-Migration { [CmdletBinding(PositionalBinding = $false)] param( [string] $Output, [switch] $Force, [switch] $SelfContained, [string] $TargetRuntime, [string] $Framework, [string] $Context, [string] $Project, [string] $StartupProject, [string] $Args) $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject if (!$Framework) { $Framework = GetProperty $dteStartupProject.Properties 'FriendlyTargetFramework' } $params = 'migrations', 'bundle', '--framework', $Framework if ($Output) { $params += '--output', $Output } if ($Force) { $params += '--force' } if ($SelfContained) { $params += '--self-contained' } if ($TargetRuntime) { $params += '--target-runtime', $TargetRuntime } $params += GetParams $Context EF $dteProject $dteStartupProject $params $Args } # # Drop-Database # Register-TabExpansion Drop-Database @{ Context = { param($x) GetContextTypes $x.Project $x.StartupProject } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Drops the database. .DESCRIPTION Drops the database. .PARAMETER Context The DbContext to use. .PARAMETER Project The project to use. .PARAMETER StartupProject The startup project to use. Defaults to the solution's startup project. .PARAMETER Args Arguments passed to the application. .LINK Update-Database about_EntityFrameworkCore #> function Drop-Database { [CmdletBinding(PositionalBinding = $false, SupportsShouldProcess = $true, ConfirmImpact = 'High')] param( [string] $Context, [string] $Project, [string] $StartupProject, [string] $Args) $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject $info = Get-DbContext -Context $Context -Project $Project -StartupProject $StartupProject if ($PSCmdlet.ShouldProcess("database '$($info.databaseName)' on server '$($info.dataSource)'")) { $params = 'database', 'drop', '--force' $params += GetParams $Context EF $dteProject $dteStartupProject $params $Args -skipBuild } } # # Enable-Migrations (Obsolete) # function Enable-Migrations { WarnIfEF6 'Enable-Migrations' Write-Warning 'Enable-Migrations is obsolete. Use Add-Migration to start using Migrations.' } # # Get-DbContext # Register-TabExpansion Get-DbContext @{ Context = { param($x) GetContextTypes $x.Project $x.StartupProject } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Lists and gets information about available DbContext types. .DESCRIPTION Lists and gets information about available DbContext types. .PARAMETER Context The DbContext to use. .PARAMETER Project The project to use. .PARAMETER StartupProject The startup project to use. Defaults to the solution's startup project. .PARAMETER Args Arguments passed to the application. .LINK about_EntityFrameworkCore #> function Get-DbContext { [CmdletBinding(PositionalBinding = $false)] param( [string] $Context, [string] $Project, [string] $StartupProject, [string] $Args) $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject if ($PSBoundParameters.ContainsKey('Context')) { $params = 'dbcontext', 'info', '--json' $params += GetParams $Context # NB: -join is here to support ConvertFrom-Json on PowerShell 3.0 return (EF $dteProject $dteStartupProject $params $Args) -join "`n" | ConvertFrom-Json } else { $params = 'dbcontext', 'list', '--json' # NB: -join is here to support ConvertFrom-Json on PowerShell 3.0 # NB: Write-Output is here to break the JSON array into individual items return (EF $dteProject $dteStartupProject $params $Args) -join "`n" | ConvertFrom-Json | Write-Output | Format-Table -Property safeName -HideTableHeaders } } # # Get-Migration # Register-TabExpansion Get-Migration @{ Context = { param($x) GetContextTypes $x.Project $x.StartupProject } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Lists available migrations. .DESCRIPTION Lists available migrations. .PARAMETER Connection The connection string to the database. Defaults to the one specified in AddDbContext or OnConfiguring. .PARAMETER NoConnect Don't connect to the database. .PARAMETER Context The DbContext to use. .PARAMETER Project The project to use. .PARAMETER StartupProject The startup project to use. Defaults to the solution's startup project. .PARAMETER Args Arguments passed to the application. .LINK Add-Migration Remove-Migration Update-Database about_EntityFrameworkCore #> function Get-Migration { [CmdletBinding(PositionalBinding = $false)] param( [string] $Connection, [switch] $NoConnect, [string] $Context, [string] $Project, [string] $StartupProject, [string] $Args) $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject $params = 'migrations', 'list', '--json' if ($Connection) { $params += '--connection', $Connection } if ($NoConnect) { $params += '--no-connect' } $params += GetParams $Context # NB: -join is here to support ConvertFrom-Json on PowerShell 3.0 # NB: Write-Output is here to break the JSON array into individual items return (EF $dteProject $dteStartupProject $params $Args) -join "`n" | ConvertFrom-Json | Write-Output } # # Remove-Migration # Register-TabExpansion Remove-Migration @{ Context = { param($x) GetContextTypes $x.Project $x.StartupProject } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Removes the last migration. .DESCRIPTION Removes the last migration. .PARAMETER Force Revert the migration if it has been applied to the database. .PARAMETER Context The DbContext to use. .PARAMETER Project The project to use. .PARAMETER StartupProject The startup project to use. Defaults to the solution's startup project. .PARAMETER Args Arguments passed to the application. .LINK Add-Migration Get-Migration about_EntityFrameworkCore #> function Remove-Migration { [CmdletBinding(PositionalBinding = $false)] param( [switch] $Force, [string] $Context, [string] $Project, [string] $StartupProject, [string] $Args) $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject $params = 'migrations', 'remove', '--json' if ($Force) { $params += '--force' } $params += GetParams $Context # NB: -join is here to support ConvertFrom-Json on PowerShell 3.0 $result = (EF $dteProject $dteStartupProject $params $Args) -join "`n" | ConvertFrom-Json if (!(IsCpsProject $dteProject) -or (GetCpsProperty $dteProject 'EnableDefaultItems') -ne 'true' -or (GetCpsProperty $dteProject 'EnableDefaultCompileItems') -ne 'true') { $files = $result.migrationFile, $result.metadataFile, $result.snapshotFile $files | ?{ $_ -ne $null } | %{ $projectItem = GetProjectItem $dteProject $_ if ($projectItem) { $projectItem.Remove() } } } } # # Optimize-DbContext # Register-TabExpansion Optimize-DbContext @{ Project = { GetProjects } StartupProject = { GetProjects } OutputDir = { <# Disabled. Otherwise, paths would be relative to the solution directory. #> } } <# .SYNOPSIS Generates a compiled version of the model used by the DbContext. .DESCRIPTION Generates a compiled version of the model used by the DbContext. .PARAMETER OutputDir The directory to put files in. Paths are relative to the project directory. .PARAMETER Namespace The namespace to use. Matches the directory by default. .PARAMETER Context The DbContext to use. .PARAMETER Project The project to use. .PARAMETER StartupProject The startup project to use. Defaults to the solution's startup project. .PARAMETER Args Arguments passed to the application. .LINK about_EntityFrameworkCore #> function Optimize-DbContext { [CmdletBinding(PositionalBinding = $false)] param( [string] $OutputDir, [string] $Namespace, [string] $Context, [string] $Project, [string] $StartupProject, [string] $Args) $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject $params = 'dbcontext', 'optimize' if ($OutputDir) { $params += '--output-dir', $OutputDir } if ($Namespace) { $params += '--namespace', $Namespace } if ($Context) { $params += '--context', $Context } EF $dteProject $dteStartupProject $params $Args } # # Scaffold-DbContext # Register-TabExpansion Scaffold-DbContext @{ Provider = { param($x) GetProviders $x.Project } Project = { GetProjects } StartupProject = { GetProjects } OutputDir = { <# Disabled. Otherwise, paths would be relative to the solution directory. #> } ContextDir = { <# Disabled. Otherwise, paths would be relative to the solution directory. #> } } <# .SYNOPSIS Scaffolds a DbContext and entity types for a database. .DESCRIPTION Scaffolds a DbContext and entity types for a database. .PARAMETER Connection The connection string to the database. .PARAMETER Provider The provider to use. (E.g. Microsoft.EntityFrameworkCore.SqlServer) .PARAMETER OutputDir The directory to put files in. Paths are relative to the project directory. .PARAMETER ContextDir The directory to put the DbContext file in. Paths are relative to the project directory. .PARAMETER Context The name of the DbContext. Defaults to the database name. .PARAMETER Schemas The schemas of tables and views to generate entity types for. All tables and views in the schemas will be included in the model, even if they are not explicitly included with the 'Tables' parameter. .PARAMETER Tables The tables and views to generate entity types for. Tables or views in a specific schema can be included using the 'schema.table' or 'schema.view' format. .PARAMETER DataAnnotations Use attributes to configure the model (where possible). If omitted, only the fluent API is used. .PARAMETER UseDatabaseNames Use table, view, sequence, and column names directly from the database. .PARAMETER Force Overwrite existing files. .PARAMETER NoOnConfiguring Don't generate DbContext.OnConfiguring. .PARAMETER Project The project to use. .PARAMETER StartupProject The startup project to use. Defaults to the solution's startup project. .PARAMETER Namespace The namespace to use. Matches the directory by default. .PARAMETER ContextNamespace The namespace of the DbContext class. Matches the directory by default. .PARAMETER NoPluralize Don't use the pluralizer. .PARAMETER Args Arguments passed to the application. .LINK about_EntityFrameworkCore #> function Scaffold-DbContext { [CmdletBinding(PositionalBinding = $false)] param( [Parameter(Position = 0, Mandatory = $true)] [string] $Connection, [Parameter(Position = 1, Mandatory = $true)] [string] $Provider, [string] $OutputDir, [string] $ContextDir, [string] $Context, [string[]] $Schemas = @(), [string[]] $Tables = @(), [switch] $DataAnnotations, [switch] $UseDatabaseNames, [switch] $Force, [switch] $NoOnConfiguring, [string] $Project, [string] $StartupProject, [string] $Namespace, [string] $ContextNamespace, [switch] $NoPluralize, [string] $Args) $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject $params = 'dbcontext', 'scaffold', $Connection, $Provider, '--json' if ($OutputDir) { $params += '--output-dir', $OutputDir } if ($ContextDir) { $params += '--context-dir', $ContextDir } if ($Context) { $params += '--context', $Context } if ($PSBoundParameters.ContainsKey('Namespace')) { $params += '--namespace', $Namespace } if ($PSBoundParameters.ContainsKey('ContextNamespace')) { $params += '--context-namespace', $ContextNamespace } $params += $Schemas | %{ '--schema', $_ } $params += $Tables | %{ '--table', $_ } if ($DataAnnotations) { $params += '--data-annotations' } if ($UseDatabaseNames) { $params += '--use-database-names' } if ($Force) { $params += '--force' } if ($NoOnConfiguring) { $params += '--no-onconfiguring' } if ($NoPluralize) { $params += '--no-pluralize' } # NB: -join is here to support ConvertFrom-Json on PowerShell 3.0 $result = (EF $dteProject $dteStartupProject $params $Args) -join "`n" | ConvertFrom-Json if (!(IsCpsProject $dteProject) -or (GetCpsProperty $dteProject 'EnableDefaultItems') -ne 'true' -or (GetCpsProperty $dteProject 'EnableDefaultCompileItems') -ne 'true') { $files = $result.entityTypeFiles + $result.contextFile $files | %{ $dteProject.ProjectItems.AddFromFile($_) | Out-Null } } $DTE.ItemOperations.OpenFile($result.contextFile) | Out-Null ShowConsole } # # Script-DbContext # Register-TabExpansion Script-DbContext @{ Context = { param($x) GetContextTypes $x.Project $x.StartupProject } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Generates a SQL script from the DbContext. Bypasses any migrations. .DESCRIPTION Generates a SQL script from the DbContext. Bypasses any migrations. .PARAMETER Output The file to write the result to. .PARAMETER Context The DbContext to use. .PARAMETER Project The project to use. .PARAMETER StartupProject The startup project to use. Defaults to the solution's startup project. .PARAMETER Args Arguments passed to the application. .LINK about_EntityFrameworkCore #> function Script-DbContext { [CmdletBinding(PositionalBinding = $false)] param( [string] $Output, [string] $Context, [string] $Project, [string] $StartupProject, [string] $Args) $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject if (!$Output) { $intermediatePath = GetIntermediatePath $dteProject if (![IO.Path]::IsPathRooted($intermediatePath)) { $projectDir = GetProperty $dteProject.Properties 'FullPath' $intermediatePath = [IO.Path]::GetFullPath((Join-Path $projectDir $intermediatePath)) } $scriptFileName = [IO.Path]::ChangeExtension([IO.Path]::GetRandomFileName(), '.sql') $Output = Join-Path $intermediatePath $scriptFileName } elseif (![IO.Path]::IsPathRooted($Output)) { $Output = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Output) } $params = 'dbcontext', 'script', '--output', $Output $params += GetParams $Context EF $dteProject $dteStartupProject $params $Args $DTE.ItemOperations.OpenFile($Output) | Out-Null ShowConsole } # # Script-Migration # Register-TabExpansion Script-Migration @{ From = { param($x) GetMigrations $x.Context $x.Project $x.StartupProject } To = { param($x) GetMigrations $x.Context $x.Project $x.StartupProject } Context = { param($x) GetContextTypes $x.Project $x.StartupProject } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Generates a SQL script from migrations. .DESCRIPTION Generates a SQL script from migrations. .PARAMETER From The starting migration. Defaults to '0' (the initial database). .PARAMETER To The target migration. Defaults to the last migration. .PARAMETER Idempotent Generate a script that can be used on a database at any migration. .PARAMETER NoTransactions Don't generate SQL transaction statements. .PARAMETER Output The file to write the result to. .PARAMETER Context The DbContext to use. .PARAMETER Project The project to use. .PARAMETER StartupProject The startup project to use. Defaults to the solution's startup project. .PARAMETER Args Arguments passed to the application. .LINK Update-Database Get-Migration about_EntityFrameworkCore #> function Script-Migration { [CmdletBinding(PositionalBinding = $false)] param( [Parameter(ParameterSetName = 'WithoutTo', Position = 0)] [Parameter(ParameterSetName = 'WithTo', Position = 0, Mandatory = $true)] [string] $From, [Parameter(ParameterSetName = 'WithTo', Position = 1, Mandatory = $true)] [string] $To, [switch] $Idempotent, [switch] $NoTransactions, [string] $Output, [string] $Context, [string] $Project, [string] $StartupProject, [string] $Args) $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject if (!$Output) { $intermediatePath = GetIntermediatePath $dteProject if (![IO.Path]::IsPathRooted($intermediatePath)) { $projectDir = GetProperty $dteProject.Properties 'FullPath' $intermediatePath = [IO.Path]::GetFullPath((Join-Path $projectDir $intermediatePath)) } $scriptFileName = [IO.Path]::ChangeExtension([IO.Path]::GetRandomFileName(), '.sql') $Output = Join-Path $intermediatePath $scriptFileName } elseif (![IO.Path]::IsPathRooted($Output)) { $Output = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Output) } $params = 'migrations', 'script', '--output', $Output if ($From) { $params += $From } if ($To) { $params += $To } if ($Idempotent) { $params += '--idempotent' } if ($NoTransactions) { $params += '--no-transactions' } $params += GetParams $Context EF $dteProject $dteStartupProject $params $Args $DTE.ItemOperations.OpenFile($Output) | Out-Null ShowConsole } # # Update-Database # Register-TabExpansion Update-Database @{ Migration = { param($x) GetMigrations $x.Context $x.Project $x.StartupProject } Context = { param($x) GetContextTypes $x.Project $x.StartupProject } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Updates the database to a specified migration. .DESCRIPTION Updates the database to a specified migration. .PARAMETER Migration The target migration. If '0', all migrations will be reverted. Defaults to the last migration. .PARAMETER Connection The connection string to the database. Defaults to the one specified in AddDbContext or OnConfiguring. .PARAMETER Context The DbContext to use. .PARAMETER Project The project to use. .PARAMETER StartupProject The startup project to use. Defaults to the solution's startup project. .PARAMETER Args Arguments passed to the application. .LINK Script-Migration about_EntityFrameworkCore #> function Update-Database { [CmdletBinding(PositionalBinding = $false)] param( [Parameter(Position = 0)] [string] $Migration, [string] $Connection, [string] $Context, [string] $Project, [string] $StartupProject, [string] $Args) WarnIfEF6 'Update-Database' $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject $params = 'database', 'update' if ($Migration) { $params += $Migration } if ($Connection) { $params += '--connection', $Connection } $params += GetParams $Context EF $dteProject $dteStartupProject $params $Args } # # (Private Helpers) # function GetProjects { return Get-Project -All | %{ $_.ProjectName } } function GetProviders($projectName) { if (!$projectName) { $projectName = (Get-Project).ProjectName } return Get-Package -ProjectName $projectName | %{ $_.Id } } function GetContextTypes($projectName, $startupProjectName) { $project = GetProject $projectName $startupProject = GetStartupProject $startupProjectName $project $params = 'dbcontext', 'list', '--json' # NB: -join is here to support ConvertFrom-Json on PowerShell 3.0 # NB: Write-Output is here to break the JSON array into individual items $result = (EF $project $startupProject $params $null -skipBuild) -join "`n" | ConvertFrom-Json | Write-Output return $result | %{ $_.safeName } } function GetMigrations($context, $projectName, $startupProjectName) { $project = GetProject $projectName $startupProject = GetStartupProject $startupProjectName $project $params = 'migrations', 'list', '--no-connect', '--json' $params += GetParams $context # NB: -join is here to support ConvertFrom-Json on PowerShell 3.0 # NB: Write-Output is here to break the JSON array into individual items $result = (EF $project $startupProject $params $null -skipBuild) -join "`n" | ConvertFrom-Json | Write-Output return $result | %{ $_.safeName } } function WarnIfEF6($cmdlet) { if (Get-Module 'EntityFramework6') { Write-Warning "Both Entity Framework Core and Entity Framework 6 are installed. The Entity Framework Core tools are running. Use 'EntityFramework6\$cmdlet' for Entity Framework 6." } elseif (Get-Module 'EntityFramework') { Write-Warning "Both Entity Framework Core and Entity Framework 6 are installed. The Entity Framework Core tools are running. Use 'EntityFramework\$cmdlet' for Entity Framework 6." } } function GetProject($projectName) { if (!$projectName) { return Get-Project } return Get-Project $projectName } function GetStartupProject($name, $fallbackProject) { if ($name) { return Get-Project $name } $startupProjectPaths = $DTE.Solution.SolutionBuild.StartupProjects if ($startupProjectPaths) { if ($startupProjectPaths.Length -eq 1) { $startupProjectPath = $startupProjectPaths[0] if (![IO.Path]::IsPathRooted($startupProjectPath)) { $solutionPath = Split-Path (GetProperty $DTE.Solution.Properties 'Path') $startupProjectPath = [IO.Path]::GetFullPath((Join-Path $solutionPath $startupProjectPath)) } $startupProject = GetSolutionProjects | ?{ try { $fullName = $_.FullName } catch [NotImplementedException] { return $false } if ($fullName -and $fullName.EndsWith('\')) { $fullName = $fullName.Substring(0, $fullName.Length - 1) } return $fullName -eq $startupProjectPath } if ($startupProject) { return $startupProject } Write-Warning "Unable to resolve startup project '$startupProjectPath'." } else { Write-Warning 'Multiple startup projects set.' } } else { Write-Warning 'No startup project set.' } Write-Warning "Using project '$($fallbackProject.ProjectName)' as the startup project." return $fallbackProject } function GetSolutionProjects() { $projects = New-Object 'System.Collections.Stack' $DTE.Solution.Projects | %{ $projects.Push($_) } while ($projects.Count) { $project = $projects.Pop(); <# yield return #> $project if ($project.ProjectItems) { $project.ProjectItems | ?{ $_.SubProject } | %{ $projects.Push($_.SubProject) } } } } function GetParams($context) { $params = @() if ($context) { $params += '--context', $context } return $params } function ShowConsole { $componentModel = Get-VSComponentModel $powerConsoleWindow = $componentModel.GetService([NuGetConsole.IPowerConsoleWindow]) $powerConsoleWindow.Show() } function WriteErrorLine($message) { try { # Call the internal API NuGet uses to display errors $componentModel = Get-VSComponentModel $powerConsoleWindow = $componentModel.GetService([NuGetConsole.IPowerConsoleWindow]) $bindingFlags = [Reflection.BindingFlags]::Instance -bor [Reflection.BindingFlags]::NonPublic $activeHostInfo = $powerConsoleWindow.GetType().GetProperty('ActiveHostInfo', $bindingFlags).GetValue($powerConsoleWindow) $internalHost = $activeHostInfo.WpfConsole.Host $reportErrorMethod = $internalHost.GetType().GetMethod('ReportError', $bindingFlags, $null, [Exception], $null) $exception = New-Object Exception $message $reportErrorMethod.Invoke($internalHost, $exception) } catch { Write-Host $message -ForegroundColor DarkRed } } function EF($project, $startupProject, $params, $applicationArgs, [switch] $skipBuild) { if (IsDocker $startupProject) { throw "Startup project '$($startupProject.ProjectName)' is a Docker project. Select an ASP.NET Core Web " + 'Application as your startup project and try again.' } if (IsUWP $startupProject) { throw "Startup project '$($startupProject.ProjectName)' is a Universal Windows Platform app. This version of " + 'the Entity Framework Core Package Manager Console Tools doesn''t support this type of project. For more ' + 'information on using the EF Core Tools with UWP projects, see ' + 'https://go.microsoft.com/fwlink/?linkid=858496' } Write-Verbose "Using project '$($project.ProjectName)'." Write-Verbose "Using startup project '$($startupProject.ProjectName)'." if (!$skipBuild) { Write-Host 'Build started...' # TODO: Only build startup project. Don't use BuildProject, you can't specify platform $solutionBuild = $DTE.Solution.SolutionBuild $solutionBuild.Build(<# WaitForBuildToFinish: #> $true) if ($solutionBuild.LastBuildInfo) { throw 'Build failed.' } Write-Host 'Build succeeded.' } $activeConfiguration = $startupProject.ConfigurationManager.ActiveConfiguration if ($activeConfiguration -eq $null) { throw "Unable to read project configuration settings of project '$($startupProject.ProjectName)' for the " + 'active solution configuration. Try closing Package Manager Console and restarting Visual Studio. If the ' + 'problem persists, use Help > Send Feedback > Report a Problem.' } $startupProjectDir = GetProperty $startupProject.Properties 'FullPath' $outputPath = GetProperty $activeConfiguration.Properties 'OutputPath' $targetDir = [IO.Path]::GetFullPath([IO.Path]::Combine($startupProjectDir, $outputPath)) $startupTargetFileName = GetProperty $startupProject.Properties 'OutputFileName' $startupTargetPath = Join-Path $targetDir $startupTargetFileName $targetFrameworkMoniker = GetProperty $startupProject.Properties 'TargetFrameworkMoniker' $frameworkName = New-Object 'System.Runtime.Versioning.FrameworkName' $targetFrameworkMoniker $targetFramework = $frameworkName.Identifier if ($targetFramework -in '.NETFramework') { $platformTarget = GetPlatformTarget $startupProject if ($platformTarget -eq 'x86') { $exePath = Join-Path $PSScriptRoot 'net461\win-x86\ef.exe' } elseif ($platformTarget -eq 'ARM64') { $exePath = Join-Path $PSScriptRoot 'net461\win-arm64\ef.exe' } elseif ($platformTarget -in 'AnyCPU', 'x64') { $exePath = Join-Path $PSScriptRoot 'net461\any\ef.exe' } else { throw "Startup project '$($startupProject.ProjectName)' has an active platform of '$platformTarget'. Select " + 'a different platform and try again.' } } elseif ($targetFramework -eq '.NETCoreApp') { $targetPlatformIdentifier = GetCpsProperty $startupProject 'TargetPlatformIdentifier' if ($targetPlatformIdentifier -and $targetPlatformIdentifier -ne 'Windows') { throw "Startup project '$($startupProject.ProjectName)' targets platform '$targetPlatformIdentifier'. The Entity Framework " + 'Core Package Manager Console Tools don''t support this platform. See https://aka.ms/efcore-docs-pmc-tfms for more ' + 'information.' } $exePath = (Get-Command 'dotnet').Path $startupTargetName = GetProperty $startupProject.Properties 'AssemblyName' $depsFile = Join-Path $targetDir ($startupTargetName + '.deps.json') $projectAssetsFile = GetCpsProperty $startupProject 'ProjectAssetsFile' $runtimeConfig = Join-Path $targetDir ($startupTargetName + '.runtimeconfig.json') $runtimeFrameworkVersion = GetCpsProperty $startupProject 'RuntimeFrameworkVersion' $efPath = Join-Path $PSScriptRoot 'netcoreapp2.0\any\ef.dll' $dotnetParams = 'exec', '--depsfile', $depsFile if ($projectAssetsFile) { # NB: Don't use Get-Content. It doesn't handle UTF-8 without a signature # NB: Don't use ReadAllLines. ConvertFrom-Json won't work on PowerShell 3.0 $projectAssets = [IO.File]::ReadAllText($projectAssetsFile) | ConvertFrom-Json $projectAssets.packageFolders.psobject.Properties.Name | %{ $dotnetParams += '--additionalprobingpath', $_.TrimEnd('\') } } if ([IO.File]::Exists($runtimeConfig)) { $dotnetParams += '--runtimeconfig', $runtimeConfig } elseif ($runtimeFrameworkVersion) { $dotnetParams += '--fx-version', $runtimeFrameworkVersion } $dotnetParams += $efPath $params = $dotnetParams + $params } elseif ($targetFramework -eq '.NETStandard') { throw "Startup project '$($startupProject.ProjectName)' targets framework '.NETStandard'. There is no " + 'runtime associated with this framework, and projects targeting it cannot be executed directly. To use ' + 'the Entity Framework Core Package Manager Console Tools with this project, add an executable project ' + 'targeting .NET Framework or .NET Core that references this project, and set it as the startup project; ' + 'or, update this project to cross-target .NET Framework or .NET Core. For more information on using the ' + 'EF Core Tools with .NET Standard projects, see https://go.microsoft.com/fwlink/?linkid=2034705' } else { throw "Startup project '$($startupProject.ProjectName)' targets framework '$targetFramework'. The Entity Framework Core Package " + 'Manager Console Tools don''t support this framework. See https://aka.ms/efcore-docs-pmc-tfms for more information and examples.' } $projectDir = GetProperty $project.Properties 'FullPath' $targetFileName = GetProperty $project.Properties 'OutputFileName' $targetPath = Join-Path $targetDir $targetFileName $rootNamespace = GetProperty $project.Properties 'RootNamespace' $language = GetLanguage $project $nullable = GetNullable $project $params += '--verbose', '--no-color', '--prefix-output', '--assembly', $targetPath, '--project', $project.FullName, '--startup-assembly', $startupTargetPath, '--startup-project', $startupProject.FullName, '--project-dir', $projectDir, '--language', $language, '--configuration', $activeConfiguration.ConfigurationName, '--working-dir', $PWD.Path if (IsWeb $startupProject) { $params += '--data-dir', (Join-Path $startupProjectDir 'App_Data') } if ($rootNamespace -ne $null) { $params += '--root-namespace', $rootNamespace } if ($nullable -in 'enable', 'annotations') { $params += '--nullable' } $arguments = ToArguments $params if ($applicationArgs) { $arguments += ' -- ' $arguments += $applicationArgs } $startInfo = New-Object 'System.Diagnostics.ProcessStartInfo' -Property @{ FileName = $exePath; Arguments = $arguments; UseShellExecute = $false; CreateNoWindow = $true; RedirectStandardOutput = $true; StandardOutputEncoding = [Text.Encoding]::UTF8; RedirectStandardError = $true; WorkingDirectory = $startupProjectDir; } Write-Verbose "$exePath $arguments" $process = [Diagnostics.Process]::Start($startInfo) while (($line = $process.StandardOutput.ReadLine()) -ne $null) { $level = $null $text = $null $parts = $line.Split(':', 2) if ($parts.Length -eq 2) { $level = $parts[0] $i = 0 $count = 8 - $level.Length while ($i -lt $count -and $parts[1][$i] -eq ' ') { $i++ } $text = $parts[1].Substring($i) } switch ($level) { 'error' { WriteErrorLine $text } 'warn' { Write-Warning $text } 'info' { Write-Host $text } 'data' { Write-Output $text } 'verbose' { Write-Verbose $text } default { Write-Host $line } } } $process.WaitForExit() if ($process.ExitCode) { while (($line = $process.StandardError.ReadLine()) -ne $null) { WriteErrorLine $line } exit } } function IsDocker($project) { return $project.Kind -eq '{E53339B2-1760-4266-BCC7-CA923CBCF16C}' } function IsCpsProject($project) { $hierarchy = GetVsHierarchy $project $isCapabilityMatch = [Microsoft.VisualStudio.Shell.PackageUtilities].GetMethod( 'IsCapabilityMatch', [type[]]([Microsoft.VisualStudio.Shell.Interop.IVsHierarchy], [string])) return $isCapabilityMatch.Invoke($null, ($hierarchy, 'CPS')) } function IsWeb($project) { $types = GetProjectTypes $project return $types -contains '{349C5851-65DF-11DA-9384-00065B846F21}' } function IsUWP($project) { $types = GetProjectTypes $project return $types -contains '{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A}' } function GetIntermediatePath($project) { $intermediatePath = GetProperty $project.ConfigurationManager.ActiveConfiguration.Properties 'IntermediatePath' if ($intermediatePath) { return $intermediatePath } return GetMSBuildProperty $project 'IntermediateOutputPath' } function GetPlatformTarget($project) { if (IsCpsProject $project) { $platformTarget = GetCpsProperty $project 'PlatformTarget' if ($platformTarget) { return $platformTarget } return GetCpsProperty $project 'Platform' } $platformTarget = GetProperty $project.ConfigurationManager.ActiveConfiguration.Properties 'PlatformTarget' if ($platformTarget) { return $platformTarget } # NB: For classic F# projects $platformTarget = GetMSBuildProperty $project 'PlatformTarget' if ($platformTarget) { return $platformTarget } return 'AnyCPU' } function GetLanguage($project) { if (IsCpsProject $project) { return GetCpsProperty $project 'Language' } return GetMSBuildProperty $project 'Language' } function GetNullable($project) { if (IsCpsProject $project) { return GetCpsProperty $project 'Nullable' } return GetMSBuildProperty $project 'Nullable' } function GetVsHierarchy($project) { $solution = Get-VSService 'Microsoft.VisualStudio.Shell.Interop.SVsSolution' 'Microsoft.VisualStudio.Shell.Interop.IVsSolution' $hierarchy = $null $hr = $solution.GetProjectOfUniqueName($project.UniqueName, [ref] $hierarchy) [Runtime.InteropServices.Marshal]::ThrowExceptionForHR($hr) return $hierarchy } function GetProjectTypes($project) { $hierarchy = GetVsHierarchy $project $aggregatableProject = Get-Interface $hierarchy 'Microsoft.VisualStudio.Shell.Interop.IVsAggregatableProject' if (!$aggregatableProject) { return $project.Kind } $projectTypeGuidsString = $null $hr = $aggregatableProject.GetAggregateProjectTypeGuids([ref] $projectTypeGuidsString) [Runtime.InteropServices.Marshal]::ThrowExceptionForHR($hr) return $projectTypeGuidsString.Split(';') } function GetProperty($properties, $propertyName) { try { return $properties.Item($propertyName).Value } catch { return $null } } function GetCpsProperty($project, $propertyName) { $browseObjectContext = Get-Interface $project 'Microsoft.VisualStudio.ProjectSystem.Properties.IVsBrowseObjectContext' $unconfiguredProject = $browseObjectContext.UnconfiguredProject $configuredProject = $unconfiguredProject.GetSuggestedConfiguredProjectAsync().Result $properties = $configuredProject.Services.ProjectPropertiesProvider.GetCommonProperties() return $properties.GetEvaluatedPropertyValueAsync($propertyName).Result } function GetMSBuildProperty($project, $propertyName) { $msbuildProject = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.LoadedProjects | ? FullPath -eq $project.FullName return $msbuildProject.GetProperty($propertyName).EvaluatedValue } function GetProjectItem($project, $path) { $fullPath = GetProperty $project.Properties 'FullPath' if ([IO.Path]::IsPathRooted($path)) { $path = $path.Substring($fullPath.Length) } $itemDirectory = (Split-Path $path -Parent) $projectItems = $project.ProjectItems if ($itemDirectory) { $directories = $itemDirectory.Split('\') $directories | %{ if ($projectItems) { $projectItems = $projectItems.Item($_).ProjectItems } } } if (!$projectItems) { return $null } $itemName = Split-Path $path -Leaf try { return $projectItems.Item($itemName) } catch [Exception] { } return $null } function ToArguments($params) { $arguments = '' for ($i = 0; $i -lt $params.Length; $i++) { if ($i) { $arguments += ' ' } if ($params[$i] -eq '') { $arguments += '""' continue } if (!$params[$i].Contains(' ')) { $arguments += $params[$i] continue } $arguments += '"' $pendingBackslashs = 0 for ($j = 0; $j -lt $params[$i].Length; $j++) { switch ($params[$i][$j]) { '"' { if ($pendingBackslashs) { $arguments += '\' * $pendingBackslashs * 2 $pendingBackslashs = 0 } $arguments += '\"' } '\' { $pendingBackslashs++ } default { if ($pendingBackslashs) { if ($pendingBackslashs -eq 1) { $arguments += '\' } else { $arguments += '\' * $pendingBackslashs * 2 } $pendingBackslashs = 0 } $arguments += $params[$i][$j] } } } if ($pendingBackslashs) { $arguments += '\' * $pendingBackslashs * 2 } $arguments += '"' } return $arguments } # SIG # Begin signature block # MIInwQYJKoZIhvcNAQcCoIInsjCCJ64CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBWmmsHeCjsweD0 # BEmQC8qZ1slDmyde5Q6xxpAF5R2li6CCDXYwggX0MIID3KADAgECAhMzAAADrzBA # DkyjTQVBAAAAAAOvMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwOTAwWhcNMjQxMTE0MTkwOTAwWjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDOS8s1ra6f0YGtg0OhEaQa/t3Q+q1MEHhWJhqQVuO5amYXQpy8MDPNoJYk+FWA # hePP5LxwcSge5aen+f5Q6WNPd6EDxGzotvVpNi5ve0H97S3F7C/axDfKxyNh21MG # 0W8Sb0vxi/vorcLHOL9i+t2D6yvvDzLlEefUCbQV/zGCBjXGlYJcUj6RAzXyeNAN # xSpKXAGd7Fh+ocGHPPphcD9LQTOJgG7Y7aYztHqBLJiQQ4eAgZNU4ac6+8LnEGAL # go1ydC5BJEuJQjYKbNTy959HrKSu7LO3Ws0w8jw6pYdC1IMpdTkk2puTgY2PDNzB # tLM4evG7FYer3WX+8t1UMYNTAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQURxxxNPIEPGSO8kqz+bgCAQWGXsEw # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMTgyNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAISxFt/zR2frTFPB45Yd # mhZpB2nNJoOoi+qlgcTlnO4QwlYN1w/vYwbDy/oFJolD5r6FMJd0RGcgEM8q9TgQ # 2OC7gQEmhweVJ7yuKJlQBH7P7Pg5RiqgV3cSonJ+OM4kFHbP3gPLiyzssSQdRuPY # 1mIWoGg9i7Y4ZC8ST7WhpSyc0pns2XsUe1XsIjaUcGu7zd7gg97eCUiLRdVklPmp # XobH9CEAWakRUGNICYN2AgjhRTC4j3KJfqMkU04R6Toyh4/Toswm1uoDcGr5laYn # TfcX3u5WnJqJLhuPe8Uj9kGAOcyo0O1mNwDa+LhFEzB6CB32+wfJMumfr6degvLT # e8x55urQLeTjimBQgS49BSUkhFN7ois3cZyNpnrMca5AZaC7pLI72vuqSsSlLalG # OcZmPHZGYJqZ0BacN274OZ80Q8B11iNokns9Od348bMb5Z4fihxaBWebl8kWEi2O # PvQImOAeq3nt7UWJBzJYLAGEpfasaA3ZQgIcEXdD+uwo6ymMzDY6UamFOfYqYWXk # ntxDGu7ngD2ugKUuccYKJJRiiz+LAUcj90BVcSHRLQop9N8zoALr/1sJuwPrVAtx # HNEgSW+AKBqIxYWM4Ev32l6agSUAezLMbq5f3d8x9qzT031jMDT+sUAoCw0M5wVt # CUQcqINPuYjbS1WgJyZIiEkBMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCGaEwghmdAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIIYxW+S/vEfND4bbvBzJYtpx # ofWL0BOrUgI6GdZikmgLMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAPUowbR+/x2LcHlavld+9+qimq/7y7g7sovlO6O72OxYoruRGMbBC10AB # mN5CCMk9QptF5/IMIsiAnBwwPh5X+FU788iGGlKN9F+s24p09e+KS12cozLOvnZa # y9gOvhdkJzIOfG2LVXFigygXrS5XBqpq3Abg1whVLijMF14sNt+AjuMxEvsSapnq # 0nI/7HJsDRV4RVTKEM2YCZnkEY3H0YpERnk/WNUaHEIPsLNhZzV2rAaEuQwh5mJI # T8VSLSNznPopmjPsW3krLSeiyRd1x3TlBD6mMVaVjnGRJdhlYm4zpa0Rj16l6dAb # MG7844l7pTextJf+DCx+8KkvaTIInKGCFyswghcnBgorBgEEAYI3AwMBMYIXFzCC # FxMGCSqGSIb3DQEHAqCCFwQwghcAAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq # hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCDUZ2+QHmqzI7EmiAoI3QCKuG+aQuv4ZTIk1B3nzIV2rwIGZh/+Rayb # GBMyMDI0MDQxNzE5MDQwMy43MDhaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl # bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO # Ojg2REYtNEJCQy05MzM1MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT # ZXJ2aWNloIIRejCCBycwggUPoAMCAQICEzMAAAHdXVcdldStqhsAAQAAAd0wDQYJ # KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjMx # MDEyMTkwNzA5WhcNMjUwMTEwMTkwNzA5WjCB0jELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl # cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4NkRGLTRC # QkMtOTMzNTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKhOA5RE6i53nHURH4lnfKLp # +9JvipuTtctairCxMUSrPSy5CWK2DtriQP+T52HXbN2g7AktQ1pQZbTDGFzK6d03 # vYYNrCPuJK+PRsP2FPVDjBXy5mrLRFzIHHLaiAaobE5vFJuoxZ0ZWdKMCs8acjhH # UmfaY+79/CR7uN+B4+xjJqwvdpU/mp0mAq3earyH+AKmv6lkrQN8zgrcbCgHwsqv # vqT6lEFqYpi7uKn7MAYbSeLe0pMdatV5EW6NVnXMYOTRKuGPfyfBKdShualLo88k # G7qa2mbA5l77+X06JAesMkoyYr4/9CgDFjHUpcHSODujlFBKMi168zRdLerdpW0b # BX9EDux2zBMMaEK8NyxawCEuAq7++7ktFAbl3hUKtuzYC1FUZuUl2Bq6U17S4CKs # qR3itLT9qNcb2pAJ4jrIDdll5Tgoqef5gpv+YcvBM834bXFNwytd3ujDD24P9Dd8 # xfVJvumjsBQQkK5T/qy3HrQJ8ud1nHSvtFVi5Sa/ubGuYEpS8gF6GDWN5/KbveFk # dsoTVIPo8pkWhjPs0Q7nA5+uBxQB4zljEjKz5WW7BA4wpmFm24fhBmRjV4Nbp+n7 # 8cgAjvDSfTlA6DYBcv2kx1JH2dIhaRnSeOXePT6hMF0Il598LMu0rw35ViUWcAQk # UNUTxRnqGFxz5w+ZusMDAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUbqL1toyPUdpF # yyHSDKWj0I4lw/EwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD # VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j # cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG # CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw # MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD # CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAC5U2bINLgXIHWbM # cqVuf9jkUT/K8zyLBvu5h8JrqYR2z/eaO2yo1Ooc9Shyvxbe9GZDu7kkUzxSyJ1I # ZksZZw6FDq6yZNT3PEjAEnREpRBL8S+mbXg+O4VLS0LSmb8XIZiLsaqZ0fDEcv3H # eA+/y/qKnCQWkXghpaEMwGMQzRkhGwcGdXr1zGpQ7HTxvfu57xFxZX1MkKnWFENJ # 6urd+4teUgXj0ngIOx//l3XMK3Ht8T2+zvGJNAF+5/5qBk7nr079zICbFXvxtidN # N5eoXdW+9rAIkS+UGD19AZdBrtt6dZ+OdAquBiDkYQ5kVfUMKS31yHQOGgmFxuCO # zTpWHalrqpdIllsy8KNsj5U9sONiWAd9PNlyEHHbQZDmi9/BNlOYyTt0YehLbDov # mZUNazk79Od/A917mqCdTqrExwBGUPbMP+/vdYUqaJspupBnUtjOf/76DAhVy8e/ # e6zR98PkplmliO2brL3Q3rD6+ZCVdrGM9Rm6hUDBBkvYh+YjmGdcQ5HB6WT9Rec8 # +qDHmbhLhX4Zdaard5/OXeLbgx2f7L4QQQj3KgqjqDOWInVhNE1gYtTWLHe4882d # /k7Lui0K1g8EZrKD7maOrsJLKPKlegceJ9FCqY1sDUKUhRa0EHUW+ZkKLlohKrS7 # FwjdrINWkPBgbQznCjdE2m47QjTbMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ # mQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNh # dGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1 # WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEB # BQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjK # NVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhg # fWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJp # rx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/d # vI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka9 # 7aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKR # Hh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9itu # qBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyO # ArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItb # oKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6 # bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6t # AgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQW # BBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacb # UzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku # aHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA # QwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2 # VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwu # bWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEw # LTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt # MjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/q # XBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6 # U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVt # I1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis # 9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTp # kbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0 # sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138e # W0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJ # sWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7 # Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0 # dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQ # tB1VM1izoXBm8qGCAtYwggI/AgEBMIIBAKGB2KSB1TCB0jELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxh # bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4 # NkRGLTRCQkMtOTMzNTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUANiNHGWXbNaDPxnyiDbEOciSjFhCggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOnKfMMwIhgPMjAyNDA0MTgwMDUyMTlaGA8yMDI0MDQxOTAwNTIxOVowdjA8Bgor # BgEEAYRZCgQBMS4wLDAKAgUA6cp8wwIBADAJAgEAAgEIAgH/MAcCAQACAhFJMAoC # BQDpy85DAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEA # AgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAAV2Fv1lnSLXC29YS # Hqf1bhNaFT9o8mdCzq3Ik7pLud0CxNtdZYTd1zsBLRn11liBrRwhD1qWe98N3nEr # Od3xfjwzGW3P4tUai1qfGI4M/RLUv6Cnzj2ZjoR10zigXWjLjcnwThqCbjhj3C4l # 2f/uLdrHYkiAxyBe/mFmcDuArN4xggQNMIIECQIBATCBkzB8MQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt # ZS1TdGFtcCBQQ0EgMjAxMAITMwAAAd1dVx2V1K2qGwABAAAB3TANBglghkgBZQME # AgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJ # BDEiBCBBThnguFtWod4DUULff5UlNzNFvq9vCO/hQV/qo7VM2DCB+gYLKoZIhvcN # AQkQAi8xgeowgecwgeQwgb0EIGH/Di2aZaxPeJmce0fRWTftQI3TaVHFj5GI43rA # MWNmMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAHd # XVcdldStqhsAAQAAAd0wIgQg0WpOHqrkOBosJxV+VkOROLR9zQ+nxM799oXSXJkI # oFkwDQYJKoZIhvcNAQELBQAEggIAa3ec8s6JVHUiPU6ps2dt+TbhyGKVfL4UFe/W # CGjQMAMYb/PivBdWA5/+KdSK84yAVwRhvVDb9zKnCNpi8sErJIU2JaJocAS6FtCa # PnvQak+Wn6JxEZxhdV3copZQNN08ianPXaRpLvwe3moNoVcBhSRtFSot6cofenAv # 65i19mJVeey4yg64jtTHpQ5ztEEB8/75RD0PFmwmX4+S4MfT9kJ6av9rmOa5iFyj # levHh+uMChq3CW/DcPXIKdz+QzuA/x3oHzfSAz2lvJL/Tz1qYEz3ZmIuOTpa/C3e # ZzXNSuu4LW62s70GaI0sY3a31ni8DKzp3IHRuB+ajE9P6qI+bfuIVr4nJKvgBpud # rg61MTzf7rPjxzYMFgPHwLZJcC0S3Kqjl572roT+Ohi8TPilklkW3jZ9DDgcU64a # xfsLOtfs4/+KqLjcoi6FQz33wzrv1iV2gxmA3ZX8WatOsFIyMM/3iGEcJavwAb8D # fUSEbGyQwFnRs9/lxlAmyyODukeVMfuI4+15tBtI8ZQtpcRHgFv053GQ8KXqsFr2 # m/ZNVhg0rzfF5R3ILVkA/LhokmuK6lsHONH8OJlTrNTOHQHNJMWHM2WxhruUursn # 7V7mvIAfIQKOZpg1TSDBudfYubYqkstOLqe4Eobz4bbPc3CB3Wg0qvZvNYWD0vco # owrSmfI= # SIG # End signature block