$ErrorActionPreference = 'Stop' $EFDefaultParameterValues = @{ ProjectName = '' ContextTypeName = '' } # # Use-DbContext # Register-TabExpansion Use-DbContext @{ Context = { param ($tabExpansionContext) GetContextTypes $tabExpansionContext.Project $tabExpansionContext.StartupProject $tabExpansionContext.Environment } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Sets the default DbContext to use. .DESCRIPTION Sets the default DbContext to use. .PARAMETER Context Specifies the DbContext to use. .PARAMETER Project Specifies the project to use. If omitted, the default project is used. .PARAMETER StartupProject Specifies the startup project to use. If omitted, the solution's startup project is used. .PARAMETER Environment Specifies the environment to use. If omitted, "Development" is used. .LINK about_EntityFramework #> function Use-DbContext { [CmdletBinding(PositionalBinding = $false)] param ([Parameter(Position = 0, Mandatory = $true)] [string] $Context, [string] $Project, [string] $StartupProject, [string] $Environment) $dteProject = GetProject $Project $dteStartupProject = GetStartupProject $StartupProject $dteProject $contextTypeName = InvokeOperation $dteStartupProject $Environment $dteProject GetContextType @{ name = $Context } $EFDefaultParameterValues.ContextTypeName = $contextTypeName $EFDefaultParameterValues.ProjectName = $dteProject.ProjectName } # # Add-Migration # Register-TabExpansion Add-Migration @{ Context = { param ($tabExpansionContext) GetContextTypes $tabExpansionContext.Project $tabExpansionContext.StartupProject $tabExpansionContext.Environment } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Adds a new migration. .DESCRIPTION Adds a new migration. .PARAMETER Name Specifies the name of the migration. .PARAMETER OutputDir The directory (and sub-namespace) to use. If omitted, "Migrations" is used. .PARAMETER Context Specifies the DbContext to use. If omitted, the default DbContext is used. .PARAMETER Project Specifies the project to use. If omitted, the default project is used. .PARAMETER StartupProject Specifies the startup project to use. If omitted, the solution's startup project is used. .PARAMETER Environment Specifies the environment to use. If omitted, "Development" is used. .LINK Remove-Migration Update-Database about_EntityFramework #> function Add-Migration { [CmdletBinding(PositionalBinding = $false)] param ( [Parameter(Position = 0, Mandatory = $true)] [string] $Name, [string] $OutputDir, [string] $Context, [string] $Project, [string] $StartupProject, [string] $Environment) $values = ProcessCommonParameters $StartupProject $Project $Context $dteStartupProject = $values.StartupProject $dteProject = $values.Project $contextTypeName = $values.ContextTypeName $artifacts = InvokeOperation $dteStartupProject $Environment $dteProject AddMigration @{ name = $Name outputDir = $OutputDir contextType = $contextTypeName } $artifacts | %{ $dteProject.ProjectItems.AddFromFile($_) | Out-Null } $DTE.ItemOperations.OpenFile($artifacts[0]) | Out-Null ShowConsole Write-Output 'To undo this action, use Remove-Migration.' } # # Update-Database # Register-TabExpansion Update-Database @{ Migration = { param ($tabExpansionContext) GetMigrations $tabExpansionContext.Context $tabExpansionContext.Project $tabExpansionContext.StartupProject $tabExpansionContext.Environment } Context = { param ($tabExpansionContext) GetContextTypes $tabExpansionContext.Project $tabExpansionContext.StartupProject $tabExpansionContext.Environment } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Updates the database to a specified migration. .DESCRIPTION Updates the database to a specified migration. .PARAMETER Migration Specifies the target migration. If '0', all migrations will be reverted. If omitted, all pending migrations will be applied. .PARAMETER Context Specifies the DbContext to use. If omitted, the default DbContext is used. .PARAMETER Project Specifies the project to use. If omitted, the default project is used. .PARAMETER StartupProject Specifies the startup project to use. If omitted, the solution's startup project is used. .PARAMETER Environment Specifies the environment to use. If omitted, "Development" is used. .LINK Script-Migration about_EntityFramework #> function Update-Database { [CmdletBinding(PositionalBinding = $false)] param ( [Parameter(Position = 0)] [string] $Migration, [string] $Context, [string] $Project, [string] $StartupProject, [string] $Environment) $values = ProcessCommonParameters $StartupProject $Project $Context $dteStartupProject = $values.StartupProject $dteProject = $values.Project $contextTypeName = $values.ContextTypeName $targetFrameworkMoniker = GetProperty $dteProject.Properties TargetFrameworkMoniker $frameworkName = New-Object System.Runtime.Versioning.FrameworkName $targetFrameworkMoniker if ($frameworkName.Identifier -eq '.NETCore') { throw 'Update-Database should not be used with Universal Windows apps. Instead, call DbContext.Database.Migrate() at runtime.' } InvokeOperation $dteStartupProject $Environment $dteProject UpdateDatabase @{ targetMigration = $Migration contextType = $contextTypeName } } # # Apply-Migration (Obsolete) # function Apply-Migration { # TODO: Remove before RTM throw 'Apply-Migration has been removed. Use Update-Database instead.' } # # Script-Migration # Register-TabExpansion Script-Migration @{ From = { param ($tabExpansionContext) GetMigrations $tabExpansionContext.Context $tabExpansionContext.Project $tabExpansionContext.StartupProject $tabExpansionContext.Environment } To = { param ($tabExpansionContext) GetMigrations $tabExpansionContext.Context $tabExpansionContext.Project $tabExpansionContext.StartupProject $tabExpansionContext.Environment } Context = { param ($tabExpansionContext) GetContextTypes $tabExpansionContext.Project $tabExpansionContext.StartupProject $tabExpansionContext.Environment } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Generates a SQL script from migrations. .DESCRIPTION Generates a SQL script from migrations. .PARAMETER From Specifies the starting migration. If omitted, '0' (the initial database) is used. .PARAMETER To Specifies the ending migration. If omitted, the last migration is used. .PARAMETER Idempotent Generates an idempotent script that can used on a database at any migration. .PARAMETER Context Specifies the DbContext to use. If omitted, the default DbContext is used. .PARAMETER Project Specifies the project to use. If omitted, the default project is used. .PARAMETER StartupProject Specifies the startup project to use. If omitted, the solution's startup project is used. .PARAMETER Environment Specifies the environment to use. If omitted, "Development" is used. .LINK Update-Database about_EntityFramework #> function Script-Migration { [CmdletBinding(PositionalBinding = $false)] param ( [Parameter(ParameterSetName = 'WithoutTo')] [Parameter(ParameterSetName = 'WithTo', Mandatory = $true)] [string] $From, [Parameter(ParameterSetName = 'WithTo', Mandatory = $true)] [string] $To, [switch] $Idempotent, [string] $Context, [string] $Project, [string] $StartupProject, [string] $Environment) $values = ProcessCommonParameters $StartupProject $Project $Context $dteStartupProject = $values.StartupProject $dteProject = $values.Project $contextTypeName = $values.ContextTypeName $script = InvokeOperation $dteStartupProject $Environment $dteProject ScriptMigration @{ fromMigration = $From toMigration = $To idempotent = [bool]$Idempotent contextType = $contextTypeName } try { # NOTE: Certain SKUs cannot create new SQL files $window = $DTE.ItemOperations.NewFile('General\Sql File') $textDocument = $window.Document.Object('TextDocument') $editPoint = $textDocument.StartPoint.CreateEditPoint() $editPoint.Insert($script) } catch { $fullPath = GetProperty $dteProject.Properties FullPath $intermediatePath = GetProperty $dteProject.ConfigurationManager.ActiveConfiguration.Properties IntermediatePath $fullIntermediatePath = Join-Path $fullPath $intermediatePath $fileName = [IO.Path]::GetRandomFileName() $fileName = [IO.Path]::ChangeExtension($fileName, '.sql') $scriptFile = Join-Path $fullIntermediatePath $fileName $script | Out-File $scriptFile $DTE.ItemOperations.OpenFile($scriptFile) | Out-Null } ShowConsole } # # Remove-Migration # Register-TabExpansion Remove-Migration @{ Context = { param ($tabExpansionContext) GetContextTypes $tabExpansionContext.Project $tabExpansionContext.StartupProject $tabExpansionContext.Environment } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Removes the last migration. .DESCRIPTION Removes the last migration. .PARAMETER Context Specifies the DbContext to use. If omitted, the default DbContext is used. .PARAMETER Project Specifies the project to use. If omitted, the default project is used. .PARAMETER StartupProject Specifies the startup project to use. If omitted, the solution's startup project is used. .PARAMETER Environment Specifies the environment to use. If omitted, "Development" is used. .LINK Add-Migration about_EntityFramework #> function Remove-Migration { [CmdletBinding(PositionalBinding = $false)] param ([string] $Context, [string] $Project, [string] $StartupProject, [string] $Environment) $values = ProcessCommonParameters $StartupProject $Project $Context $dteProject = $values.Project $contextTypeName = $values.ContextTypeName $dteStartupProject = $values.StartupProject $filesToRemove = InvokeOperation $dteStartupProject $Environment $dteProject RemoveMigration @{ contextType = $contextTypeName } $filesToRemove | %{ $projectItem = GetProjectItem $dteProject $_ if ($projectItem) { $projectItem.Remove() } } } # # Scaffold-DbContext # Register-TabExpansion Scaffold-DbContext @{ Provider = { param ($tabExpansionContext) GetProviders $tabExpansionContext.Project } Project = { GetProjects } StartupProject = { GetProjects } } <# .SYNOPSIS Scaffolds a DbContext and entity type classes for a specified database. .DESCRIPTION Scaffolds a DbContext and entity type classes for a specified database. .PARAMETER Connection Specifies the connection string of the database. .PARAMETER Provider Specifies the provider to use. For example, EntityFramework.MicrosoftSqlServer. .PARAMETER OutputDirectory Specifies the directory to use to output the classes. If omitted, the top-level project directory is used. .PARAMETER Context Specifies the name of the generated DbContext class. .PARAMETER Schemas Specifies the schemas for which to generate classes. .PARAMETER Tables Specifies the tables for which to generate classes. .PARAMETER DataAnnotations Use DataAnnotation attributes to configure the model where possible. If omitted, the output code will use only the fluent API. .PARAMETER Project Specifies the project to use. If omitted, the default project is used. .PARAMETER StartupProject Specifies the startup project to use. If omitted, the solution's startup project is used. .PARAMETER Environment Specifies the environment to use. If omitted, "Development" is used. .LINK about_EntityFramework #> function Scaffold-DbContext { [CmdletBinding(PositionalBinding = $false)] param ( [Parameter(Position = 0, Mandatory = $true)] [string] $Connection, [Parameter(Position = 1, Mandatory = $true)] [string] $Provider, [string] $OutputDirectory, [string] $ContextClassName, [string[]] $Schemas, [string[]] $Tables, [switch] $DataAnnotations, [string] $Project, [string] $StartupProject, [string] $Environment) $values = ProcessCommonParameters $StartupProject $Project $dteStartupProject = $values.StartupProject $dteProject = $values.Project $artifacts = InvokeOperation $dteStartupProject $Environment $dteProject ReverseEngineer @{ connectionString = $Connection provider = $Provider outputDir = $OutputDirectory dbContextClassName = $ContextClassName schemaFilters = $Schemas tableFilters = $Tables useDataAnnotations = [bool]$DataAnnotations } $artifacts | %{ $dteProject.ProjectItems.AddFromFile($_) | Out-Null } $DTE.ItemOperations.OpenFile($artifacts[0]) | Out-Null ShowConsole } # # Enable-Migrations (Obsolete) # function Enable-Migrations { # TODO: Link to some docs on the changes to Migrations Write-Warning 'Enable-Migrations is obsolete. Use Add-Migration to start using Migrations.' } # # (Private Helpers) # function GetProjects { $projects = Get-Project -All $groups = $projects | group Name return $projects | %{ if ($groups | ? Name -eq $_.Name | ? Count -eq 1) { return $_.Name } return $_.ProjectName } } function GetContextTypes($projectName, $startupProjectName, $environment) { $values = ProcessCommonParameters $startupProjectName $projectName $startupProject = $values.StartupProject $project = $values.Project $contextTypes = InvokeOperation $startupProject $environment $project GetContextTypes -skipBuild return $contextTypes | %{ $_.SafeName } } function GetMigrations($contextTypeName, $projectName, $startupProjectName, $environment) { $values = ProcessCommonParameters $startupProjectName $projectName $contextTypeName $startupProject = $values.StartupProject $project = $values.Project $contextTypeName = $values.ContextTypeName $migrations = InvokeOperation $startupProject $environment $project GetMigrations @{ contextTypeName = $contextTypeName } -skipBuild return $migrations | %{ $_.SafeName } } function ProcessCommonParameters($startupProjectName, $projectName, $contextTypeName) { $project = GetProject $projectName if (!$contextTypeName -and $project.ProjectName -eq $EFDefaultParameterValues.ProjectName) { $contextTypeName = $EFDefaultParameterValues.ContextTypeName } $startupProject = GetStartupProject $startupProjectName $project return @{ Project = $project ContextTypeName = $contextTypeName StartupProject = $startupProject } } function GetProject($projectName) { if ($projectName) { return Get-Project $projectName } return Get-Project } function ShowConsole { $componentModel = Get-VSComponentModel $powerConsoleWindow = $componentModel.GetService([NuGetConsole.IPowerConsoleWindow]) $powerConsoleWindow.Show() } function InvokeOperation($startupProject, $environment, $project, $operation, $arguments = @{}, [switch] $skipBuild) { $startupProjectName = $startupProject.ProjectName Write-Verbose "Using startup project '$startupProjectName'." $projectName = $project.ProjectName Write-Verbose "Using project '$projectName'" $package = Get-Package -ProjectName $startupProjectName | ? Id -eq EntityFramework.Commands if (!($package)) { throw "Cannot execute this command because EntityFramework.Commands is not installed in the startup project '$startupProjectName'." } if (!$skipBuild) { Write-Verbose 'Build started...' # TODO: Only build required project. Don't use BuildProject, you can't specify platform $solutionBuild = $DTE.Solution.SolutionBuild $solutionBuild.Build($true) if ($solutionBuild.LastBuildInfo) { throw "Build failed." } Write-Verbose 'Build succeeded.' } if (![Type]::GetType('Microsoft.Data.Entity.Design.OperationResultHandler')) { Add-Type -Path (Join-Path $PSScriptRoot OperationHandlers.cs) -CompilerParameters ( New-Object CodeDom.Compiler.CompilerParameters -Property @{ CompilerOptions = '/d:ENABLE_HANDLERS' }) } $logHandler = New-Object Microsoft.Data.Entity.Design.OperationLogHandler @( { param ($message) Write-Error $message } { param ($message) Write-Warning $message } { param ($message) Write-Host $message } { param ($message) Write-Verbose $message } { param ($message) Write-Debug $message } ) $properties = $project.Properties $fullPath = GetProperty $properties FullPath $startupOutputPath = GetProperty $startupProject.ConfigurationManager.ActiveConfiguration.Properties OutputPath $startupProperties = $startupProject.Properties $startupFullPath = GetProperty $startupProperties FullPath $startupTargetDir = Join-Path $startupFullPath $startupOutputPath $webConfig = GetProjectItem $startupProject 'Web.Config' $appConfig = GetProjectItem $startupProject 'App.Config' if ($webConfig) { $configurationFile = GetProperty $webConfig.Properties FullPath $dataDirectory = Join-Path $startupFullPath 'App_Data' } elseif ($appConfig) { $configurationFile = GetProperty $appConfig.Properties FullPath } Write-Verbose "Using application base '$startupTargetDir'." $info = New-Object AppDomainSetup -Property @{ ApplicationBase = $startupTargetDir ShadowCopyFiles = 'true' } if ($configurationFile) { Write-Verbose "Using application configuration '$configurationFile'" $info.ConfigurationFile = $configurationFile } else { Write-Verbose 'No configuration file found.' } $domain = [AppDomain]::CreateDomain('EntityFrameworkDesignDomain', $null, $info) if ($dataDirectory) { Write-Verbose "Using data directory '$dataDirectory'" $domain.SetData('DataDirectory', $dataDirectory) } try { $commandsAssembly = 'EntityFramework.Commands' $operationExecutorTypeName = 'Microsoft.Data.Entity.Design.OperationExecutor' $targetAssemblyName = GetProperty $properties AssemblyName $startupAssemblyName = GetProperty $startupProperties AssemblyName $rootNamespace = GetProperty $properties RootNamespace $executor = $domain.CreateInstanceAndUnwrap( $commandsAssembly, $operationExecutorTypeName, $false, 0, $null, @( [MarshalByRefObject]$logHandler, @{ startupTargetName = $startupAssemblyName targetName = $targetAssemblyName environment = $environment projectDir = $fullPath rootNamespace = $rootNamespace } ), $null, $null) $resultHandler = New-Object Microsoft.Data.Entity.Design.OperationResultHandler $currentDirectory = [IO.Directory]::GetCurrentDirectory() Write-Verbose "Using current directory '$startupTargetDir'." [IO.Directory]::SetCurrentDirectory($startupTargetDir) try { $domain.CreateInstance( $commandsAssembly, "$operationExecutorTypeName+$operation", $false, 0, $null, ($executor, [MarshalByRefObject]$resultHandler, $arguments), $null, $null) | Out-Null } finally { [IO.Directory]::SetCurrentDirectory($currentDirectory) } } finally { [AppDomain]::Unload($domain) } if ($resultHandler.ErrorType) { if ($resultHandler.ErrorType -eq 'Microsoft.Data.Entity.Design.OperationException') { Write-Verbose $resultHandler.ErrorStackTrace } else { Write-Host $resultHandler.ErrorStackTrace } throw $resultHandler.ErrorMessage } if ($resultHandler.HasResult) { return $resultHandler.Result } } function GetProperty($properties, $propertyName) { $property = $properties.Item($propertyName) if (!$property) { return $null } return $property.Value } function GetProjectItem($project, $path) { $fullPath = GetProperty $project.Properties FullPath if (Split-Path $path -IsAbsolute) { $path = $path.Substring($fullPath.Length) } $itemDirectory = (Split-Path $path -Parent) $projectItems = $project.ProjectItems if ($itemDirectory) { $directories = $itemDirectory.Split('\') $directories | %{ $projectItems = $projectItems.Item($_).ProjectItems } } $itemName = Split-Path $path -Leaf try { return $projectItems.Item($itemName) } catch [Exception] { } return $null } 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 (!(Split-Path -IsAbsolute $startupProjectPath)) { $solutionPath = Split-Path (GetProperty $DTE.Solution.Properties Path) $startupProjectPath = Join-Path $solutionPath $startupProjectPath -Resolve } $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-Verbose 'More than one startup project found.' } } else { Write-Verbose 'No startup project found.' } return $fallbackProject } function GetSolutionProjects() { $projects = New-Object System.Collections.Stack $DTE.Solution.Projects | %{ $projects.Push($_) } while ($projects.Count -ne 0) { $project = $projects.Pop(); # NOTE: This line is similar to doing a "yield return" in C# $project if ($project.ProjectItems) { $project.ProjectItems | ?{ $_.SubProject } | %{ $projects.Push($_.SubProject) } } } } function GetProviders($projectName) { if (!($projectName)) { $projectName = (Get-Project).ProjectName } return Get-Package -ProjectName $projectName | select -ExpandProperty Id } # SIG # Begin signature block # MIIkCAYJKoZIhvcNAQcCoIIj+TCCI/UCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBb+AHUwlUcC0P0 # KEW7krPhrAf1rCRDDgYuENfAzicT/aCCDZIwggYQMIID+KADAgECAhMzAAAAZEeE # lIbbQRk4AAAAAABkMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMTUxMDI4MjAzMTQ2WhcNMTcwMTI4MjAzMTQ2WjCBgzEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsGA1UECxMETU9Q # UjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMIIBIjANBgkqhkiG9w0B # AQEFAAOCAQ8AMIIBCgKCAQEAky7a2OY+mNkbD2RfTahYTRQ793qE/DwRMTrvicJK # LUGlSF3dEp7vq2YoNNV9KlV7TE2K8sDxstNSFYu2swi4i1AL3X/7agmg3GcExPHf # vHUYIEC+eCyZVt3u9S7dPkL5Wh8wrgEUirCCtVGg4m1l/vcYCo0wbU06p8XzNi3u # XyygkgCxHEziy/f/JCV/14/A3ZduzrIXtsccRKckyn6B5uYxuRbZXT7RaO6+zUjQ # hiyu3A4hwcCKw+4bk1kT9sY7gHIYiFP7q78wPqB3vVKIv3rY6LCTraEbjNR+phBQ # EL7hyBxk+ocu+8RHZhbAhHs2r1+6hURsAg8t4LAOG6I+JQIDAQABo4IBfzCCAXsw # HwYDVR0lBBgwFgYIKwYBBQUHAwMGCisGAQQBgjdMCAEwHQYDVR0OBBYEFFhWcQTw # vbsz9YNozOeARvdXr9IiMFEGA1UdEQRKMEikRjBEMQ0wCwYDVQQLEwRNT1BSMTMw # MQYDVQQFEyozMTY0Mis0OWU4YzNmMy0yMzU5LTQ3ZjYtYTNiZS02YzhjNDc1MWM0 # YjYwHwYDVR0jBBgwFoAUSG5k5VAF04KqFzc3IrVtqMp1ApUwVAYDVR0fBE0wSzBJ # oEegRYZDaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljQ29k # U2lnUENBMjAxMV8yMDExLTA3LTA4LmNybDBhBggrBgEFBQcBAQRVMFMwUQYIKwYB # BQUHMAKGRWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj # Q29kU2lnUENBMjAxMV8yMDExLTA3LTA4LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqG # SIb3DQEBCwUAA4ICAQCI4gxkQx3dXK6MO4UktZ1A1r1mrFtXNdn06DrARZkQTdu0 # kOTLdlGBCfCzk0309RLkvUgnFKpvLddrg9TGp3n80yUbRsp2AogyrlBU+gP5ggHF # i7NjGEpj5bH+FDsMw9PygLg8JelgsvBVudw1SgUt625nY7w1vrwk+cDd58TvAyJQ # FAW1zJ+0ySgB9lu2vwg0NKetOyL7dxe3KoRLaztUcqXoYW5CkI+Mv3m8HOeqlhyf # FTYxPB5YXyQJPKQJYh8zC9b90JXLT7raM7mQ94ygDuFmlaiZ+QSUR3XVupdEngrm # ZgUB5jX13M+Pl2Vv7PPFU3xlo3Uhj1wtupNC81epoxGhJ0tRuLdEajD/dCZ0xIni # esRXCKSC4HCL3BMnSwVXtIoj/QFymFYwD5+sAZuvRSgkKyD1rDA7MPcEI2i/Bh5O # MAo9App4sR0Gp049oSkXNhvRi/au7QG6NJBTSBbNBGJG8Qp+5QThKoQUk8mj0ugr # 4yWRsA9JTbmqVw7u9suB5OKYBMUN4hL/yI+aFVsE/KJInvnxSzXJ1YHka45ADYMK # AMl+fLdIqm3nx6rIN0RkoDAbvTAAXGehUCsIod049A1T3IJyUJXt3OsTd3WabhIB # XICYfxMg10naaWcyUePgW3+VwP0XLKu4O1+8ZeGyaDSi33GnzmmyYacX3BTqMDCC # B3owggVioAMCAQICCmEOkNIAAAAAAAMwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29m # dCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDcwODIwNTkw # OVoXDTI2MDcwODIxMDkwOVowfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAx # MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKvw+nIQHC6t2G6qghBN # NLrytlghn0IbKmvpWlCquAY4GgRJun/DDB7dN2vGEtgL8DjCmQawyDnVARQxQtOJ # DXlkh36UYCRsr55JnOloXtLfm1OyCizDr9mpK656Ca/XllnKYBoF6WZ26DJSJhIv # 56sIUM+zRLdd2MQuA3WraPPLbfM6XKEW9Ea64DhkrG5kNXimoGMPLdNAk/jj3gcN # 1Vx5pUkp5w2+oBN3vpQ97/vjK1oQH01WKKJ6cuASOrdJXtjt7UORg9l7snuGG9k+ # sYxd6IlPhBryoS9Z5JA7La4zWMW3Pv4y07MDPbGyr5I4ftKdgCz1TlaRITUlwzlu # ZH9TupwPrRkjhMv0ugOGjfdf8NBSv4yUh7zAIXQlXxgotswnKDglmDlKNs98sZKu # HCOnqWbsYR9q4ShJnV+I4iVd0yFLPlLEtVc/JAPw0XpbL9Uj43BdD1FGd7P4AOG8 # rAKCX9vAFbO9G9RVS+c5oQ/pI0m8GLhEfEXkwcNyeuBy5yTfv0aZxe/CHFfbg43s # TUkwp6uO3+xbn6/83bBm4sGXgXvt1u1L50kppxMopqd9Z4DmimJ4X7IvhNdXnFy/ # dygo8e1twyiPLI9AN0/B4YVEicQJTMXUpUMvdJX3bvh4IFgsE11glZo+TzOE2rCI # F96eTvSWsLxGoGyY0uDWiIwLAgMBAAGjggHtMIIB6TAQBgkrBgEEAYI3FQEEAwIB # ADAdBgNVHQ4EFgQUSG5k5VAF04KqFzc3IrVtqMp1ApUwGQYJKwYBBAGCNxQCBAwe # CgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j # BBgwFoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0 # cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2Vy # QXV0MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUH # MAKGQmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2Vy # QXV0MjAxMV8yMDExXzAzXzIyLmNydDCBnwYDVR0gBIGXMIGUMIGRBgkrBgEEAYI3 # LgMwgYMwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lv # cHMvZG9jcy9wcmltYXJ5Y3BzLmh0bTBABggrBgEFBQcCAjA0HjIgHQBMAGUAZwBh # AGwAXwBwAG8AbABpAGMAeQBfAHMAdABhAHQAZQBtAGUAbgB0AC4gHTANBgkqhkiG # 9w0BAQsFAAOCAgEAZ/KGpZjgVHkaLtPYdGcimwuWEeFjkplCln3SeQyQwWVfLiw+ # +MNy0W2D/r4/6ArKO79HqaPzadtjvyI1pZddZYSQfYtGUFXYDJJ80hpLHPM8QotS # 0LD9a+M+By4pm+Y9G6XUtR13lDni6WTJRD14eiPzE32mkHSDjfTLJgJGKsKKELuk # qQUMm+1o+mgulaAqPyprWEljHwlpblqYluSD9MCP80Yr3vw70L01724lruWvJ+3Q # 3fMOr5kol5hNDj0L8giJ1h/DMhji8MUtzluetEk5CsYKwsatruWy2dsViFFFWDgy # cScaf7H0J/jeLDogaZiyWYlobm+nt3TDQAUGpgEqKD6CPxNNZgvAs0314Y9/HG8V # fUWnduVAKmWjw11SYobDHWM2l4bf2vP48hahmifhzaWX0O5dY0HjWwechz4GdwbR # BrF1HxS+YWG18NzGGwS+30HHDiju3mUv7Jf2oVyW2ADWoUa9WfOXpQlLSBCZgB/Q # ACnFsZulP0V3HjXG0qKin3p6IvpIlR+r+0cjgPWe+L9rt0uX4ut1eBrs6jeZeRhL # /9azI2h15q/6/IvrC4DqaTuv/DDtBEyO3991bWORPdGdVk5Pv4BXIqF4ETIheu9B # CrE/+6jMpF3BoYibV3FWTkhFwELJm3ZbCoBIa/15n8G9bW1qyVJzEw16UM0xghXM # MIIVyAIBATCBlTB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExAhMzAAAA # ZEeElIbbQRk4AAAAAABkMA0GCWCGSAFlAwQCAQUAoIG6MBkGCSqGSIb3DQEJAzEM # BgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqG # SIb3DQEJBDEiBCANVv19l8rn4c1QuRlGPJQ3b4ubzgxowZjqY7YfFyLeEDBOBgor # BgEEAYI3AgEMMUAwPqAkgCIATQBpAGMAcgBvAHMAbwBmAHQAIABBAFMAUAAuAE4A # RQBUoRaAFGh0dHA6Ly93d3cuYXNwLm5ldC8gMA0GCSqGSIb3DQEBAQUABIIBAD7/ # xK5eHMaXbVfA7waWvQkzx5olLN5kYFnPyDrL20LnF5B3sXT2Ln7LsYotNYRJWiE/ # /DmdioFvnp6dfx12wxEGSK9pPIyaJJHtz8bH/6EoTp0igFOfTqOH3AHK4uQe9JfQ # 6ONr7aYoF6kfzOq133WCgNJ/wU0pKva0S8ZxbzhnektaHO2ufsF+Ls1VID8/5Rvs # laS0BeEpLqxlLgPqKu4r8qmruTghY5fwLGu3rlg+GuG+8QdWSRN1cTfHgSJ13iiX # Vgah/8aAAXjrrbh8PV7nJEwNtZkCgDvawIZJfypSxpE08Y5VrdQ8KSQKJm4tckj+ # kL8ZgGhsAIm/bSWpxoehghNKMIITRgYKKwYBBAGCNwMDATGCEzYwghMyBgkqhkiG # 9w0BBwKgghMjMIITHwIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBPAYLKoZIhvcNAQkQ # AQSgggErBIIBJzCCASMCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQg # ECS/t9aSzk2D6TdYMWhFaIHuvFSJcL8SQbM7GUBrIjsCBlYqdFSZDxgTMjAxNTEx # MTcwMDAyMTYuMTA1WjAHAgEBgAIB9KCBuKSBtTCBsjELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xDzANBgNVBAcTBlJlZG1vZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BSMScwJQYDVQQLEx5uQ2lw # aGVyIERTRSBFU046MzFDNS0zMEJBLTdDOTExJTAjBgNVBAMTHE1pY3Jvc29mdCBU # aW1lLVN0YW1wIFNlcnZpY2Wggg7OMIIGcTCCBFmgAwIBAgIKYQmBKgAAAAAAAjAN # BgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9y # aXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYD # VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe # MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3Nv # ZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC # AQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2 # tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiEVEMM1024OAizQt2TrNZz # MFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+BVLHPk0ySwcSmXdFhE24o # xhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5n # f/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXoeByw6ZnNPOcvRLqn9Nxk # vaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYwggHiMBAGCSsGAQQBgjcV # AQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3 # FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAf # BgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNS # b29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0Nl # ckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3 # LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kv # ZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBs # AF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcN # AQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM9TG+zwXiqf76V20ZMLPC # xWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5 # Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgPF/UveYFl2am1a+THzvbK # egBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/625Y4zu2JfmttXQOnxzplm # kIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZqkHimbdLhnPkd/DjYlPTG # pQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGn # Ecua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd4 # 6PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1V # mXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduWsqdCosnPGUFN4Ib5Kpqj # EWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV42neV8HR3jDA/czmTfsN # v11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto229Nfj950iEkSMIIE2TCC # A8GgAwIBAgITMwAAAHR0zK2pPny1rAAAAAAAdDANBgkqhkiG9w0BAQsFADB8MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy # b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0xNTEwMDcxODE3MzlaFw0xNzAx # MDcxODE3MzlaMIGyMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEP # MA0GA1UEBxMGUmVkbW9kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # DTALBgNVBAsTBE1PUFIxJzAlBgNVBAsTHm5DaXBoZXIgRFNFIEVTTjozMUM1LTMw # QkEtN0M5MTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC # ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKtD2GPlo0EP5ToGkaOTIK29 # Kq7brXtNqFhfOgUJwUtYQhc6JI5tqN1F5JRSMOJ+9jAWrCAEkXx3eU20k56+ehNq # fWjE8UIz2NRljSi8J1NynAcCVgSJFuKjYAdOljvWU8rxFSI6P/KRoMPbictp6jL/ # PzRG/BmlkhvMjNyspPfNlbH5bJRY89ihU2gNlzALmGkWfiOgdK/oRPOe+ZPU42i1 # at+WOCReoWX2Z8mtsNn4gH9yt1NmK4cCoAgKuvuNmQVf0L+NGE2/Lx9jPJSx52Nc # CcbqtK60yRE8x83K9944NHb9iyQROUD5fuq9rvfESl7BwVOUoWtgvMZE77Ez2skC # AwEAAaOCARswggEXMB0GA1UdDgQWBBQ0xqxSiifT0x6XvwwDdXdKutjkMTAfBgNV # HSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVo # dHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1T # dGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAC # hj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBD # QV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUF # BwMIMA0GCSqGSIb3DQEBCwUAA4IBAQBZkYwuw6ioh0NFGo6Zk8hEbrRwkVNITye1 # +EbXXG4siy5OCF4iXWRR9domidXuE3uMsbjqHgHfgDYVAovD0Oi9Zqxbkc0vPEhX # qi8xRQMLLwy4jFSjTJ6S52DlcIr+EIBQj4pOUFqvcRdO8bSFsDIKUVJxCHDkDKyK # MwY008TjNB+OKBvhcQND6+nbQOLWtUnGYKOWEUgQpNVyKrEu90fq4yvbuEq68CMQ # GrhHTLXSpSyxSjrE1j+OXz+e95D/jeUs4G7323tx0Q0oihO8sm+aJsC8iFmb5c8X # HVmIof4wKgCzDkDTGwdUnpnL+kOs7FXQm07NmbPsHIKI2WA74OgSoYIDeDCCAmAC # AQEwgeKhgbikgbUwgbIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MQ8wDQYDVQQHEwZSZWRtb2QxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNOOjMxQzUt # MzBCQS03QzkxMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl # oiUKAQEwCQYFKw4DAhoFAAMVABB2BqyiAFCEUKaVc38ZxJdOER74oIHCMIG/pIG8 # MIG5MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQL # EwRNT1BSMScwJQYDVQQLEx5uQ2lwaGVyIE5UUyBFU046NTdGNi1DMUUwLTU1NEMx # KzApBgNVBAMTIk1pY3Jvc29mdCBUaW1lIFNvdXJjZSBNYXN0ZXIgQ2xvY2swDQYJ # KoZIhvcNAQEFBQACBQDZ9InrMCIYDzIwMTUxMTE2MTY1OTIzWhgPMjAxNTExMTcx # NjU5MjNaMHcwPQYKKwYBBAGEWQoEATEvMC0wCgIFANn0iesCAQAwCgIBAAICHnAC # Af8wBwIBAAICGH8wCgIFANn122sCAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYB # BAGEWQoDAaAKMAgCAQACAxbjYKEKMAgCAQACAwehIDANBgkqhkiG9w0BAQUFAAOC # AQEAGXYdsvH8BO5ykyXOHXanfUMbeqhL+u7tX7wN5xwL9lSKg2asdnYO53AFBg+x # +MbVYUkW/u4IYuBW1WA981NLdzV1oDtsdkvC4aJfA/M8X7HXOd6jMfHuXMfIHeR6 # C5dR95bwwVCVKlwhnnnYAKBH8tbw3kOZ/6oP4iS8bI1k9Nihiebb6WF1ocuAtu3I # M1mJt3VS2G2jRWdXO/MNrmlssB+1Km4CkvRZV70ZmWssZy09L6hP0xvqd4AnKSDW # 2X4T3PUF8OR4uUZ0aYjlkHJkfJPf1Yh0Oe/sCf5A5Qas88BlSmATyU0WSRd1YR7m # YICUMkoEQxAzNMjGF825rTGqSzGCAvUwggLxAgEBMIGTMHwxCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1l # LVN0YW1wIFBDQSAyMDEwAhMzAAAAdHTMrak+fLWsAAAAAAB0MA0GCWCGSAFlAwQC # AQUAoIIBMjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkE # MSIEIOrJJOKLinmYlSOIyanQfvQZGZ74mWwLN2A2/enCQgCiMIHiBgsqhkiG9w0B # CRACDDGB0jCBzzCBzDCBsQQUEHYGrKIAUIRQppVzfxnEl04RHvgwgZgwgYCkfjB8 # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N # aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAHR0zK2pPny1rAAAAAAA # dDAWBBQnnC6SnEZmRR+MN+ymmnNCdB8rqzANBgkqhkiG9w0BAQsFAASCAQCKhi57 # UtfKy3nfOc/QsYoMYAwMEcw+O0QeYSc+usQnY7OuJeTFG8jbb363S9AJOEQNm8C4 # Aq4KRfewKuxWuLYOQVwKOm67NlgmuwhvpKOpQ8wpRs110AQhpn3o7L3W4IN4XrA1 # W72gy0sSUuMKamkj6BQ3A2FQAMzCF1a4+Fao19HXAMPblelbYVmxADqL6Ai8jGkP # 3+lZE0/sZVxsmGnjqWi2OsurfhMnw1kiZ7gQqk6WR22Lt8LCi9RblfuZGKMyecn2 # cY5LxEKqMlfSuF0K9Rt+4jz9Z5UbBko2540XN08Hgu1JpxFwrcygxO22CsrnT3Xl # qdvbsRKWbxqq6IXp # SIG # End signature block