Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ public async Task<List<ISymbol>> GetAllClassSymbolsAsync()
{
EnsureInitialized();
List<ISymbol> classSymbols = [];
var compilations = new List<Compilation>();
if (_compilation is null)
{
// Explicitly use only the MSBuildWorkspace for compilation — the fallback
Expand All @@ -284,6 +285,18 @@ public async Task<List<ISymbol>> GetAllClassSymbolsAsync()
if (project is not null)
{
_compilation = await project.GetCompilationAsync();

// Also gather compilations for any referenced projects (e.g. a DbContext/model
// class library referenced via a ProjectReference) so that types declared there
// are discoverable, not just types in the main scaffolding project.
foreach (var referencedProject in GetTransitiveProjectReferences(project))
{
var referencedCompilation = await referencedProject.GetCompilationAsync();
if (referencedCompilation is not null)
{
compilations.Add(referencedCompilation);
}
}
}

// Fallback: MSBuildWorkspace can fail to open projects when the SDK resolver
Expand All @@ -296,27 +309,57 @@ public async Task<List<ISymbol>> GetAllClassSymbolsAsync()
}
}

List<ISymbol?>? compilationClassSymbols = _compilation?.SyntaxTrees.SelectMany(tree =>
if (_compilation is not null)
{
var model = _compilation.GetSemanticModel(tree);
return tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()
.Select(classSyntax => model.GetDeclaredSymbol(classSyntax))
.Where(classSymbol => classSymbol is not null &&
!classSymbol.MetadataName.StartsWith("<")); //if the metadata name starts with < it is a compiler generated class
})
.Append(_compilation.GetEntryPoint(CancellationToken.None)?.ContainingType)
.Distinct(SymbolEqualityComparer.Default)
.ToList();

compilationClassSymbols?.ForEach(x =>
compilations.Insert(0, _compilation);
}

foreach (var compilation in compilations)
{
if (x is not null)
var compilationClassSymbols = compilation.SyntaxTrees.SelectMany(tree =>
{
var model = compilation.GetSemanticModel(tree);
return tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()
.Select(classSyntax => model.GetDeclaredSymbol(classSyntax))
.Where(classSymbol => classSymbol is not null &&
!classSymbol.MetadataName.StartsWith("<")); //if the metadata name starts with < it is a compiler generated class
})
.ToList();

if (ReferenceEquals(compilation, _compilation))
{
classSymbols.Add(x);
var entryPointType = compilation.GetEntryPoint(CancellationToken.None)?.ContainingType;
if (entryPointType is not null)
{
compilationClassSymbols.Add(entryPointType);
}
}
});

return classSymbols;
compilationClassSymbols?.ForEach(x =>
{
if (x is not null)
{
classSymbols.Add(x);
}
});
}

return classSymbols
.Distinct(SymbolEqualityComparer.Default)
.ToList();
}

/// <summary>
/// Collects all Roslyn <see cref="Project"/> instances referenced (directly or
/// transitively) by <paramref name="project"/> via ProjectReference (e.g. a class library
/// containing a DbContext and/or model classes referenced from the main scaffolding project).
/// </summary>
private static IEnumerable<Project> GetTransitiveProjectReferences(Project project)
{
var dependencyGraph = project.Solution.GetProjectDependencyGraph();
return dependencyGraph.GetProjectsThatThisProjectTransitivelyDependsOn(project.Id)
.Select(project.Solution.GetProject)
.OfType<Project>();
}

/// <summary>
Expand Down