WAQS NuGet PowerShell command usage: init ViewModel using Roslyn

If you take a look to WCFAsyncQueryableFunctionsClientWPF.psm1 of WCFAsyncQueryableServices.Client.WPF NuGet package, you can see the PowerShell command used to initialize ViewModel.

After removing error and TFS management, this is the code of the powershell command to initialize the active document (ViewModel) and an optional associated View.

function WCFAsyncQueryableServicesApplyViewModelWPF($edmxName, $xamlFilePath)
    $project = ($DTE.Solution.FindProjectItem($DTE.ActiveDocument.FullName)).ContainingProject

    $edmxName = [System.Text.RegularExpressions.Regex]::Match($edmxName, ‘^\"?(.*?)\"?$’).Groups[1].Value
    $xamlFilePath = [System.Text.RegularExpressions.Regex]::Match($xamlFilePath, ‘^\"?(.*?)\"?$’).Groups[1].Value

    $toolsPath = GetToolsPath
    $defaultNamespace = ($project.Properties | ? {$_.Name -eq ‘RootNamespace’}).Value
    $activeDocumentPath = $DTE.ActiveDocument.FullName
    $clientVersion = "WPF"
    $toolsPath = Join-Path $toolsPath ("Client." + $clientVersion)
    $exePath = Join-Path $toolsPath "InitViewModel.exe"
    $waqsFilePath = ($DTE.Solution.FindProjectItem((Join-Path ([System.IO.Path]::GetDirectoryName($project.FullName)) (Join-Path ("WAQS." + $edmxName) ($edmxName + ".Client." + $clientVersion + ".waqs")))).Properties | ? {$_.Name -eq "LocalPath"}).Value
    $exeArgs = @(‘"’ + $edmxName + ‘"’, ‘"’ + $defaultNamespace + ‘"’, ‘"’ + $activeDocumentPath +‘"’, ‘"’ + $waqsFilePath + ‘"’, ‘"’ + $xamlFilePath + ‘"’)
    start-process -filepath $exePath -ArgumentList $exeArgs -Wait

Register-TabExpansion ‘WCFAsyncQueryableServicesApplyViewModelWPF’ @{ 
‘edmxName’ = { GetProjects | foreach {(GetAllProjectItems $_)} | ?{$_.Name.EndsWith(".edmx")} | select {[System.IO.Path]::GetFileNameWithoutExtension($_.Name)} | select -unique -ExpandProperty * | Sort-Object | foreach {‘"’ + $_ + ‘"’} };
‘xamlFilePath’ = { (GetAllProjectItems (Get-Project)) | ?{$_.Name.EndsWith(".xaml")} | foreach {$_.Properties | ?{$_.Name -eq ‘LocalPath’} | select -ExpandProperty Value} | Sort-Object | foreach {‘"’ + $_ + ‘"’} }

Export-ModuleMember WCFAsyncQueryableServicesApplyViewModelWPF


Note that, I’m a PowerShell beginner. So my code could probably be written with a better way.

Also note that the GetProjects and GetAllProjectItems (used for Intellisense) are PowerShell commands defined by WAQS.


In order to update safely the ViewModel and the View, WAQS uses Roslyn. But I prefer using C# than PowerShell to do it.

So WAQS NuGet package brings a Console Application (coded in C#) and the PowerShell command executes it with arguments.


With WAQS logic, you should have a WAQS configuration xml file in the ViewModel project that describes WAQS generation including namespaces. So, using EnvDTE, NuGet PowerShell command determines the path of this file ($waqsFilePath), the project default namespace ($defaultNamespace) (used if configuration file does not set namespaces) and the path of Active document ($activeDocumentPath).

The Console Application read the ViewModel / View file, parse it using SyntaxFactory.ParseCompilationUnit, use a specific CSharpSyntaxRewriter to transform it and write the text associated to the SyntaxTree in the file.

Note that SyntaxNode.ToString method does not generate a code that compiles because spaces are missing. So you have to use mySyntaxNode.NormalizeWhitespace().ToString().


See also: How does WAQS code generation work?

This entry was posted in 16868, 17894, 17895, 18440, 8606. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *