Fluent API-based Source Generator Type Manipulation Library
Structura is a flexible type generation library that converts anonymous types and EF Core projection results to strongly-typed types, with the ability to add, exclude, and change properties.
- π Anonymous Type Combination: Combine multiple anonymous types to create new types
- π EF Core Projection Support: Automatically convert EF Core Select results to strongly-typed types
- β Property Addition: Add new properties to existing types (
.Add()) - β Property Exclusion: Exclude sensitive or unnecessary properties (
.Exclude()) - π Type Conversion: Change existing property types to different types (
.ChangeType()) - π― Smart Converter: Automatically generate extension methods for converting anonymous objects to strong types
- π·οΈ Multiple Type Generation: Support for Records, Classes, and Structs
- π§ͺ Comprehensive Testing: Stability verified with 100+ unit tests
dotnet add package Structura
Combine multiple anonymous types to create new strongly-typed types.
// Single anonymous type usage var userInfo = new { Name = "John Doe", Age = 30, Department = "Development" }; TypeCombiner.Combine() .With(userInfo) .WithName("Employee") .AsRecord() .Generate(); // Usage: new Generated.Employee("Jane Smith", 25, "Design") // Multiple anonymous type combination var personal = new { FirstName = "John", LastName = "Doe" }; var work = new { Company = "TechCorp", Position = "Developer" }; TypeCombiner.Combine() .With(personal) .With(work) .WithName("FullProfile") .AsClass() .Generate();
Automatically convert EF Core Select results to strongly-typed types.
// EF Core projection query var userProjection = dbContext.Users .Select(u => new { u.Name, u.Email, u.Department }) .ToList(); // Strong type generation TypeCombiner.Combine() .WithProjection(userProjection) .WithName("UserDto") .WithConverter() // π₯ Enable smart conversion .AsRecord() .Generate(); // Automatic conversion usage List<Generated.UserDto> typedUsers = UserDto.FromCollection(userProjection); Generated.UserDto singleUser = UserDto.FromSingle(userProjection.First());
The game-changer for EF Core projections! Generates static methods for automatically converting anonymous objects to strongly-typed types.
// Step 1: Generate type with converter enabled var customerData = dbContext.Orders .GroupBy(o => o.CustomerId) .Select(g => new { CustomerId = g.Key, CustomerName = g.First().Customer.Name, TotalOrders = g.Count(), TotalSpent = g.Sum(o => o.Amount) }) .ToList(); TypeCombiner.Combine() .WithProjection(customerData) .WithName("CustomerAnalytics") .WithConverter() // π₯ The magic begins! .AsRecord() .Generate(); // Step 2: Use conversion methods directly on the generated type List<Generated.CustomerAnalytics> analytics = CustomerAnalytics.FromCollection(customerData); Generated.CustomerAnalytics single = CustomerAnalytics.FromSingle(customerData.First()); // Strongly-typed anonymous objects are also convertible var anonymousData = new[] { new { CustomerId = 1, CustomerName = "John Doe", TotalOrders = 5, TotalSpent = 150000m }, new { CustomerId = 2, CustomerName = "Jane Smith", TotalOrders = 3, TotalSpent = 75000m } }; List<Generated.CustomerAnalytics> converted = CustomerAnalytics.FromCollection(anonymousData);
// Add new properties to existing types var userData = new { Name = "John Doe", Email = "john@test.com" }; TypeCombiner.Combine() .With(userData) .Add("Id", typeof(int)) .Add("CreatedAt", typeof(DateTime)) .Add("Metadata", typeof(Dictionary<string, object>)) .WithName("EnhancedUser") .AsClass() .Generate(); // Pure Add usage for type creation TypeCombiner.Combine() .Add("Name", typeof(string)) .Add("Value", typeof(int)) .Add("IsActive", typeof(bool)) .WithName("CustomType") .AsRecord() .Generate();
// Exclude sensitive information var sensitiveData = new { Name = "John Doe", Age = 30, Password = "secret123", Email = "john@test.com" }; TypeCombiner.Combine() .With(sensitiveData) .Exclude("Password") // Exclude password .WithName("SafeUser") .AsClass() .Generate(); // Multiple property exclusion TypeCombiner.Combine() .With(userData) .Exclude("Password") .Exclude("InternalId") .WithName("PublicData") .Generate();
// Property type conversion var numericData = new { Id = 1, Price = 100m, Quantity = 5, IsActive = true }; TypeCombiner.Combine() .With(numericData) .ChangeType("Price", typeof(string)) // decimal β string .ChangeType("Quantity", typeof(long)) // int β long .ChangeType("IsActive", typeof(int)) // bool β int .WithName("ConvertedProduct") .AsClass() .Generate();
All features can be used together.
// EF Core projection + property addition + type conversion + converter var orderProjection = dbContext.Orders .Select(o => new { o.Id, o.CustomerName, o.Amount, o.OrderDate }) .ToList(); TypeCombiner.Combine() .WithProjection(orderProjection) .Add("ProcessedAt", typeof(DateTime)) // Property addition .Add("Status", typeof(string)) // Property addition .ChangeType("Amount", typeof(string)) // decimal β string .Exclude("InternalId") // Property exclusion (if exists) .WithConverter() // Enable smart converter .WithName("ProcessedOrder") .AsRecord() .Generate(); // Complete conversion support List<Generated.ProcessedOrder> orders = ProcessedOrder.FromCollection(orderProjection);
| Type | Method | Description | Converter Support |
|---|---|---|---|
| Record | .AsRecord() |
Generate immutable record types | β Full Support |
| Class | .AsClass() |
Generate mutable class types | β Full Support |
| Struct | .AsStruct() |
Generate value type structs | β Full Support |
- .NET Standard 2.0 and above
- .NET 9 recommended
- C# 12.0 syntax support
// Complex dashboard data generation var dashboardData = await dbContext.Orders .Include(o => o.Customer) .GroupBy(o => new { o.CustomerId, o.Customer.Name }) .Select(g => new { CustomerId = g.Key.CustomerId, CustomerName = g.Key.Name, TotalOrders = g.Count(), TotalRevenue = g.Sum(o => o.Amount), AverageOrderValue = g.Average(o => o.Amount), LastOrderDate = g.Max(o => o.OrderDate) }) .ToListAsync(); // Strongly-typed DTO generation and conversion TypeCombiner.Combine() .WithProjection(dashboardData) .Add("GeneratedAt", typeof(DateTime)) .Add("ReportType", typeof(string)) .WithName("CustomerDashboard") .WithConverter() .AsRecord() .Generate(); // Immediately available strongly-typed collection List<Generated.CustomerDashboard> dashboard = CustomerDashboard.FromCollection(dashboardData); // Complete type safety in business logic var topCustomers = dashboard .Where(c => c.TotalRevenue > 100000) .OrderByDescending(c => c.TotalRevenue) .Take(10) .ToList();
// Generate public API DTO from internal entity var internalUser = new { Id = 1, Name = "John Doe", Email = "john@company.com", Password = "hashed_password", InternalNotes = "Important internal info", Salary = 5000000m }; TypeCombiner.Combine() .With(internalUser) .Exclude("Password") // Exclude password .Exclude("InternalNotes") // Exclude internal info .Exclude("Salary") // Exclude salary info .Add("PublicId", typeof(Guid)) .WithConverter() .WithName("UserApiDto") .AsRecord() .Generate();
// Convert external API response to internal format var externalApiResponse = new[] { new { user_id = "123", full_name = "John Doe", email_addr = "john@example.com", is_active = "1" }, new { user_id = "456", full_name = "Jane Smith", email_addr = "jane@example.com", is_active = "0" } }; TypeCombiner.Combine() .WithProjection(externalApiResponse) .ChangeType("user_id", typeof(int)) // string β int .ChangeType("is_active", typeof(bool)) // string β bool .Add("ImportedAt", typeof(DateTime)) .WithConverter() .WithName("ImportedUser") .AsRecord() .Generate(); // Type-safe conversion List<Generated.ImportedUser> users = ImportedUser.FromCollection(externalApiResponse);
dotnet test
Structura is validated with 100+ comprehensive unit tests:
| Test Category | Test Count | Description |
|---|---|---|
| Core Features | 15 | All basic TypeCombiner API features |
| Anonymous Types | 12 | Anonymous object combination and processing |
| EF Core Projection | 18 | EF Core projection result processing |
| Variable References | 8 | Anonymous type variable analysis |
| Type Generation Modes | 6 | Record, Class, Struct type generation |
| Edge Cases | 5 | Error conditions and boundary cases |
| Fluent API | 4 | Method chaining integrity |
| Type Safety | 3 | Compile-time type validation |
| Source Generator Integration | 8 | Generated type verification |
| Performance | 3 | Large-scale processing and performance |
| Real-World Scenarios | 10 | Production environment use cases |
| Documented Features | 12 | README example code validation |
| Integration Scenarios | 15 | Complex feature combinations |
| π Converter Extensions | 20 | Smart converter functionality |
| Add/Exclude/ChangeType | 18 | Property manipulation features |
- Total Tests: 150+
- Passed: 150+ β
- Failed: 0
- Skipped: 0
- Execution Time: < 3 seconds
Structura/ βββ π Structura/ # Main library β βββ TypeCombiner.cs # Fluent API entry point β βββ AnonymousTypeCombinerBuilder.cs # Anonymous type builder β βββ TypeDefinitions.cs # Core type definitions β βββ StructuraSourceGenerator.cs # Source generator engine βββ π Structura.Test.Console/ # Integration test console β βββ Program.cs # Real usage examples and demos βββ π Structura.Tests/ # Unit test project β βββ UnitTest.cs # Basic functionality unit tests β βββ VariableReferenceTests.cs # Variable reference feature tests β βββ EFCoreProjectionTests.cs # EF Core projection tests β βββ ConverterExtensionTests.cs # Smart converter tests β βββ AddFunctionalityTests.cs # Add functionality tests β βββ ExcludeFunctionalityTests.cs # Exclude functionality tests β βββ ChangeTypeFunctionalityTests.cs # ChangeType functionality tests β βββ IntegrationTests.cs # Integration and scenario tests βββ π README.md # Documentation
| Feature | Status | Description |
|---|---|---|
| Source Generator Engine | 100% β | Complete |
| Fluent API | 100% β | Complete |
| Anonymous Type Support | 100% β | Complete |
| Variable Reference Analysis | 100% β | Complete |
| EF Core Projection Support | 100% β | Complete |
| Property Add/Exclude/ChangeType | 100% β | Complete |
| Type Conversion | 100% β | Record/Class/Struct support |
| π Smart Converter Extensions | 100% β | NEW! Perfect object conversion |
| Comprehensive Test Suite | 100% β | 150+ tests passing |
dotnet add package Structura
using Structura; // Create new type from anonymous types var userData = new { Name = "John Doe", Age = 30, Email = "john@test.com" }; TypeCombiner.Combine() .With(userData) .Add("Id", typeof(int)) .Exclude("InternalData") // Exclude if exists .WithName("User") .AsRecord() .Generate(); // Use the generated type var user = new Generated.User("Jane Smith", 25, "jane@test.com", 1001);
// EF Core projection conversion var efResult = dbContext.Products .Select(p => new { p.Name, p.Price, p.Category }) .ToList(); TypeCombiner.Combine() .WithProjection(efResult) .Add("ProcessedAt", typeof(DateTime)) .WithConverter() // π₯ Enable smart conversion .WithName("ProductDto") .AsRecord() .Generate(); // Immediate conversion available List<Generated.ProductDto> products = ProductDto.FromCollection(efResult);
| Method | Description | Return Type |
|---|---|---|
Combine() |
Start with anonymous types only | AnonymousTypeCombinerBuilder |
| Method | Description | Chainable |
|---|---|---|
.With(object) |
Add anonymous type | β |
.WithProjection(IEnumerable<object>) |
Add EF Core projection | β |
.Add(string, Type) |
Add property | β |
.Exclude(string) |
Exclude property | β |
.ChangeType(string, Type) |
Change property type | β |
.WithConverter() |
π Enable smart converter extensions | β |
.WithName(string) |
Set generated type name | β |
.AsRecord() |
Generate as record | β |
.AsClass() |
Generate as class | β |
.AsStruct() |
Generate as struct | β |
.Generate() |
Execute type generation | β |
When .WithConverter() is used, the following static methods are automatically generated on the generated type itself:
| Method | Description | Usage |
|---|---|---|
.FromSingle(object) |
Convert single object | UserDto.FromSingle(objectItem) |
.FromCollection(IEnumerable<object>) |
Convert object collection | UserDto.FromCollection(objectList) |
Issues and pull requests are welcome!
MIT License
Structura - Simplify type manipulation with smart conversion! π