How do I use Roslyn with a Website project?

Viewed 374

In my Visual Studio Package I get the VisualStudioWorkspace as described by Josh Varty https://joshvarty.com/2014/09/12/learn-roslyn-now-part-6-working-with-workspaces/

Using the EnvDTE.Project object, I look for a matching Roslyn project in VisualStudioWorkspace.Projects by comparing the properties CodeAnalysis.Project.FilePath and EnvDTE.Project.FileName.

If I find a match, then I know that the project supports Roslyn and that I can find documents in the CodeAnalysis.Project.Documents collection. This has so far worked fine for C# and VB projects.

If I do not find a match, then it might be a C++ project, which does not support Roslyn.

However, I have now found that WebSite projects behave quite differently.

I have created a new WebSite project in Visual Studio 2019. Althouth the solution only contains one project, the VisualStudioWorkspace contains two CodeAnalysis.Project objects, as follows:

?VSWorkspace.CurrentSolution.Projects(0)
1_Default.aspx
    AdditionalDocumentIds: Count = 0
    AdditionalDocuments: {System.Linq.Enumerable.WhereSelectEnumerableIterator(Of Microsoft.CodeAnalysis.DocumentId, Microsoft.CodeAnalysis.TextDocument)}
    AllProjectReferences: Length = 1
    AnalyzerConfigDocuments: {System.Linq.Enumerable.WhereSelectEnumerableIterator(Of System.Collections.Generic.KeyValuePair(Of Microsoft.CodeAnalysis.DocumentId, Microsoft.CodeAnalysis.AnalyzerConfigDocumentState), Microsoft.CodeAnalysis.AnalyzerConfigDocument)}
    AnalyzerOptions: {Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions}
    AnalyzerReferences: Length = 0
    AssemblyName: "1_Default"
    CompilationOptions: {Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions}
    DefaultNamespace: ""
    DocumentIds: Count = 2
    Documents: {System.Linq.Enumerable.WhereSelectEnumerableIterator(Of Microsoft.CodeAnalysis.DocumentId, Microsoft.CodeAnalysis.Document)}
    FilePath: Nothing
    HasDocuments: True
    Id: (ProjectId, #ca6b1f58-967a-4c31-a874-71d7720dd972 - 1_Default.aspx)
    IsSubmission: False
    Language: "C#"
    LanguageServices: {Microsoft.CodeAnalysis.Host.Mef.MefLanguageServices}
    MetadataReferences: Length = 56
    Name: "1_Default.aspx"
    OutputFilePath: Nothing
    OutputRefFilePath: Nothing
    ParseOptions: {Microsoft.CodeAnalysis.CSharp.CSharpParseOptions}
    ProjectReferences: {System.Linq.Enumerable.WhereEnumerableIterator(Of Microsoft.CodeAnalysis.ProjectReference)}
    Solution: {Microsoft.CodeAnalysis.Solution}
    SupportsCompilation: True
    Version: {2019-10-13T06:46:42.2691386Z-10141-0}
?VSWorkspace.CurrentSolution.Projects(1)
2_App_Code
    AdditionalDocumentIds: Count = 0
    AdditionalDocuments: {System.Linq.Enumerable.WhereSelectEnumerableIterator(Of Microsoft.CodeAnalysis.DocumentId, Microsoft.CodeAnalysis.TextDocument)}
    AllProjectReferences: Length = 0
    AnalyzerConfigDocuments: {System.Linq.Enumerable.WhereSelectEnumerableIterator(Of System.Collections.Generic.KeyValuePair(Of Microsoft.CodeAnalysis.DocumentId, Microsoft.CodeAnalysis.AnalyzerConfigDocumentState), Microsoft.CodeAnalysis.AnalyzerConfigDocument)}
    AnalyzerOptions: {Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions}
    AnalyzerReferences: Length = 0
    AssemblyName: "App_Code.yy7qumxk"
    CompilationOptions: {Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions}
    DefaultNamespace: ""
    DocumentIds: Count = 5
    Documents: {System.Linq.Enumerable.WhereSelectEnumerableIterator(Of Microsoft.CodeAnalysis.DocumentId, Microsoft.CodeAnalysis.Document)}
    FilePath: Nothing
    HasDocuments: True
    Id: (ProjectId, #12ab68ac-7b29-4252-87c2-67dc2f3d0696 - 2_App_Code)
    IsSubmission: False
    Language: "C#"
    LanguageServices: {Microsoft.CodeAnalysis.Host.Mef.MefLanguageServices}
    MetadataReferences: Length = 53
    Name: "2_App_Code"
    OutputFilePath: Nothing
    OutputRefFilePath: Nothing
    ParseOptions: {Microsoft.CodeAnalysis.CSharp.CSharpParseOptions}
    ProjectReferences: {System.Linq.Enumerable.WhereEnumerableIterator(Of Microsoft.CodeAnalysis.ProjectReference)}
    Solution: {Microsoft.CodeAnalysis.Solution}
    SupportsCompilation: True
    Version: {2019-10-13T06:46:41.6118955Z-10139-2}

First of all, the FilePath property in both projects is null, so I was not able to identify the project by its filename.

The two projects contain a total of seven documents, which are:

"C:\DotNetTestPrograms\2019\CS\WebSite1\Default.aspx.cs"
"C:\DotNetTestPrograms\2019\CS\WebSite1\Default.aspx"

"C:\DotNetTestPrograms\2019\CS\WebSite1\App_Code\BundleConfig.cs"
"C:\DotNetTestPrograms\2019\CS\WebSite1\App_Code\IdentityModels.cs"
"C:\DotNetTestPrograms\2019\CS\WebSite1\App_Code\RouteConfig.cs"
"C:\DotNetTestPrograms\2019\CS\WebSite1\App_Code\Startup.Auth.cs"
"C:\DotNetTestPrograms\2019\CS\WebSite1\App_Code\Startup.cs"

This is not a complete list of documents in the project. The project contains seven additional-code behind files:

Account\Login.apsx.cs
Account\Manage.apsx.cs
Account\OpenAuthProviders.ascx.cs
Account\Register.apsx.cs
Account\RegisterExternalLogin.apsx.cs
Site.master.cs
ViewSwitcher.ascx.cs

My question is, can I work with Roslyn in a WebSite project?

Clearly, there is not a one-to-one relationship between EnvDTE and CodeAnalysis projects. I guess that I could work around that.

But what about the missing files? Are there not Roslyn documents for the seven additional code-behind files?

1 Answers

I finally looked at this problem again and I have found the following solution.

This method only works if the document is open in Visual Studio. Therefore I start of with

projectItem.Open ( EnvDTE.Constants.vsViewKindTextView ) ;

(I already have the project item in my function, so this is easy.)

Then, with the full path to the file (FullPath) and the VisualStudioWorkspace (VSWorkspace), I can get the Roslyn Document with

var docid = VSWorkspace.CurrentSolution.GetDocumentIdsWithFilePath(FullPath).FirstOrDefault() ;
if ( docid != null )
{
  var RoslynDoc = VSWorkspace.CurrentSolution.GetDocument ( docid ) ;
}

If the document was not already open, it would probably be good practice to close it again, when you are finished. You can determine whether the document was previously open with

var wasOpen = projectItem.IsOpen ( EnvDTE.Constants.vsViewKindTextView ) ;

By the way, opening the document like this does not make the window visible in Visual Studio (which in my application is fine). If you want to make it visible you can open it like this:

var wnd = projectItem.Open ( EnvDTE.Constants.vsViewKindTextView ) ;
wnd.Visible = true ;
Related