From f0bfbcbc7c9c317df460dc700d70542f95eef34e Mon Sep 17 00:00:00 2001 From: IAFahim Date: Thu, 1 Oct 2026 18:49:41 +0600 Subject: [PATCH 1/2] fix(generator): handle types declared in the global namespace ContainingNamespace.ToDisplayString() returns the literal string "" for types declared at global scope (e.g. a DbContext defined in a top-level Program.cs). It was stored in DbContextInfo.Namespace/EntityInfo.Namespace and flowed into emitted source and generator hint names, producing: warning CS8785: Generator 'MapperGenerator' failed to generate source ... The hintName '.MyDbContext_Program. Mappers.g.cs' contains an invalid character '<' and, had the hint name not thrown first, invalid emitted C# like "namespace .X" / "using ;". Map the global namespace to string.Empty via a new SyntaxHelper.GetNamespaceOrEmpty() helper at all three assignment sites, emit the DbContext partial class without a namespace wrapper when the namespace is empty, and omit the empty prefix from the mapper namespace and hint name. Existing IsNullOrEmpty(Namespace) guards (filter namespace fallback, [DocumentMapper] pipeline) now behave as intended for global types too. --- src/BLite.SourceGenerators/EntityAnalyzer.cs | 4 +-- .../Helpers/SyntaxHelper.cs | 12 ++++++++ src/BLite.SourceGenerators/MapperGenerator.cs | 28 ++++++++++++++----- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/BLite.SourceGenerators/EntityAnalyzer.cs b/src/BLite.SourceGenerators/EntityAnalyzer.cs index da8b3028..9946080a 100644 --- a/src/BLite.SourceGenerators/EntityAnalyzer.cs +++ b/src/BLite.SourceGenerators/EntityAnalyzer.cs @@ -14,7 +14,7 @@ public static EntityInfo Analyze(INamedTypeSymbol entityType, SemanticModel sema var entityInfo = new EntityInfo { Name = entityType.Name, - Namespace = entityType.ContainingNamespace.ToDisplayString(), + Namespace = SyntaxHelper.GetNamespaceOrEmpty(entityType), FullTypeName = SyntaxHelper.GetFullName(entityType), CollectionName = entityType.Name.ToLowerInvariant() + BLiteConventions.DefaultCollectionNameSuffix }; @@ -342,7 +342,7 @@ private static void AnalyzeNestedTypesRecursive( var nestedInfo = new NestedTypeInfo { Name = simpleName, - Namespace = nestedTypeSymbol.ContainingNamespace.ToDisplayString(), + Namespace = SyntaxHelper.GetNamespaceOrEmpty(nestedTypeSymbol), FullTypeName = fullTypeName, Depth = currentDepth }; diff --git a/src/BLite.SourceGenerators/Helpers/SyntaxHelper.cs b/src/BLite.SourceGenerators/Helpers/SyntaxHelper.cs index 9f29e79e..55213001 100644 --- a/src/BLite.SourceGenerators/Helpers/SyntaxHelper.cs +++ b/src/BLite.SourceGenerators/Helpers/SyntaxHelper.cs @@ -100,6 +100,18 @@ public static string GetFullName(INamedTypeSymbol symbol) .Replace("global::", ""); } + /// + /// Returns the containing namespace's display string, or an empty string when the + /// symbol lives in the global namespace. + /// returns the literal text "<global namespace>" for global types, which produces + /// invalid source and invalid generator hint names if used directly. + /// + public static string GetNamespaceOrEmpty(ISymbol symbol) + { + var ns = symbol.ContainingNamespace; + return ns is { IsGlobalNamespace: false } ? ns.ToDisplayString() : string.Empty; + } + public static string GetTypeName(ITypeSymbol type) { if (type is INamedTypeSymbol namedType && diff --git a/src/BLite.SourceGenerators/MapperGenerator.cs b/src/BLite.SourceGenerators/MapperGenerator.cs index 2daedf3b..7e7cfa7f 100644 --- a/src/BLite.SourceGenerators/MapperGenerator.cs +++ b/src/BLite.SourceGenerators/MapperGenerator.cs @@ -126,7 +126,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context) sb.AppendLine(); // Use safeName (Context + Filename) to avoid collisions - var mapperNamespace = $"{dbContext.Namespace}.{safeName}_Mappers"; + var mapperNamespace = string.IsNullOrEmpty(dbContext.Namespace) + ? $"{safeName}_Mappers" + : $"{dbContext.Namespace}.{safeName}_Mappers"; sb.AppendLine($"namespace {mapperNamespace}"); sb.AppendLine($"{{"); @@ -169,8 +171,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // Partial DbContext for InitializeCollections (Only for top-level partial classes) if (!dbContext.IsNested && dbContext.IsPartial) { - sb.AppendLine($"namespace {dbContext.Namespace}"); - sb.AppendLine($"{{"); + // Global-namespace DbContexts (e.g. declared in a top-level Program.cs) + // emit the partial class without a namespace wrapper. + var wrapNamespace = !string.IsNullOrEmpty(dbContext.Namespace); + if (wrapNamespace) + { + sb.AppendLine($"namespace {dbContext.Namespace}"); + sb.AppendLine($"{{"); + } sb.AppendLine($" public partial class {dbContext.ClassName}"); sb.AppendLine($" {{"); sb.AppendLine($" protected override void InitializeCollections()"); @@ -289,10 +297,16 @@ public void Initialize(IncrementalGeneratorInitializationContext context) } sb.AppendLine($" }}"); - sb.AppendLine($"}}"); + if (wrapNamespace) + { + sb.AppendLine($"}}"); + } } - - spc.AddSource($"{dbContext.Namespace}.{safeName}.Mappers.g.cs", sb.ToString()); + + var hintName = string.IsNullOrEmpty(dbContext.Namespace) + ? $"{safeName}.Mappers.g.cs" + : $"{dbContext.Namespace}.{safeName}.Mappers.g.cs"; + spc.AddSource(hintName, sb.ToString()); }); // ── Filter pipeline: emit exactly one {Entity}Filter per unique entity type ── @@ -808,7 +822,7 @@ private static string EscapeString(string s) var info = new DbContextInfo { ClassName = classSymbol.Name, - Namespace = classSymbol.ContainingNamespace.ToDisplayString(), + Namespace = SyntaxHelper.GetNamespaceOrEmpty(classSymbol), FilePath = classDecl.SyntaxTree.FilePath, IsNested = classSymbol.ContainingType != null, IsPartial = classDecl.Modifiers.Any(m => m.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.PartialKeyword)), From 6032adaf6adf71966cd98828f485b19283c9d771 Mon Sep 17 00:00:00 2001 From: IAFahim Date: Thu, 1 Oct 2026 18:49:41 +0600 Subject: [PATCH 2/2] docs(readme): add missing usings to quickstart snippet The Basic Usage sample uses ObjectId (BLite.Bson), DocumentCollection/UpsertResult (BLite.Core.Collections) and async LINQ extensions (BLite.Core.Query), none of which are in scope with only 'using BLite.Core;'. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 3929ca8b..0ca4b40c 100644 --- a/README.md +++ b/README.md @@ -680,6 +680,11 @@ dotnet add package BLite ### 2. Basic Usage ```csharp +using BLite.Bson; // ObjectId +using BLite.Core; // DocumentDbContext +using BLite.Core.Collections; // DocumentCollection, UpsertResult +using BLite.Core.Query; // async LINQ extensions (ToListAsync, ...) + // 1. Define your Entities public class User {