Microlens.Synthesizer.Core
2.0.1
dotnet add package Microlens.Synthesizer.Core --version 2.0.1
NuGet\Install-Package Microlens.Synthesizer.Core -Version 2.0.1
<PackageReference Include="Microlens.Synthesizer.Core" Version="2.0.1" />
<PackageVersion Include="Microlens.Synthesizer.Core" Version="2.0.1" />
<PackageReference Include="Microlens.Synthesizer.Core" />
paket add Microlens.Synthesizer.Core --version 2.0.1
#r "nuget: Microlens.Synthesizer.Core, 2.0.1"
#:package Microlens.Synthesizer.Core@2.0.1
#addin nuget:?package=Microlens.Synthesizer.Core&version=2.0.1
#tool nuget:?package=Microlens.Synthesizer.Core&version=2.0.1
Microlens.Synthesizer.Core
Generate compilable Bogus Faker<T> classes directly from C# classes and records, using the Roslyn semantic model — no runtime type inspection, no hand-written boilerplate.
Microlens.Synthesizer.Core is a Roslyn-based Faker<T> generation engine for .NET that analyzes, maps and emits Bogus fakers directly from a SemanticModel.
Unlike reflection-based fake-data generators that build values at runtime, Microlens.Synthesizer.Core emits an actual Faker<T> class as C# source — reviewable, debuggable, and just as fast as a hand-written one, because it is one.
What's New | Why Microlens.Synthesizer.Core? | Visual Studio Extension | Requirements | Quick Start | Quick Example | Features | Extensible Architecture | Comparison | Limitations | When Not To Use Microlens.Synthesizer.Core | License
What's New
- Inherited properties: Base-class properties are now discovered and emitted base-first. A hiding or overriding property replaces its base.
- Generic source types:
Box<T>now producesBoxFaker<T> : Faker<Box<T>>, with type parameter constraints carried over. - Accessibility-aware generation: The Faker mirrors the source type's accessibility (
public/internal). Only properties whose getter is accessible from the source assembly are emitted. - Syntax validation: Every generated Faker is parsed before it is returned. A syntax error, for example from a custom provider expression, throws
ApplicationExceptioninstead of producing a broken file. - Provider helpers:
ITypeSymbol.ToDisplayName()andITypeSymbol.GetReferencedNamespaces()are now public for custom providers. - Changed: Convention-based
stringmapping moved into the built-instringprovider. A customstringprovider registered beforeAddCoreServices()now replaces conventions entirely. - Changed:
DateTimeProvider,DateTimeOffsetProviderandGuidProviderno longer implementINamespaceProvider. Ausingdirective is emitted only when the generated code names a type from that namespace. - Fixed: Types in the global namespace produced an invalid
namespaceblock. - Fixed:
internalsource types producedpublicFakers, causing compiler error CS0060. - Fixed:
private,protectedand write-only properties produced non-compilable rules. - Fixed: Nested enum property types were emitted without their containing type.
- Fixed: Static classes, and classes nested inside structs, could be selected as the target type.
- Fixed: Keyword identifiers (e.g.
event) were emitted unescaped. - Fixed:
GuidandDateTimeOffsetdetection matched any namespace namedSystem, case-insensitively. - Fixed: A redundant
using System;was emitted for scalarDateTime,DateTimeOffsetandGuidproperties.
Why Microlens.Synthesizer.Core?
Most Faker<T> classes are written and maintained by hand, one RuleFor at a time, and quietly drift out of sync the moment the source class changes.
Examples of typical use cases:
- Test Data Setup: Generate realistic seed data for unit and integration tests without hand-authoring every property.
- Contract Prototyping: Produce sample payloads for a new
DTOthe moment it's defined. - Consistent Test Fixtures: Keep Fakers in sync with their source class by regenerating instead of manually patching them.
- Onboarding New Types: Every new entity or
DTOgets a matching Faker with zero extra effort.
Visual Studio Extension
Most users won't need this package directly — the companion Visual Studio extension generates and saves Faker<T> files for you, right from Solution Explorer.
Generate Bogus Fakers extension is available for Visual Studio 2019, and 2022 / 2026.
Use Microlens.Synthesizer.Core directly only when you need the same generation logic outside Visual Studio — a build step, a source generator, or your own tooling.
Requirements
- Target framework:
netstandard2.0 Microsoft.CodeAnalysis.CSharp4.14.0or laterMicrosoft.Extensions.DependencyInjection9.0.20or later- The project that compiles the generated Fakers must reference
Bogus.
When used inside a source generator:
- The host compiler must be
Roslyn 4.14or later (Visual Studio 17.14 / .NET SDK 9.0.300 or later). - Analyzer packages do not flow NuGet dependencies, so
Microlens.Synthesizer.CoreandMicrosoft.Extensions.DependencyInjectionmust be packed alongside the generator.
Quick Start
Installation
dotnet add package Microlens.Synthesizer.Core
Implement IOptions
Microlens.Synthesizer.Core ships no default IOptions implementation. Default values are exposed as constants on Registry.
using Microlens.Synthesizer.Core.Options;
using Microlens.Synthesizer.Core.Shared;
public sealed class GeneratorOptions : IOptions {
public bool OverwriteExisting => Registry.OptionsOverwriteExistingDefaultValue;
public int ElementCount => Registry.OptionsElementCountDefaultValue;
public string FakerSuffix => Registry.OptionsFakerSuffixDefaultValue;
public string LogFilePath => Registry.OptionsLogFilePathDefaultValue;
public Registry.LogWritingMode LogWritingMode => Registry.OptionsLogWritingModeDefaultValue;
}
| Member | Consumed By |
|---|---|
ElementCount |
Array, List<T>, HashSet<T> and Dictionary<TKey, TValue> rules |
FakerSuffix |
Generated class name (IBogusGenerator) and output file name (IFileWriter) |
OverwriteExisting |
IFileWriter only |
LogFilePath, LogWritingMode |
Not read by Microlens.Synthesizer.Core; available to the host for its own logging |
Register Services
using Microlens.Synthesizer.Core.Extensions;
using Microlens.Synthesizer.Core.Options;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
services.AddCoreServices();
services.AddSingleton<IOptions, GeneratorOptions>();
using var provider = services.BuildServiceProvider();
Analyze And Generate
The SemanticModel must come from a compilation with metadata references. Unresolved types become error types and are emitted as // TODO:.
using System;
using System.IO;
using Microlens.Synthesizer.Core.Analyzers;
using Microlens.Synthesizer.Core.Generators;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
string[] paths = ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")!).Split(Path.PathSeparator);
var references = new MetadataReference[paths.Length];
for (int i = 0; i < paths.Length; i++) {
references[i] = MetadataReference.CreateFromFile(paths[i]);
}
SyntaxTree tree = CSharpSyntaxTree.ParseText(File.ReadAllText("Customer.cs"));
CSharpCompilation compilation = CSharpCompilation.Create("Scratch", [tree], references);
SemanticModel model = compilation.GetSemanticModel(tree);
var metadata = provider.GetRequiredService<IClassAnalyzer>().Analyze(model);
string fakerSource = provider.GetRequiredService<IBogusGenerator>().Generate(metadata);
fakerSource is a complete Faker<T> class as a string. Inside Visual Studio or a source generator, use the SemanticModel of the existing compilation instead.
Persist (Optional)
using Microlens.Synthesizer.Core.Persistence;
var writer = provider.GetRequiredService<IFileWriter>();
bool written = writer.TryWrite("Customer.cs", outputDirectory, fakerSource, out string outputPath);
- The output file is
{source file name}{FakerSuffix}.csinoutputDirectory. TryWritereturnsfalsewhen the file exists andOverwriteExistingisfalse.- Any other I/O failure throws
ApplicationException.
Quick Example
Given a class:
using System.Collections.Generic;
namespace YourNamespace {
public class Customer {
public string FirstName { get; set; }
public string Email { get; set; }
public int Age { get; set; }
public List<string> Tags { get; set; }
}
}
Microlens.Synthesizer.Core analyzes it through Roslyn and emits (ElementCount = 3):
using Bogus;
using System.Collections.Generic;
namespace YourNamespace
{
public class CustomerFaker : Faker<Customer>
{
public CustomerFaker()
{
RuleFor(x => x.FirstName, f => f.Name.FirstName());
RuleFor(x => x.Email, f => f.Internet.Email());
RuleFor(x => x.Age, f => f.Random.Int());
RuleFor(x => x.Tags, f => new List<string>(f.Make(3, () => f.Name.FullName())));
}
}
}
- No manually written
RuleForcalls. - No stale Fakers after a property is renamed — just regenerate.
Features
Semantic-Model-Based Analysis
Type and property discovery runs against a Roslyn SemanticModel:
- Target: The first non-static, top-level
classorrecord classin the file.partialdeclarations across files are merged. - Properties:
- Instance, non-indexer properties with a getter and a
setorinitaccessor. - The getter must be accessible from the source assembly (
public,internal,protected internal). - Inherited properties are included, emitted base-first; a hiding or overriding property replaces its base.
- Instance, non-indexer properties with a getter and a
- Nullable value types:
int?,DateTime?,Status?resolve as their underlying type.
Faithful Output Shape
- Accessibility: The Faker mirrors the source type —
publicorinternal. - Generic types: Type parameters and constraints are carried over:
public class BoxFaker<T> : Faker<Box<T>> where T : class
- Global namespace: Types without a namespace produce a Faker without a namespace block.
- Keywords: Keyword identifiers are escaped (
x => x.@event). - Nested types: Nested enums and other nested types are emitted with their containing type (
Order.Status).
Supported Types
| Type | Emitted |
|---|---|
bool |
f.Random.Bool() |
char |
f.Random.Char() |
byte |
f.Random.Byte() |
short |
f.Random.Short() |
int |
f.Random.Int() |
long |
f.Random.Long() |
float |
f.Random.Float() |
double |
f.Random.Double() |
decimal |
f.Finance.Amount() |
string |
Convention factory, else f.Name.FullName() |
DateTime |
f.Date.Past() |
DateTimeOffset |
f.Date.PastOffset() |
Guid |
f.Random.Guid() |
enum |
f.PickRandom<TEnum>() |
T[], List<T>, HashSet<T> |
f.Make(ElementCount, …) |
Dictionary<TKey, TValue> |
ElementCount key/value assignments |
Convention-Based String Mapping
string properties — and string elements of collections and dictionaries — are matched by the property name's suffix, case-insensitive, singular or plural (ContactEmail, Emails):
| Suffix | Emitted |
|---|---|
Email |
f.Internet.Email() |
PhoneNumber, Phone, Mobile |
f.Phone.PhoneNumber() |
FirstName, GivenName |
f.Name.FirstName() |
LastName, Surname, FamilyName |
f.Name.LastName() |
UserName, Username |
f.Internet.UserName() |
Url, Website |
f.Internet.Url() |
PostalCode, ZipCode, Zip |
f.Address.ZipCode() |
City |
f.Address.City() |
Country |
f.Address.Country() |
CompanyName, Company |
f.Company.CompanyName() |
Mapping is performed by the built-in string provider, so a custom string provider registered ahead of it replaces conventions entirely.
Collection & Dictionary Support
T[], List<T>, HashSet<T> and Dictionary<TKey, TValue> are generated element-by-element using the same provider resolution as scalar properties, including nested collections of supported types.
Namespace-Aware Output
A using directive is emitted only when the generated code names a type from that namespace. Collection element types, enums and custom provider namespaces are included; the source type's own namespace and duplicates are skipped.
Syntax Validation
Every generated Faker is parsed before it is returned. A syntax error — for example from a custom provider expression — throws ApplicationException instead of producing a broken file.
Graceful Fallback
A property that no provider can resolve is emitted as a comment, so nothing is guessed and nothing is dropped silently:
// TODO: Address (Address)
Extensible Architecture
Register:
- Custom
IDataTypeProviderimplementations - Custom
IOptionsimplementations
Custom Type Providers
Example: Custom Provider
Assuming Contoso.Billing.Money exposes Money(decimal amount, string currency):
using System.Collections.Generic;
using Microlens.Synthesizer.Core.Domain;
using Microlens.Synthesizer.Core.Extensions;
using Microlens.Synthesizer.Core.Providers;
public sealed class MoneyProvider : IDataTypeProvider, INamespaceProvider {
public bool CanHandle(PropertyMetadata metadata) {
return metadata.Type.Name == "Money" && metadata.Type.ContainingNamespace.ToDisplayString() == "Contoso.Billing";
}
public string Generate(PropertyMetadata metadata) {
return "new Money(f.Finance.Amount(), f.Finance.Currency().Code)";
}
public IEnumerable<string> GetRequiredNamespaces(PropertyMetadata metadata) {
return metadata.Type.GetReferencedNamespaces();
}
}
Generate returns the expression placed after f =>. Implement INamespaceProvider whenever that expression names a type.
Register: Custom Provider
services.AddSingleton<IDataTypeProvider, MoneyProvider>();
Resolution rules:
- Providers are evaluated in registration order — the first
CanHandlematch wins. - A provider registered before
AddCoreServices()overrides built-in handling, includingstringconventions. - A provider registered after
AddCoreServices()only receives types no built-in provider handles. - Register providers as singletons — the property generator is a singleton.
- Custom types inside supported collections and dictionaries (
List<Money>) resolve through the same providers.
Helpers
| Extension | Purpose |
|---|---|
ITypeSymbol.ToDisplayName() |
Minimally qualified type name, as used in generated code. |
ITypeSymbol.GetReferencedNamespaces() |
Namespaces required to name the type, including type arguments and array elements. |
Comparison
| Capability | Microlens.Synthesizer.Core |
Manual Faker<T> Authoring |
|---|---|---|
| Property discovery (including inherited) | Automatic, via SemanticModel |
Manual |
Convention-based mapping (Email, PhoneNumber, etc.) |
Yes | Manual |
| Collection / dictionary element rules | Generated automatically | Manual |
| Generic source types | Type parameters and constraints carried over | Manual |
Namespace resolution for generated using directives |
Automatic | Manual |
| Stays current after a class changes | Regenerate on demand | Manual re-sync |
| Unsupported property handling | Marked // TODO: |
N/A |
| Custom type support | Extensible via IDataTypeProvider |
Hand-written |
Microlens.Synthesizer.Core focuses on generating ordinary Faker<T> source — the output is a normal class you own, not a runtime-only construct.
Limitations
- One type per file: Only the first non-static, top-level class or record class is processed; nested types cannot be targeted.
- Same assembly: Generated Fakers are assumed to compile into the source type's assembly (
internalmembers are included). - Unsupported types (emitted as
// TODO:):sbyte,ushort,uint,ulongTimeSpan,DateOnly,TimeOnly,Uri,object- Type-parameter-typed properties (
T) - Complex types (no child Faker composition)
IEnumerable<T>,IList<T>,ICollection<T>,IReadOnly*,IDictionary<TKey, TValue>- Collections of nullable elements (
List<int?>)
- Runtime instantiation:
Faker<T>uses a parameterless constructor by default. Abstract classes, positional records and types without one compile, but requireCustomInstantiatorbeforeGenerate(). - Unsupported declarations:
structandrecord structare not supported. - Collisions:
HashSet<T>andDictionary<TKey, TValue>can hold fewer thanElementCountentries when generated values collide. - Conventions are suffix-based: e.g.
Microphonemaps to a phone number.
When Not To Use Microlens.Synthesizer.Core
Microlens.Synthesizer.Core is not intended for:
- Runtime/reflection-based fake generation with no persisted source file
- Serializing or deserializing real instances of your classes
- Types the built-in providers can't resolve, without registering a custom
IDataTypeProvider - Types with no instance, non-indexer properties that have an accessible getter and a setter
If you need fake data generated at runtime without a compiled Faker<T> class, a reflection-based library is a better fit.
License
Licensed under the Apache License 2.0.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.CodeAnalysis.CSharp (>= 4.14.0)
- Microsoft.Extensions.DependencyInjection (>= 9.0.20)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Compiled binary drop for version tag: v2.0.1 (Commit: 0018ba6c8172e631d73d374ce475494e3d751b31)