# Ensure we're on the right version of the tooling. If not, there's nothing for us to do here. $toolingExists = [System.AppDomain]::CurrentDomain.GetAssemblies() | ?{ $_.GetType("Microsoft.VisualStudio.Web.Mvc.Scaffolding.ScaffolderProviders") } if (!$toolingExists) { return } # Todo: Scope the following to this module if possible function global:MvcScaffoldingHashTableToPsObject($hashOfScriptMethods) { $result = New-Object PSObject $hashOfScriptMethods.Keys | %{ Add-Member -InputObject $result -Member ScriptMethod -Name $_ -Value $hashOfScriptMethods[$_] } $result } function global:MvcScaffoldingInvokeViaScriptExecutor($scriptToExecute) { $completeScript = @" try { # Activate output pane `$packageManagerOutputPaneGuid = "{CEC55EC8-CC51-40E7-9243-57B87A6F6BEB}" `$dteService = [NuGet.VisualStudio.ServiceLocator].GetMethods() | ?{ `$_.Name -eq 'GetInstance' } | %{ `$_.MakeGenericMethod([Microsoft.VisualStudio.Shell.Interop.SDTE]).Invoke(`$null, [Array]`$null) } `$outputWindow = `$dteService.Windows.Item([EnvDTE.Constants]::vsWindowKindOutput) `$packageManagerOutputPane = `$outputWindow.Object.OutputWindowPanes.Item(`$packageManagerOutputPaneGuid) `$packageManagerOutputPane.Clear() `$packageManagerOutputPane.Activate() `$outputWindow.Activate() # Invoke requested script $scriptToExecute } catch { [System.Windows.Forms.MessageBox]::Show("An error occurred during scaffolding:`n`n`$(`$_.Exception.ToString())`n`nYou may need to upgrade to a newer version of MvcScaffolding.", "Scaffolding error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error) | Out-Null } "@ try { # Write the script to disk $tempDir = Join-Path $env:Temp ([System.Guid]::NewGuid()) $tempScriptFilename = Join-Path $tempDir "tools\tempScript.ps1" md $tempdir md ([System.IO.Path]::GetDirectoryName($tempScriptFilename)) Set-Content -Path $tempScriptFilename -Value $completeScript # Ensure we're on the right NuGet version $scriptExecutorExists = [System.AppDomain]::CurrentDomain.GetAssemblies() | ?{ $_.GetType("NuGet.VisualStudio.IScriptExecutor") } if (!$scriptExecutorExists) { [System.Windows.Forms.MessageBox]::Show("Sorry, this operation requires NuGet 1.2 or later.", "Scaffolding error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error) | Out-Null return } # Invoke via IScriptExecutor, then clean up $scriptExecutor = [NuGet.VisualStudio.ServiceLocator].GetMethods() | ?{ $_.Name -eq 'GetInstance' } | %{ $_.MakeGenericMethod([NuGet.VisualStudio.IScriptExecutor]).Invoke($null, [Array]$null) } $scriptExecutor.Execute($tempDir, "tempScript.ps1", $null, $null, (New-Object NuGet.NullLogger)) rmdir $tempdir -Force -Recurse } catch { [System.Windows.Forms.MessageBox]::Show("An error occurred during scaffolding:`n`n$($_.Exception.ToString())`n`nYou may need to upgrade to a newer version of MvcScaffolding.", "Scaffolding error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error) | Out-Null } } $mvcScaffoldingProvider = global:MvcScaffoldingHashTableToPsObject @{ ID = { "{9EC893D9-B925-403C-B785-A50545149521}" }; GetControllerScaffolders = { param($project) $allControllerScaffolders = Get-Scaffolder -Project $project.Name -IncludeHidden | ?{ $_.ScaffolderAttribute -is [T4Scaffolding.ControllerScaffolderAttribute] } if (!$allControllerScaffolders) { return @() } $result = $allControllerScaffolders | %{ global:MvcScaffoldingHashTableToPsObject @{ ID = { $_.Name }.GetNewClosure(); DisplayName = { "MvcScaffolding: " + $_.ScaffolderAttribute.DisplayName }.GetNewClosure(); SupportsModelType = { $_.ScaffolderAttribute.SupportsModelType }.GetNewClosure(); SupportsDataContextType = { $_.ScaffolderAttribute.SupportsDataContextType }.GetNewClosure(); ViewsScaffolders = { if (!$_.ScaffolderAttribute.SupportsViewScaffolder) { return @() } $viewScaffolderSelector = $_.ScaffolderAttribute.ViewScaffolderSelector if (!$viewScaffolderSelector) { $viewScaffolderSelector = [T4Scaffolding.ViewScaffolderAttribute] } $viewScaffolders = Get-Scaffolder -Project $project.Name -IncludeHidden | ?{ $viewScaffolderSelector.IsAssignableFrom($_.ScaffolderAttribute.GetType()) } # Put default view engine at the top of the list so it's the default selection until you choose otherwise $defaultViewScaffolder = (Get-DefaultScaffolder View).ScaffolderName $viewScaffolders = $viewScaffolders | Sort-Object { if($_.Name -eq $defaultViewScaffolder) { "" } else { $_.Name } } $result = $viewScaffolders | %{ global:MvcScaffoldingHashTableToPsObject @{ ID = { $_.Name }.GetNewClosure(); DisplayName = { $_.ScaffolderAttribute.DisplayName }.GetNewClosure(); LayoutPageFilter = { $_.ScaffolderAttribute.LayoutPageFilter }.GetNewClosure(); } } return ,[Array]$result }.GetNewClosure(); Execute = { param($container, $controllerName, $modelType, $dataContextType, $viewsScaffolder, $options) # Infer possible area name from container location $areaName = $null if ($container -is [EnvDTE.ProjectItem]) { $containerNamespace = $container.Properties.Item("DefaultNamespace").Value $areaMatch = [System.Text.RegularExpressions.Regex]::Match($containerNamespace, "(^|\.)Areas\.(.*)\.Controllers($|\.)") $areaName = if ($areaMatch.Success) { $areaMatch.Groups[2].Value } } $scriptToExecute = @" # These are all the args we may pass to the target scaffolder... `$possibleArgs = @{ ControllerName = `"$controllerName`"; ModelType = `"$modelType`"; DbContextType = `"$dataContextType`"; Project = `"$($project.Name)`"; Area = $(if($areaName) { "`"" + $areaName + "`"" } else { "`$null" }); ViewScaffolder = $(if($viewsScaffolder) { "`"" + $viewsScaffolder.ID + "`"" } else { "`$null" }); Force = $(if($options.OverwriteViews -or $options.OverwriteController) { "`$true" } else { "`$false" }); ForceMode = $(if($options.OverwriteViews -and $options.OverwriteController) { "`$null" } else { if($options.OverwriteViews) { "`"PreserveController`"" } else { "`"ControllerOnly`"" } }); Layout = $(if($options.UseLayout) { "`"" + $options.Layout + "`"" } else { "`$null" }); PrimarySectionName = $(if($options.PrimarySectionName) { "`"" + $options.PrimarySectionName + "`"" } else { "`$null" }); ReferenceScriptLibraries = $(if($options.ReferenceScriptLibraries) { "`$true" } else { "`$false" }); } # ... but we only pass the ones it actually accepts `$actualArgs = @{} `$acceptedParameterNames = (Get-Command Invoke-Scaffolder -ArgumentList @(`"$($_.Name)`")).Parameters.Keys `$acceptedParameterNames | ?{ `$possibleArgs.ContainsKey(`"`$_`") } | %{ `$actualArgs.Add(`$_, `$possibleArgs[`$_]) } Invoke-Scaffolder `"$($_.Name)`" @actualArgs "@ global:MvcScaffoldingInvokeViaScriptExecutor $scriptToExecute | Out-Null # Trick PowerShell into not unrolling the return collection by wrapping it in a further collection $result = [System.Activator]::CreateInstance(([System.Collections.Generic.List``1].MakeGenericType([System.Object]))) $result.Add(([System.Activator]::CreateInstance(([System.Collections.Generic.List``1].MakeGenericType([EnvDTE.ProjectItem]))))) return $result }.GetNewClosure(); } } return ,[Array]$result } } # Need to register with each ScaffolderProviders type # There can be multiple, one per loaded ASP.NET MVC tooling DLL, if your VS instance has multiple versions of ASP.NET MVC tooling loaded $scaffolderProviderTypes = [System.AppDomain]::CurrentDomain.GetAssemblies() | %{ $_.GetType("Microsoft.VisualStudio.Web.Mvc.Scaffolding.ScaffolderProviders") } | ?{ $_ } $scaffolderProviderTypes | %{ $allProviders = $_.GetProperty("Providers").GetValue($null, $null) # Remove existing MvcScaffolding providers $existingMvcScaffoldingProviders = $allProviders | ?{ $_.ID -eq $mvcScaffoldingProvider.ID() } $existingMvcScaffoldingProviders | %{ $allProviders.Remove($_) } | Out-Null # Add new provider $powerShellScaffolderProviderType = $_.Assembly.GetType("Microsoft.VisualStudio.Web.Mvc.Scaffolding.PowerShell.PowerShellScaffolderProvider") $newProvider = New-Object $powerShellScaffolderProviderType($mvcScaffoldingProvider) $allProviders.Add($newProvider) | Out-Null }