# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
$ErrorActionPreference = 'Stop'
|
$InitialDatabase = '0'
|
|
<#
|
.SYNOPSIS
|
Adds or updates an Entity Framework provider entry in the project config
|
file.
|
|
.DESCRIPTION
|
Adds an entry into the 'entityFramework' section of the project config
|
file for the specified provider invariant name and provider type. If an
|
entry for the given invariant name already exists, then that entry is
|
updated with the given type name, unless the given type name already
|
matches, in which case no action is taken. The 'entityFramework'
|
section is added if it does not exist. The config file is automatically
|
saved if and only if a change was made.
|
|
This command is typically used only by Entity Framework provider NuGet
|
packages and is run from the 'install.ps1' script.
|
|
.PARAMETER Project
|
The Visual Studio project to update. When running in the NuGet install.ps1
|
script the '$project' variable provided as part of that script should be
|
used.
|
|
.PARAMETER InvariantName
|
The provider invariant name that uniquely identifies this provider. For
|
example, the Microsoft SQL Server provider is registered with the invariant
|
name 'System.Data.SqlClient'.
|
|
.PARAMETER TypeName
|
The assembly-qualified type name of the provider-specific type that
|
inherits from 'System.Data.Entity.Core.Common.DbProviderServices'. For
|
example, for the Microsoft SQL Server provider, this type is
|
'System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer'.
|
#>
|
function Add-EFProvider
|
{
|
[CmdletBinding(PositionalBinding = $false)]
|
param(
|
[parameter(Position = 0, Mandatory = $true)]
|
$Project,
|
[parameter(Position = 1, Mandatory = $true)]
|
[string] $InvariantName,
|
[parameter(Position = 2, Mandatory = $true)]
|
[string] $TypeName)
|
|
$configPath = GetConfigPath $Project
|
if (!$configPath)
|
{
|
return
|
}
|
|
[xml] $configXml = Get-Content $configPath
|
|
$providers = $configXml.configuration.entityFramework.providers
|
|
$providers.provider |
|
where invariantName -eq $InvariantName |
|
%{ $providers.RemoveChild($_) | Out-Null }
|
|
$provider = $providers.AppendChild($configXml.CreateElement('provider'))
|
$provider.SetAttribute('invariantName', $InvariantName)
|
$provider.SetAttribute('type', $TypeName)
|
|
$configXml.Save($configPath)
|
}
|
|
<#
|
.SYNOPSIS
|
Adds or updates an Entity Framework default connection factory in the
|
project config file.
|
|
.DESCRIPTION
|
Adds an entry into the 'entityFramework' section of the project config
|
file for the connection factory that Entity Framework will use by default
|
when creating new connections by convention. Any existing entry will be
|
overridden if it does not match. The 'entityFramework' section is added if
|
it does not exist. The config file is automatically saved if and only if
|
a change was made.
|
|
This command is typically used only by Entity Framework provider NuGet
|
packages and is run from the 'install.ps1' script.
|
|
.PARAMETER Project
|
The Visual Studio project to update. When running in the NuGet install.ps1
|
script the '$project' variable provided as part of that script should be
|
used.
|
|
.PARAMETER TypeName
|
The assembly-qualified type name of the connection factory type that
|
implements the 'System.Data.Entity.Infrastructure.IDbConnectionFactory'
|
interface. For example, for the Microsoft SQL Server Express provider
|
connection factory, this type is
|
'System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework'.
|
|
.PARAMETER ConstructorArguments
|
An optional array of strings that will be passed as arguments to the
|
connection factory type constructor.
|
#>
|
function Add-EFDefaultConnectionFactory
|
{
|
[CmdletBinding(PositionalBinding = $false)]
|
param(
|
[parameter(Position = 0, Mandatory = $true)]
|
$Project,
|
[parameter(Position = 1, Mandatory = $true)]
|
[string] $TypeName,
|
[string[]] $ConstructorArguments)
|
|
$configPath = GetConfigPath $Project
|
if (!$configPath)
|
{
|
return
|
}
|
|
[xml] $configXml = Get-Content $configPath
|
|
$entityFramework = $configXml.configuration.entityFramework
|
$defaultConnectionFactory = $entityFramework.defaultConnectionFactory
|
if ($defaultConnectionFactory)
|
{
|
$entityFramework.RemoveChild($defaultConnectionFactory) | Out-Null
|
}
|
$defaultConnectionFactory = $entityFramework.AppendChild($configXml.CreateElement('defaultConnectionFactory'))
|
|
$defaultConnectionFactory.SetAttribute('type', $TypeName)
|
|
if ($ConstructorArguments)
|
{
|
$parameters = $defaultConnectionFactory.AppendChild($configXml.CreateElement('parameters'))
|
|
foreach ($constructorArgument in $ConstructorArguments)
|
{
|
$parameter = $parameters.AppendChild($configXml.CreateElement('parameter'))
|
$parameter.SetAttribute('value', $constructorArgument)
|
}
|
}
|
|
$configXml.Save($configPath)
|
}
|
|
<#
|
.SYNOPSIS
|
Enables Code First Migrations in a project.
|
|
.DESCRIPTION
|
Enables Migrations by scaffolding a migrations configuration class in the project. If the
|
target database was created by an initializer, an initial migration will be created (unless
|
automatic migrations are enabled via the EnableAutomaticMigrations parameter).
|
|
.PARAMETER ContextTypeName
|
Specifies the context to use. If omitted, migrations will attempt to locate a
|
single context type in the target project.
|
|
.PARAMETER EnableAutomaticMigrations
|
Specifies whether automatic migrations will be enabled in the scaffolded migrations configuration.
|
If omitted, automatic migrations will be disabled.
|
|
.PARAMETER MigrationsDirectory
|
Specifies the name of the directory that will contain migrations code files.
|
If omitted, the directory will be named "Migrations".
|
|
.PARAMETER ProjectName
|
Specifies the project that the scaffolded migrations configuration class will
|
be added to. If omitted, the default project selected in package manager
|
console is used.
|
|
.PARAMETER StartUpProjectName
|
Specifies the configuration file to use for named connection strings. If
|
omitted, the specified project's configuration file is used.
|
|
.PARAMETER ContextProjectName
|
Specifies the project which contains the DbContext class to use. If omitted,
|
the context is assumed to be in the same project used for migrations.
|
|
.PARAMETER ConnectionStringName
|
Specifies the name of a connection string to use from the application's
|
configuration file.
|
|
.PARAMETER ConnectionString
|
Specifies the connection string to use. If omitted, the context's
|
default connection will be used.
|
|
.PARAMETER ConnectionProviderName
|
Specifies the provider invariant name of the connection string.
|
|
.PARAMETER Force
|
Specifies that the migrations configuration be overwritten when running more
|
than once for a given project.
|
|
.PARAMETER ContextAssemblyName
|
Specifies the name of the assembly which contains the DbContext class to use. Use this
|
parameter instead of ContextProjectName when the context is contained in a referenced
|
assembly rather than in a project of the solution.
|
|
.PARAMETER AppDomainBaseDirectory
|
Specifies the directory to use for the app-domain that is used for running Migrations
|
code such that the app-domain is able to find all required assemblies. This is an
|
advanced option that should only be needed if the solution contains several projects
|
such that the assemblies needed for the context and configuration are not all
|
referenced from either the project containing the context or the project containing
|
the migrations.
|
|
.EXAMPLE
|
Enable-Migrations
|
# Scaffold a migrations configuration in a project with only one context
|
|
.EXAMPLE
|
Enable-Migrations -Auto
|
# Scaffold a migrations configuration with automatic migrations enabled for a project
|
# with only one context
|
|
.EXAMPLE
|
Enable-Migrations -ContextTypeName MyContext -MigrationsDirectory DirectoryName
|
# Scaffold a migrations configuration for a project with multiple contexts
|
# This scaffolds a migrations configuration for MyContext and will put the configuration
|
# and subsequent configurations in a new directory called "DirectoryName"
|
|
#>
|
function Enable-Migrations
|
{
|
[CmdletBinding(DefaultParameterSetName = 'ConnectionStringName', PositionalBinding = $false)]
|
param(
|
[string] $ContextTypeName,
|
[alias('Auto')]
|
[switch] $EnableAutomaticMigrations,
|
[string] $MigrationsDirectory,
|
[string] $ProjectName,
|
[string] $StartUpProjectName,
|
[string] $ContextProjectName,
|
[parameter(ParameterSetName = 'ConnectionStringName')]
|
[string] $ConnectionStringName,
|
[parameter(ParameterSetName = 'ConnectionStringAndProviderName', Mandatory = $true)]
|
[string] $ConnectionString,
|
[parameter(ParameterSetName = 'ConnectionStringAndProviderName', Mandatory = $true)]
|
[string] $ConnectionProviderName,
|
[switch] $Force,
|
[string] $ContextAssemblyName,
|
[string] $AppDomainBaseDirectory)
|
|
WarnIfOtherEFs 'Enable-Migrations'
|
|
$project = GetProject $ProjectName
|
$startupProject = GetStartupProject $StartUpProjectName $project
|
|
if (!$ContextAssemblyName -and $ContextProjectName)
|
{
|
$contextProject = Get-Project $ContextProjectName
|
$ContextAssemblyName = GetProperty $contextProject.Properties 'AssemblyName'
|
}
|
|
$params = 'migrations', 'enable', '--json'
|
|
if ($ContextTypeName)
|
{
|
$params += '--context', $ContextTypeName
|
}
|
|
if ($ContextAssemblyName)
|
{
|
$params += '--context-assembly', $ContextAssemblyName
|
}
|
|
if ($EnableAutomaticMigrations)
|
{
|
$params += '--auto'
|
}
|
|
if ($MigrationsDirectory)
|
{
|
$params += '--migrations-dir', $MigrationsDirectory
|
}
|
|
$params += GetParams $ConnectionStringName $ConnectionString $ConnectionProviderName
|
|
if ($Force)
|
{
|
$params += '--force'
|
}
|
|
# NB: -join is here to support ConvertFrom-Json on PowerShell 3.0
|
$result = (EF6 $project $startupProject $AppDomainBaseDirectory $params) -join "`n" | ConvertFrom-Json
|
|
$project.ProjectItems.AddFromFile($result.migrationsConfiguration) | Out-Null
|
$DTE.ItemOperations.OpenFile($result.migrationsConfiguration) | Out-Null
|
ShowConsole
|
|
if ($result.migration)
|
{
|
$project.ProjectItems.AddFromFile($result.migration) | Out-Null
|
$resourcesProperties = $project.ProjectItems.AddFromFile($result.migrationResources).Properties
|
$project.ProjectItems.AddFromFile($result.migrationDesigner) | Out-Null
|
}
|
}
|
|
<#
|
.SYNOPSIS
|
Scaffolds a migration script for any pending model changes.
|
|
.DESCRIPTION
|
Scaffolds a new migration script and adds it to the project.
|
|
.PARAMETER Name
|
Specifies the name of the custom script.
|
|
.PARAMETER Force
|
Specifies that the migration user code be overwritten when re-scaffolding an
|
existing migration.
|
|
.PARAMETER ProjectName
|
Specifies the project that contains the migration configuration type to be
|
used. If omitted, the default project selected in package manager console
|
is used.
|
|
.PARAMETER StartUpProjectName
|
Specifies the configuration file to use for named connection strings. If
|
omitted, the specified project's configuration file is used.
|
|
.PARAMETER ConfigurationTypeName
|
Specifies the migrations configuration to use. If omitted, migrations will
|
attempt to locate a single migrations configuration type in the target
|
project.
|
|
.PARAMETER ConnectionStringName
|
Specifies the name of a connection string to use from the application's
|
configuration file.
|
|
.PARAMETER ConnectionString
|
Specifies the connection string to use. If omitted, the context's
|
default connection will be used.
|
|
.PARAMETER ConnectionProviderName
|
Specifies the provider invariant name of the connection string.
|
|
.PARAMETER IgnoreChanges
|
Scaffolds an empty migration ignoring any pending changes detected in the current model.
|
This can be used to create an initial, empty migration to enable Migrations for an existing
|
database. N.B. Doing this assumes that the target database schema is compatible with the
|
current model.
|
|
.PARAMETER AppDomainBaseDirectory
|
Specifies the directory to use for the app-domain that is used for running Migrations
|
code such that the app-domain is able to find all required assemblies. This is an
|
advanced option that should only be needed if the solution contains several projects
|
such that the assemblies needed for the context and configuration are not all
|
referenced from either the project containing the context or the project containing
|
the migrations.
|
|
.EXAMPLE
|
Add-Migration First
|
# Scaffold a new migration named "First"
|
|
.EXAMPLE
|
Add-Migration First -IgnoreChanges
|
# Scaffold an empty migration ignoring any pending changes detected in the current model.
|
# This can be used to create an initial, empty migration to enable Migrations for an existing
|
# database. N.B. Doing this assumes that the target database schema is compatible with the
|
# current model.
|
|
#>
|
function Add-Migration
|
{
|
[CmdletBinding(DefaultParameterSetName = 'ConnectionStringName', PositionalBinding = $false)]
|
param(
|
[parameter(Position = 0, Mandatory = $true)]
|
[string] $Name,
|
[switch] $Force,
|
[string] $ProjectName,
|
[string] $StartUpProjectName,
|
[string] $ConfigurationTypeName,
|
[parameter(ParameterSetName = 'ConnectionStringName')]
|
[string] $ConnectionStringName,
|
[parameter(ParameterSetName = 'ConnectionStringAndProviderName', Mandatory = $true)]
|
[string] $ConnectionString,
|
[parameter(ParameterSetName = 'ConnectionStringAndProviderName', Mandatory = $true)]
|
[string] $ConnectionProviderName,
|
[switch] $IgnoreChanges,
|
[string] $AppDomainBaseDirectory)
|
|
WarnIfOtherEFs 'Add-Migration'
|
|
$project = GetProject $ProjectName
|
$startupProject = GetStartupProject $StartUpProjectName $project
|
|
$params = 'migrations', 'add', $Name, '--json'
|
|
if ($Force)
|
{
|
$params += '--force'
|
}
|
|
if ($ConfigurationTypeName)
|
{
|
$params += '--migrations-config', $ConfigurationTypeName
|
}
|
|
if ($IgnoreChanges)
|
{
|
$params += '--ignore-changes'
|
}
|
|
$params += GetParams $ConnectionStringName $ConnectionString $ConnectionProviderName
|
|
# NB: -join is here to support ConvertFrom-Json on PowerShell 3.0
|
$result = (EF6 $project $startupProject $AppDomainBaseDirectory $params) -join "`n" | ConvertFrom-Json
|
|
$project.ProjectItems.AddFromFile($result.migration) | Out-Null
|
$DTE.ItemOperations.OpenFile($result.migration) | Out-Null
|
$resourcesProperties = $project.ProjectItems.AddFromFile($result.migrationResources).Properties
|
$project.ProjectItems.AddFromFile($result.migrationDesigner) | Out-Null
|
}
|
|
<#
|
.SYNOPSIS
|
Applies any pending migrations to the database.
|
|
.DESCRIPTION
|
Updates the database to the current model by applying pending migrations.
|
|
.PARAMETER SourceMigration
|
Only valid with -Script. Specifies the name of a particular migration to use
|
as the update's starting point. If omitted, the last applied migration in
|
the database will be used.
|
|
.PARAMETER TargetMigration
|
Specifies the name of a particular migration to update the database to. If
|
omitted, the current model will be used.
|
|
.PARAMETER Script
|
Generate a SQL script rather than executing the pending changes directly.
|
|
.PARAMETER Force
|
Specifies that data loss is acceptable during automatic migration of the
|
database.
|
|
.PARAMETER ProjectName
|
Specifies the project that contains the migration configuration type to be
|
used. If omitted, the default project selected in package manager console
|
is used.
|
|
.PARAMETER StartUpProjectName
|
Specifies the configuration file to use for named connection strings. If
|
omitted, the specified project's configuration file is used.
|
|
.PARAMETER ConfigurationTypeName
|
Specifies the migrations configuration to use. If omitted, migrations will
|
attempt to locate a single migrations configuration type in the target
|
project.
|
|
.PARAMETER ConnectionStringName
|
Specifies the name of a connection string to use from the application's
|
configuration file.
|
|
.PARAMETER ConnectionString
|
Specifies the connection string to use. If omitted, the context's
|
default connection will be used.
|
|
.PARAMETER ConnectionProviderName
|
Specifies the provider invariant name of the connection string.
|
|
.PARAMETER AppDomainBaseDirectory
|
Specifies the directory to use for the app-domain that is used for running Migrations
|
code such that the app-domain is able to find all required assemblies. This is an
|
advanced option that should only be needed if the solution contains several projects
|
such that the assemblies needed for the context and configuration are not all
|
referenced from either the project containing the context or the project containing
|
the migrations.
|
|
.EXAMPLE
|
Update-Database
|
# Update the database to the latest migration
|
|
.EXAMPLE
|
Update-Database -TargetMigration Second
|
# Update database to a migration named "Second"
|
# This will apply migrations if the target hasn't been applied or roll back migrations
|
# if it has
|
|
.EXAMPLE
|
Update-Database -Script
|
# Generate a script to update the database from its current state to the latest migration
|
|
.EXAMPLE
|
Update-Database -Script -SourceMigration Second -TargetMigration First
|
# Generate a script to migrate the database from a specified start migration
|
# named "Second" to a specified target migration named "First"
|
|
.EXAMPLE
|
Update-Database -Script -SourceMigration $InitialDatabase
|
# Generate a script that can upgrade a database currently at any version to the latest version.
|
# The generated script includes logic to check the __MigrationsHistory table and only apply changes
|
# that haven't been previously applied.
|
|
.EXAMPLE
|
Update-Database -TargetMigration $InitialDatabase
|
# Runs the Down method to roll-back any migrations that have been applied to the database
|
|
|
#>
|
function Update-Database
|
{
|
[CmdletBinding(DefaultParameterSetName = 'ConnectionStringName', PositionalBinding = $false)]
|
param(
|
[string] $SourceMigration,
|
[string] $TargetMigration,
|
[switch] $Script,
|
[switch] $Force,
|
[string] $ProjectName,
|
[string] $StartUpProjectName,
|
[string] $ConfigurationTypeName,
|
[parameter(ParameterSetName = 'ConnectionStringName')]
|
[string] $ConnectionStringName,
|
[parameter(ParameterSetName = 'ConnectionStringAndProviderName', Mandatory = $true)]
|
[string] $ConnectionString,
|
[parameter(ParameterSetName = 'ConnectionStringAndProviderName', Mandatory = $true)]
|
[string] $ConnectionProviderName,
|
[string] $AppDomainBaseDirectory)
|
|
WarnIfOtherEFs 'Update-Database'
|
|
$project = GetProject $ProjectName
|
$startupProject = GetStartupProject $StartUpProjectName $project
|
|
$params = 'database', 'update'
|
|
if ($SourceMigration)
|
{
|
$params += '--source', $SourceMigration
|
}
|
|
if ($TargetMigration)
|
{
|
$params += '--target', $TargetMigration
|
}
|
|
if ($Script)
|
{
|
$params += '--script'
|
}
|
|
if ($Force)
|
{
|
$params += '--force'
|
}
|
|
if ($ConfigurationTypeName)
|
{
|
$params += '--migrations-config', $ConfigurationTypeName
|
}
|
|
$params += GetParams $ConnectionStringName $ConnectionString $ConnectionProviderName
|
|
$result = (EF6 $project $startupProject $AppDomainBaseDirectory $params) -join "`n"
|
if ($result)
|
{
|
try
|
{
|
$window = $DTE.ItemOperations.NewFile('General\Sql File')
|
$textDocument = $window.Document.Object('TextDocument')
|
$editPoint = $textDocument.StartPoint.CreateEditPoint()
|
$editPoint.Insert($result)
|
}
|
catch
|
{
|
$intermediatePath = GetIntermediatePath $project
|
if (![IO.Path]::IsPathRooted($intermediatePath))
|
{
|
$projectDir = GetProperty $project.Properties 'FullPath'
|
$intermediatePath = Join-Path $projectDir $intermediatePath -Resolve | Convert-Path
|
}
|
|
$fileName = [IO.Path]::ChangeExtension([IO.Path]::GetRandomFileName(), '.sql')
|
$sqlFile = Join-Path $intermediatePath $fileName
|
|
[IO.File]::WriteAllText($sqlFile, $result)
|
|
$DTE.ItemOperations.OpenFile($sqlFile) | Out-Null
|
}
|
|
ShowConsole
|
}
|
}
|
|
<#
|
.SYNOPSIS
|
Displays the migrations that have been applied to the target database.
|
|
.DESCRIPTION
|
Displays the migrations that have been applied to the target database.
|
|
.PARAMETER ProjectName
|
Specifies the project that contains the migration configuration type to be
|
used. If omitted, the default project selected in package manager console
|
is used.
|
|
.PARAMETER StartUpProjectName
|
Specifies the configuration file to use for named connection strings. If
|
omitted, the specified project's configuration file is used.
|
|
.PARAMETER ConfigurationTypeName
|
Specifies the migrations configuration to use. If omitted, migrations will
|
attempt to locate a single migrations configuration type in the target
|
project.
|
|
.PARAMETER ConnectionStringName
|
Specifies the name of a connection string to use from the application's
|
configuration file.
|
|
.PARAMETER ConnectionString
|
Specifies the connection string to use. If omitted, the context's
|
default connection will be used.
|
|
.PARAMETER ConnectionProviderName
|
Specifies the provider invariant name of the connection string.
|
|
.PARAMETER AppDomainBaseDirectory
|
Specifies the directory to use for the app-domain that is used for running Migrations
|
code such that the app-domain is able to find all required assemblies. This is an
|
advanced option that should only be needed if the solution contains several projects
|
such that the assemblies needed for the context and configuration are not all
|
referenced from either the project containing the context or the project containing
|
the migrations.
|
#>
|
function Get-Migrations
|
{
|
[CmdletBinding(DefaultParameterSetName = 'ConnectionStringName', PositionalBinding = $false)]
|
param(
|
[string] $ProjectName,
|
[string] $StartUpProjectName,
|
[string] $ConfigurationTypeName,
|
[parameter(ParameterSetName = 'ConnectionStringName')]
|
[string] $ConnectionStringName,
|
[parameter(ParameterSetName = 'ConnectionStringAndProviderName', Mandatory = $true)]
|
[string] $ConnectionString,
|
[parameter(ParameterSetName = 'ConnectionStringAndProviderName', Mandatory = $true)]
|
[string] $ConnectionProviderName,
|
[string] $AppDomainBaseDirectory)
|
|
WarnIfOtherEFs 'Get-Migrations'
|
|
$project = GetProject $ProjectName
|
$startupProject = GetStartupProject $StartUpProjectName $project
|
|
$params = 'migrations', 'list'
|
|
if ($ConfigurationTypeName)
|
{
|
$params += '--migrations-config', $ConfigurationTypeName
|
}
|
|
$params += GetParams $ConnectionStringName $ConnectionString $ConnectionProviderName
|
|
return EF6 $project $startupProject $AppDomainBaseDirectory $params
|
}
|
|
function WarnIfOtherEFs($cmdlet)
|
{
|
if (Get-Module 'EntityFrameworkCore')
|
{
|
Write-Warning "Both Entity Framework 6 and Entity Framework Core are installed. The Entity Framework 6 tools are running. Use 'EntityFrameworkCore\$cmdlet' for Entity Framework Core."
|
}
|
|
if (Get-Module 'EntityFramework')
|
{
|
Write-Warning "A version of Entity Framework older than 6.3 is also installed. The newer tools are running. Use 'EntityFramework\$cmdlet' for the older version."
|
}
|
}
|
|
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 = Join-Path $solutionPath $startupProjectPath -Resolve | Convert-Path
|
}
|
|
$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($connectionStringName, $connectionString, $connectionProviderName)
|
{
|
$params = @()
|
|
if ($connectionStringName)
|
{
|
$params += '--connection-string-name', $connectionStringName
|
}
|
|
if ($connectionString)
|
{
|
$params += '--connection-string', $connectionString,
|
'--connection-provider', $connectionProviderName
|
}
|
|
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 EF6($project, $startupProject, $workingDir, $params)
|
{
|
$solutionBuild = $DTE.Solution.SolutionBuild
|
$solutionBuild.BuildProject(
|
$solutionBuild.ActiveConfiguration.Name,
|
$project.UniqueName,
|
<# WaitForBuildToFinish #> $true)
|
if ($solutionBuild.LastBuildInfo)
|
{
|
throw "The project '$($project.ProjectName)' failed to build."
|
}
|
|
$projectDir = GetProperty $project.Properties 'FullPath'
|
$outputPath = GetProperty $project.ConfigurationManager.ActiveConfiguration.Properties 'OutputPath'
|
$targetDir = [IO.Path]::GetFullPath([IO.Path]::Combine($projectDir, $outputPath))
|
$targetFrameworkMoniker = GetProperty $project.Properties 'TargetFrameworkMoniker'
|
$frameworkName = New-Object 'System.Runtime.Versioning.FrameworkName' $targetFrameworkMoniker
|
$targetFrameworkIdentifier = $frameworkName.Identifier
|
$targetFrameworkVersion = $frameworkName.Version
|
|
if ($targetFrameworkIdentifier -in '.NETFramework')
|
{
|
if ($targetFrameworkVersion -lt '4.5')
|
{
|
$frameworkDir = 'net40'
|
}
|
else
|
{
|
$frameworkDir = 'net45'
|
}
|
|
$platformTarget = GetPlatformTarget $project
|
if ($platformTarget -eq 'x86')
|
{
|
$runtimeDir = 'win-x86'
|
}
|
elseif ($platformTarget -eq 'ARM64')
|
{
|
$runtimeDir = 'win-arm64'
|
}
|
elseif ($platformTarget -in 'AnyCPU', 'x64')
|
{
|
$runtimeDir = 'any'
|
}
|
else
|
{
|
throw "Project '$($project.ProjectName)' has an active platform of '$platformTarget'. Select a different " +
|
'platform and try again.'
|
}
|
|
$exePath = Join-Path $PSScriptRoot "$frameworkDir\$runtimeDir\ef6.exe"
|
}
|
elseif ($targetFrameworkIdentifier -eq '.NETCoreApp')
|
{
|
$exePath = (Get-Command 'dotnet').Path
|
|
$targetName = GetProperty $project.Properties 'AssemblyName'
|
$depsFile = Join-Path $targetDir ($targetName + '.deps.json')
|
$projectAssetsFile = GetCpsProperty $project 'ProjectAssetsFile'
|
$runtimeConfig = Join-Path $targetDir ($targetName + '.runtimeconfig.json')
|
$runtimeFrameworkVersion = GetCpsProperty $project 'RuntimeFrameworkVersion'
|
$efPath = Join-Path $PSScriptRoot 'net6.0\any\ef6.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 (Test-Path $runtimeConfig)
|
{
|
$dotnetParams += '--runtimeconfig', $runtimeConfig
|
}
|
elseif ($runtimeFrameworkVersion)
|
{
|
$dotnetParams += '--fx-version', $runtimeFrameworkVersion
|
}
|
|
$dotnetParams += $efPath
|
|
$params = $dotnetParams + $params
|
}
|
else
|
{
|
throw "Project '$($startupProject.ProjectName)' targets framework '$targetFrameworkIdentifier'. The Entity Framework " +
|
'Package Manager Console Tools don''t support this framework.'
|
}
|
|
$targetFileName = GetProperty $project.Properties 'OutputFileName'
|
$targetPath = Join-Path $targetDir $targetFileName
|
$rootNamespace = GetProperty $project.Properties 'RootNamespace'
|
$language = GetLanguage $project
|
|
$params += '--verbose',
|
'--no-color',
|
'--prefix-output',
|
'--assembly', $targetPath,
|
'--project-dir', $projectDir,
|
'--language', $language
|
|
if (IsWeb $startupProject)
|
{
|
$startupProjectDir = GetProperty $startupProject.Properties 'FullPath'
|
$params += '--data-dir', (Join-Path $startupProjectDir 'App_Data')
|
}
|
|
if ($rootNamespace)
|
{
|
$params += '--root-namespace', $rootNamespace
|
}
|
|
$configFile = GetConfigPath $startupProject
|
if ($configFile)
|
{
|
$params += '--config', $configFile
|
}
|
|
if (!$workingDir)
|
{
|
$workingDir = $targetDir
|
}
|
|
$arguments = ToArguments $params
|
$startInfo = New-Object 'System.Diagnostics.ProcessStartInfo' -Property @{
|
FileName = $exePath;
|
Arguments = $arguments;
|
UseShellExecute = $false;
|
CreateNoWindow = $true;
|
RedirectStandardOutput = $true;
|
StandardOutputEncoding = [Text.Encoding]::UTF8;
|
RedirectStandardError = $true;
|
WorkingDirectory = $workingDir;
|
}
|
|
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 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)
|
{
|
$hierarchy = GetVsHierarchy $project
|
|
$aggregatableProject = Get-Interface $hierarchy 'Microsoft.VisualStudio.Shell.Interop.IVsAggregatableProject'
|
if (!$aggregatableProject)
|
{
|
$projectTypes = $project.Kind
|
}
|
else
|
{
|
$projectTypeGuids = $null
|
$hr = $aggregatableProject.GetAggregateProjectTypeGuids([ref] $projectTypeGuids)
|
[Runtime.InteropServices.Marshal]::ThrowExceptionForHR($hr)
|
|
$projectTypes = $projectTypeGuids.Split(';')
|
}
|
|
foreach ($projectType in $projectTypes)
|
{
|
if ($projectType -in '{349C5851-65DF-11DA-9384-00065B846F21}', '{E24C65DC-7377-472B-9ABA-BC803B73C61A}')
|
{
|
return $true
|
}
|
}
|
|
return $false;
|
}
|
|
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 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 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 |
|
where FullPath -eq $project.FullName
|
|
return $msbuildProject.GetProperty($propertyName).EvaluatedValue
|
}
|
|
function ToArguments($params)
|
{
|
$arguments = ''
|
for ($i = 0; $i -lt $params.Length; $i++)
|
{
|
if ($i)
|
{
|
$arguments += ' '
|
}
|
|
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
|
}
|
|
function GetConfigPath($project)
|
{
|
if (IsWeb $project)
|
{
|
$configFileName = 'web.config'
|
}
|
else
|
{
|
$configFileName = 'app.config'
|
}
|
|
$item = $project.ProjectItems |
|
where Name -eq $configFileName |
|
select -First 1
|
|
return GetProperty $item.Properties 'FullPath'
|
}
|
|
Export-ModuleMember 'Add-EFDefaultConnectionFactory', 'Add-EFProvider', 'Add-Migration', 'Enable-Migrations', 'Get-Migrations', 'Update-Database' -Variable 'InitialDatabase'
|
|
# SIG # Begin signature block
|
# MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor
|
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
|
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDbeCwMxGtKRttT
|
# ijQaHdjd2z/9CxF7jWlHbDuJIb0OjaCCDXYwggX0MIID3KADAgECAhMzAAADrzBA
|
# 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
|
# /Xmfwb1tbWrJUnMTDXpQzTGCGg0wghoJAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
|
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
|
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
|
# Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB
|
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
|
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIBf2+UZ1V35D68eblBFde4xK
|
# ErQLQBXaZSIuA5QJUVgxMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
|
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
|
# BQAEggEAR8fq8Jub/0GsohruVqfypUU3COVTWm7wk8KfttGlBVHT/bihicBX9+EA
|
# oGOuQ+L3AenRCz9X4noiT1BYEq7hStfL2zFaOYLqUOQ3w/YhjDtUv9vxZ1e5ImIf
|
# Bv8q9vLdZSXqDt5f3JDsKTae3Bi490wdXhu4QIWyQQDA7m+fzJ2Z6UN/wAFpt09w
|
# gSsiRHjpGssxhxQxOJ5F0RpGqswvMJi6YAGrDUsDqa84L+jERSppTPVGtXke/4sf
|
# vDmhf+NiFlvpFRWKsu5tvkMBwaQHH1oAF+jhkx0fyfoNM+S/oPsiQifakBL70tlm
|
# nt3EAxleVtY7DKDT7VIpWRAlrvL7o6GCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC
|
# F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq
|
# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
|
# AwQCAQUABCACHAdw3WaFxj/DE6frUdpSQB7E01IJP5vGrsiRWT8ncwIGZkY0sUZf
|
# GBMyMDI0MDYxNjIyMTczMC45NjlaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
|
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
|
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
|
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046MzcwMy0w
|
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
|
# ghHtMIIHIDCCBQigAwIBAgITMwAAAeqaJHLVWT9hYwABAAAB6jANBgkqhkiG9w0B
|
# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
|
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
|
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzEyMDYxODQ1
|
# MzBaFw0yNTAzMDUxODQ1MzBaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
|
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
|
# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
|
# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046MzcwMy0wNUUwLUQ5NDcxJTAjBgNV
|
# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
|
# AQUAA4ICDwAwggIKAoICAQC1C1/xSD8gB9X7Ludoo2rWb2ksqaF65QtJkbQpmsc6
|
# G4bg5MOv6WP/uJ4XOJvKX/c1t0ej4oWBqdGD6VbjXX4T0KfylTulrzKtgxnxZh7q
|
# 1uD0Dy/w5G0DJDPb6oxQrz6vMV2Z3y9ZxjfZqBnDfqGon/4VDHnZhdas22svSC5G
|
# HywsQ2J90MM7L4ecY8TnLI85kXXTVESb09txL2tHMYrB+KHCy08ds36an7IcOGfR
|
# mhHbFoPa5om9YGpVKS8xeT7EAwW7WbXL/lo5p9KRRIjAlsBBHD1TdGBucrGC3TQX
|
# STp9s7DjkvvNFuUa0BKsz6UiCLxJGQSZhd2iOJTEfJ1fxYk2nY6SCKsV+VmtV5ai
|
# PzY/sWoFY542+zzrAPr4elrvr9uB6ci/Kci//EOERZEUTBPXME/ia+t8jrT2y3ug
|
# 15MSCVuhOsNrmuZFwaRCrRED0yz4V9wlMTGHIJW55iNM3HPVJJ19vOSvrCP9lsEc
|
# EwWZIQ1FCyPOnkM1fs7880dahAa5UmPqMk5WEKxzDPVp081X5RQ6HGVUz6ZdgQ0j
|
# cT59EG+CKDPRD6mx8ovzIpS/r/wEHPKt5kOhYrjyQHXc9KHKTWfXpAVj1Syqt5X4
|
# nr+Mpeubv+N/PjQEPr0iYJDjSzJrqILhBs5pytb6vyR8HUVMp+mAA4rXjOw42vkH
|
# fQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFCuBRSWiUebpF0BU1MTIcosFblleMB8G
|
# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
|
# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
|
# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
|
# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
|
# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
|
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
|
# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQAog61WXj9+/nxVbX3G37KgvyoNAnuu2w3H
|
# oWZj3H0YCeQ3b9KSZThVThW4iFcHrKnhFMBbXJX4uQI53kOWSaWCaV3xCznpRt3c
|
# 4/gSn3dvO/1GP3MJkpJfgo56CgS9zLOiP31kfmpUdPqekZb4ivMR6LoPb5HNlq0W
|
# bBpzFbtsTjNrTyfqqcqAwc6r99Df2UQTqDa0vzwpA8CxiAg2KlbPyMwBOPcr9hJT
|
# 8sGpX/ZhLDh11dZcbUAzXHo1RJorSSftVa9hLWnzxGzEGafPUwLmoETihOGLqIQl
|
# Cpvr94Hiak0Gq0wY6lduUQjk/lxZ4EzAw/cGMek8J3QdiNS8u9ujYh1B7NLr6t3I
|
# glfScDV3bdVWet1itTUoKVRLIivRDwAT7dRH13Cq32j2JG5BYu/XitRE8cdzaJmD
|
# VBzYhlPl9QXvC+6qR8I6NIN/9914bTq/S4g6FF4f1dixUxE4qlfUPMixGr0Ft4/S
|
# 0P4fwmhs+WHRn62PB4j3zCHixKJCsRn9IR3ExBQKQdMi5auiqB6xQBADUf+F7hSK
|
# ZfbA8sFSFreLSqhvj+qUQF84NcxuaxpbJWVpsO18IL4Qbt45Cz/QMa7EmMGNn7a8
|
# MM3uTQOlQy0u6c/jq111i1JqMjayTceQZNMBMM5EMc5Dr5m3T4bDj9WTNLgP8SFe
|
# 3EqTaWVMOTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
|
# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
|
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
|
# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy
|
# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC
|
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
|
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
|
# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
|
# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg
|
# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF
|
# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6
|
# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp
|
# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu
|
# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E
|
# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0
|
# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q
|
# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ
|
# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA
|
# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw
|
# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG
|
# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV
|
# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
|
# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK
|
# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
|
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
|
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
|
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
|
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
|
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG
|
# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x
|
# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC
|
# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449
|
# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM
|
# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS
|
# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d
|
# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn
|
# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs
|
# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL
|
# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL
|
# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNQ
|
# MIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
|
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
|
# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn
|
# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjM3MDMtMDVFMC1EOTQ3MSUwIwYDVQQD
|
# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQCJ
|
# 2x7cQfjpRskJ8UGIctOCkmEkj6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
|
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
|
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
|
# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6hmPUTAiGA8yMDI0MDYxNjE2MjAz
|
# M1oYDzIwMjQwNjE3MTYyMDMzWjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDqGY9R
|
# AgEAMAoCAQACAhItAgH/MAcCAQACAhNeMAoCBQDqGuDRAgEAMDYGCisGAQQBhFkK
|
# BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ
|
# KoZIhvcNAQELBQADggEBABe5v3A1V/y7e45Z2FbmMDy5IN7cYMg1VtKx5kO+Bw2h
|
# vijh9Q1ySNL/xzwDIiYIF/kASmjDhnRG9041hmVZvZBEJy2FRmLk/KM44DoRchF3
|
# mBbKpW7rYUNj8S+ckBREDn1UXrzxKddUqp0T31xF08RIGHqKsBAm8PMDp51Rz0wD
|
# d+TFKiR+l9KwwmrYRWWF8y9cc+v2Yq6FX30pG2xltgQAQ0oAGbbiIPdt7cQ9rhh+
|
# kICo2G69nqVVthecLNpd0ZIKB/vAM/gXu5Izr3gArZ5P9cRWjNku1KLElR8ZQiuT
|
# FdSGJm9gxVbtQJqL2uRyZHcyJPLCfjcvccLh6+ZBn2ExggQNMIIECQIBATCBkzB8
|
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
|
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
|
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAeqaJHLVWT9hYwABAAAB
|
# 6jANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE
|
# MC8GCSqGSIb3DQEJBDEiBCDJQ6pqu4rUoxyRdLUlh9Lg/VtkL5KoSQpcQfwM3d4Y
|
# WDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EICmPodXjZDR4iwg0ltLANXBh
|
# 5G1uKqKIvq8sjKekuGZ4MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
|
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
|
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
|
# IDIwMTACEzMAAAHqmiRy1Vk/YWMAAQAAAeowIgQgbVBvWZhabjYRdyaw9U2jE0Ne
|
# SVFcgrR8CQxQ587OfJowDQYJKoZIhvcNAQELBQAEggIAhwpRtIDDtx7E/F+26Uuh
|
# hTUZXHF0HDO2j5ILvcCLsRFvAYF4ALQ7xQAR2ujm7m7lK0RtK/zwZpZhifWlYwcG
|
# HT+/af9qmogvJmtsyK+Gd3CMcFgnOk7VLR0FX1z+yQ6aoAEuJKimv9DwLi8Tf6En
|
# M26/JH8Mo2DBXWsZiiXTZojf70ZRgbRQFigb3LoF7GUJC0C4Nbif6eWCGcQCM0zK
|
# frgZKeAnjDxNgLxsV0qZlJ3uBB2zBLRK1XWBJuThkWVbrdTQpkE2iM41ZS8aFe2P
|
# +87gMVwlUFT7cVdTn3ekgB+n0XGUm9wQEnQmOdFQDClskocuXy9WNgqeSBmzh+zI
|
# v2zgwa9bRqdwLC/1i80i+zmJRT5lmYiCefFXDck54zms5YmRZdGBeRqLZTjJW/Fq
|
# 3DqQtEr0BB7beIb0sYVBR4JudGE9MII9w2VYiGU+PabeaGdeFFFuyXWq/QrNkjor
|
# dPPV13jdGG//QjBgRN1sSPg2h8ovIaVwcoO9yenFucQTEBdoWRZVhqlWYwoLZrgE
|
# +wEGqfdcg6R061oxTF8Mjv+RnHmtx71GedAsowU6y/EOhprva6HSNgfM9MnvYlSd
|
# qT/khlJ59qA8Q0lfrVecdbSJ0L7ainXIAFMes53rcs2v8w+k++53/n3TTbv+Glz4
|
# ySDQCn0k8HpAPtS6Ifz1Rqc=
|
# SIG # End signature block
|