516 lines
23 KiB
C#
516 lines
23 KiB
C#
using AyCode.Core.Extensions;
|
|
using AyCode.Core.Interfaces;
|
|
using BenchmarkDotNet.Attributes;
|
|
using Newtonsoft.Json;
|
|
using System.Text;
|
|
|
|
namespace AyCode.Core.Benchmarks;
|
|
|
|
[MemoryDiagnoser]
|
|
public class SerializationBenchmarks
|
|
{
|
|
// Complex graph with 7 levels, ~1500 objects, cross-references
|
|
private Level1_Company _complexGraph = null!;
|
|
|
|
// Pre-serialized JSON for deserialization benchmarks
|
|
private string _newtonsoftJson = null!;
|
|
private string _ayCodeJson = null!;
|
|
|
|
// Settings
|
|
private JsonSerializerSettings _newtonsoftNoRefSettings = null!;
|
|
private JsonSerializerSettings _ayCodeSettings = null!;
|
|
|
|
[GlobalSetup]
|
|
public void Setup()
|
|
{
|
|
// Newtonsoft WITHOUT reference handling (baseline)
|
|
_newtonsoftNoRefSettings = new JsonSerializerSettings
|
|
{
|
|
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
|
NullValueHandling = NullValueHandling.Ignore,
|
|
DefaultValueHandling = DefaultValueHandling.Ignore, // Fair comparison - also skip defaults
|
|
Formatting = Formatting.None
|
|
};
|
|
|
|
// AyCode WITH reference handling (our optimized solution)
|
|
_ayCodeSettings = SerializeObjectExtensions.Options;
|
|
|
|
// Create complex 7-level graph with ~1500 objects and cross-references
|
|
_complexGraph = CreateComplexGraph();
|
|
|
|
// Pre-serialize for deserialization benchmarks
|
|
_newtonsoftJson = JsonConvert.SerializeObject(_complexGraph, _newtonsoftNoRefSettings);
|
|
_ayCodeJson = _complexGraph.ToJson(_ayCodeSettings);
|
|
|
|
// Output sizes for comparison
|
|
var newtonsoftBytes = Encoding.UTF8.GetByteCount(_newtonsoftJson);
|
|
var ayCodeBytes = Encoding.UTF8.GetByteCount(_ayCodeJson);
|
|
|
|
Console.WriteLine("=== JSON Size Comparison ===");
|
|
Console.WriteLine($"Newtonsoft (skip defaults): {_newtonsoftJson.Length:N0} chars ({newtonsoftBytes:N0} bytes, {newtonsoftBytes / 1024.0:F1} KB)");
|
|
Console.WriteLine($"AcJsonSerializer (refs+skip): {_ayCodeJson.Length:N0} chars ({ayCodeBytes:N0} bytes, {ayCodeBytes / 1024.0:F1} KB)");
|
|
Console.WriteLine($"Size reduction: {(1.0 - (double)ayCodeBytes / newtonsoftBytes) * 100:F1}%");
|
|
Console.WriteLine($"Bytes saved: {newtonsoftBytes - ayCodeBytes:N0}");
|
|
}
|
|
|
|
#region Serialization Benchmarks
|
|
|
|
[Benchmark(Description = "Newtonsoft (no refs)")]
|
|
[BenchmarkCategory("Serialize")]
|
|
public string Serialize_Newtonsoft_NoRefs()
|
|
=> JsonConvert.SerializeObject(_complexGraph, _newtonsoftNoRefSettings);
|
|
|
|
[Benchmark(Description = "AyCode (with refs)")]
|
|
[BenchmarkCategory("Serialize")]
|
|
public string Serialize_AyCode_WithRefs()
|
|
=> _complexGraph.ToJson(_ayCodeSettings);
|
|
|
|
#endregion
|
|
|
|
#region Deserialization Benchmarks
|
|
|
|
[Benchmark(Description = "Newtonsoft (no refs)")]
|
|
[BenchmarkCategory("Deserialize")]
|
|
public Level1_Company? Deserialize_Newtonsoft_NoRefs()
|
|
=> JsonConvert.DeserializeObject<Level1_Company>(_newtonsoftJson, _newtonsoftNoRefSettings);
|
|
|
|
[Benchmark(Description = "AyCode (with refs)")]
|
|
[BenchmarkCategory("Deserialize")]
|
|
public Level1_Company? Deserialize_AyCode_WithRefs()
|
|
=> _ayCodeJson.JsonTo<Level1_Company>(_ayCodeSettings);
|
|
|
|
[Benchmark(Description = "AcJsonDeserializer (custom)")]
|
|
[BenchmarkCategory("Deserialize")]
|
|
public Level1_Company? Deserialize_AcJsonDeserializer()
|
|
=> AcJsonDeserializer.Deserialize<Level1_Company>(_ayCodeJson);
|
|
|
|
#endregion
|
|
|
|
#region JSON Size Comparison (not timed, just for reporting)
|
|
|
|
[Benchmark(Description = "JSON Size - Newtonsoft")]
|
|
[BenchmarkCategory("Size")]
|
|
public int JsonSize_Newtonsoft() => _newtonsoftJson.Length;
|
|
|
|
[Benchmark(Description = "JSON Size - AyCode")]
|
|
[BenchmarkCategory("Size")]
|
|
public int JsonSize_AyCode() => _ayCodeJson.Length;
|
|
|
|
#endregion
|
|
|
|
#region Complex Graph Factory - 7 Levels, ~1500 objects, Cross-references
|
|
|
|
private static int _idCounter = 1;
|
|
|
|
/// <summary>
|
|
/// Creates a 7-level deep graph with approximately 1500 objects and cross-references.
|
|
/// Structure: Company -> Departments -> Teams -> Projects -> Tasks -> SubTasks -> Comments
|
|
/// Each object has 8-15 properties of various types.
|
|
/// </summary>
|
|
private static Level1_Company CreateComplexGraph()
|
|
{
|
|
_idCounter = 1;
|
|
|
|
// Shared references (cross-references across the graph)
|
|
var sharedTags = Enumerable.Range(1, 10)
|
|
.Select(i => new SharedTag
|
|
{
|
|
Id = _idCounter++,
|
|
Name = $"Tag-{i}",
|
|
Color = $"#{i:X2}{i * 10:X2}{i * 20:X2}",
|
|
Priority = i % 5,
|
|
IsActive = i % 2 == 0,
|
|
CreatedAt = DateTime.UtcNow.AddDays(-i * 10),
|
|
Metadata = $"Metadata for tag {i}"
|
|
})
|
|
.ToList();
|
|
|
|
var sharedCategories = Enumerable.Range(1, 5)
|
|
.Select(i => new SharedCategory
|
|
{
|
|
Id = _idCounter++,
|
|
Name = $"Category-{i}",
|
|
Description = $"Description for category {i} with some extra text to make it longer",
|
|
SortOrder = i * 100,
|
|
IconUrl = $"https://icons.example.com/cat-{i}.png",
|
|
IsDefault = i == 1,
|
|
ParentCategoryId = i > 1 ? i - 1 : null,
|
|
CreatedAt = DateTime.UtcNow.AddMonths(-i),
|
|
UpdatedAt = DateTime.UtcNow.AddDays(-i)
|
|
})
|
|
.ToList();
|
|
|
|
var sharedUser = new SharedUser
|
|
{
|
|
Id = _idCounter++,
|
|
Username = "admin",
|
|
Email = "admin@company.com",
|
|
FirstName = "System",
|
|
LastName = "Administrator",
|
|
PhoneNumber = "+1-555-0100",
|
|
IsActive = true,
|
|
Role = UserRole.Admin,
|
|
LastLoginAt = DateTime.UtcNow.AddHours(-1),
|
|
CreatedAt = DateTime.UtcNow.AddYears(-2),
|
|
Preferences = new UserPreferences
|
|
{
|
|
Theme = "dark",
|
|
Language = "en-US",
|
|
NotificationsEnabled = true,
|
|
EmailDigestFrequency = "daily"
|
|
}
|
|
};
|
|
|
|
// Level 1: Company (1 object)
|
|
var company = new Level1_Company
|
|
{
|
|
Id = _idCounter++,
|
|
Name = "TechCorp International",
|
|
LegalName = "TechCorp International Holdings Ltd.",
|
|
TaxId = "TC-123456789",
|
|
FoundedDate = new DateTime(2010, 3, 15),
|
|
EmployeeCount = 1500,
|
|
AnnualRevenue = 125_000_000.50m,
|
|
IsPubliclyTraded = true,
|
|
StockSymbol = "TECH",
|
|
HeadquartersAddress = "123 Innovation Drive, Tech City, TC 12345",
|
|
Website = "https://www.techcorp.example.com",
|
|
PrimaryContact = sharedUser,
|
|
MainCategory = sharedCategories[0],
|
|
Tags = [sharedTags[0], sharedTags[1], sharedTags[2]],
|
|
CreatedAt = DateTime.UtcNow.AddYears(-5),
|
|
UpdatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
// Level 2: Departments (5 objects)
|
|
company.Departments = Enumerable.Range(1, 5).Select(deptIdx => new Level2_Department
|
|
{
|
|
Id = _idCounter++,
|
|
Name = $"Department-{deptIdx}",
|
|
Code = $"DEPT-{deptIdx:D3}",
|
|
Description = $"This is department {deptIdx} responsible for various operations and strategic initiatives",
|
|
Budget = 1_000_000m + (deptIdx * 250_000m),
|
|
HeadCount = 50 + (deptIdx * 20),
|
|
Location = $"Building {(char)('A' + deptIdx - 1)}, Floor {deptIdx}",
|
|
CostCenter = $"CC-{1000 + deptIdx}",
|
|
IsActive = true,
|
|
Manager = sharedUser, // Cross-reference
|
|
Category = sharedCategories[deptIdx % sharedCategories.Count], // Cross-reference
|
|
Tags = [sharedTags[deptIdx % sharedTags.Count], sharedTags[(deptIdx + 1) % sharedTags.Count]], // Cross-reference
|
|
EstablishedDate = DateTime.UtcNow.AddYears(-4).AddMonths(deptIdx),
|
|
CreatedAt = DateTime.UtcNow.AddYears(-4),
|
|
UpdatedAt = DateTime.UtcNow.AddMonths(-deptIdx),
|
|
// Level 3: Teams (6 per department = 30 total)
|
|
Teams = Enumerable.Range(1, 6).Select(teamIdx => new Level3_Team
|
|
{
|
|
Id = _idCounter++,
|
|
Name = $"Team-{deptIdx}-{teamIdx}",
|
|
Acronym = $"T{deptIdx}{teamIdx}",
|
|
Description = $"Team {teamIdx} in department {deptIdx}, focused on delivering excellence",
|
|
MemberCount = 5 + (teamIdx * 2),
|
|
Capacity = 10 + (teamIdx * 2),
|
|
Utilization = 0.65 + (teamIdx * 0.05),
|
|
SprintLength = 14,
|
|
VelocityAverage = 42.5 + teamIdx,
|
|
IsRemote = teamIdx % 3 == 0,
|
|
Timezone = teamIdx % 2 == 0 ? "UTC" : "America/New_York",
|
|
SlackChannel = $"#team-{deptIdx}-{teamIdx}",
|
|
TeamLead = sharedUser, // Cross-reference
|
|
PrimaryTag = sharedTags[(deptIdx + teamIdx) % sharedTags.Count], // Cross-reference
|
|
CreatedAt = DateTime.UtcNow.AddYears(-3).AddMonths(teamIdx),
|
|
UpdatedAt = DateTime.UtcNow.AddDays(-teamIdx * 7),
|
|
// Level 4: Projects (4 per team = 120 total)
|
|
Projects = Enumerable.Range(1, 4).Select(projIdx => new Level4_Project
|
|
{
|
|
Id = _idCounter++,
|
|
Name = $"Project-{deptIdx}-{teamIdx}-{projIdx}",
|
|
Code = $"PRJ-{deptIdx}{teamIdx}{projIdx:D2}",
|
|
Description = $"Project {projIdx} for team {teamIdx}, delivering key business value and innovation",
|
|
Status = (ProjectStatus)(projIdx % 4),
|
|
Priority = (Priority)(projIdx % 3),
|
|
Budget = 50_000m + (projIdx * 15_000m),
|
|
SpentAmount = 25_000m + (projIdx * 5_000m),
|
|
ProgressPercent = 0.1 + (projIdx * 0.2),
|
|
StartDate = DateTime.UtcNow.AddMonths(-projIdx * 2),
|
|
DueDate = DateTime.UtcNow.AddMonths(projIdx),
|
|
CompletedDate = projIdx == 4 ? DateTime.UtcNow.AddDays(-10) : null,
|
|
EstimatedHours = 200 + (projIdx * 50),
|
|
ActualHours = 150 + (projIdx * 40),
|
|
RiskLevel = projIdx % 3,
|
|
Owner = sharedUser, // Cross-reference
|
|
Category = sharedCategories[projIdx % sharedCategories.Count], // Cross-reference
|
|
Tags = [sharedTags[projIdx % sharedTags.Count]], // Cross-reference
|
|
CreatedAt = DateTime.UtcNow.AddMonths(-projIdx * 3),
|
|
UpdatedAt = DateTime.UtcNow.AddDays(-projIdx),
|
|
// Level 5: Tasks (5 per project = 600 total)
|
|
Tasks = Enumerable.Range(1, 5).Select(taskIdx => new Level5_Task
|
|
{
|
|
Id = _idCounter++,
|
|
Title = $"Task-{deptIdx}-{teamIdx}-{projIdx}-{taskIdx}",
|
|
Description = $"Detailed task description for task {taskIdx} in project {projIdx}. This includes requirements and acceptance criteria.",
|
|
Status = (TaskStatus)(taskIdx % 5),
|
|
Priority = (Priority)(taskIdx % 3),
|
|
Type = (TaskType)(taskIdx % 4),
|
|
StoryPoints = taskIdx * 2,
|
|
EstimatedHours = 4 + taskIdx * 2,
|
|
ActualHours = 3 + taskIdx * 1.5,
|
|
DueDate = DateTime.UtcNow.AddDays(taskIdx * 3),
|
|
CompletedDate = taskIdx <= 2 ? DateTime.UtcNow.AddDays(-taskIdx) : null,
|
|
IsBlocked = taskIdx == 3,
|
|
BlockedReason = taskIdx == 3 ? "Waiting for external dependency" : null,
|
|
Assignee = sharedUser, // Cross-reference
|
|
Reporter = sharedUser, // Cross-reference
|
|
Labels = [sharedTags[taskIdx % sharedTags.Count]], // Cross-reference
|
|
CreatedAt = DateTime.UtcNow.AddDays(-taskIdx * 5),
|
|
UpdatedAt = DateTime.UtcNow.AddHours(-taskIdx),
|
|
// Level 6: SubTasks (3 per task = 1800 total -> we'll limit to keep ~1500)
|
|
SubTasks = Enumerable.Range(1, 2).Select(subIdx => new Level6_SubTask
|
|
{
|
|
Id = _idCounter++,
|
|
Title = $"SubTask-{taskIdx}-{subIdx}",
|
|
Description = $"Sub-task {subIdx} details for completing parent task {taskIdx}",
|
|
Status = (TaskStatus)(subIdx % 5),
|
|
EstimatedMinutes = 30 + subIdx * 15,
|
|
ActualMinutes = 25 + subIdx * 12,
|
|
IsCompleted = subIdx == 1,
|
|
CompletedAt = subIdx == 1 ? DateTime.UtcNow.AddHours(-subIdx * 2) : null,
|
|
Assignee = sharedUser, // Cross-reference
|
|
CreatedAt = DateTime.UtcNow.AddDays(-subIdx),
|
|
UpdatedAt = DateTime.UtcNow.AddMinutes(-subIdx * 30),
|
|
// Level 7: Comments (2 per subtask = 2400 total -> limiting)
|
|
Comments = Enumerable.Range(1, 1).Select(comIdx => new Level7_Comment
|
|
{
|
|
Id = _idCounter++,
|
|
Text = $"Comment {comIdx} on subtask {subIdx}: This is a detailed comment with feedback and suggestions for improvement.",
|
|
Author = sharedUser, // Cross-reference
|
|
IsEdited = comIdx % 2 == 0,
|
|
EditedAt = comIdx % 2 == 0 ? DateTime.UtcNow.AddHours(-1) : null,
|
|
LikeCount = comIdx * 3,
|
|
ReplyCount = comIdx,
|
|
CreatedAt = DateTime.UtcNow.AddHours(-comIdx * 4),
|
|
MentionedTags = [sharedTags[comIdx % sharedTags.Count]] // Cross-reference
|
|
}).ToList()
|
|
}).ToList()
|
|
}).ToList()
|
|
}).ToList()
|
|
}).ToList()
|
|
}).ToList();
|
|
|
|
return company;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 7-Level Deep DTOs with 8-15 Properties Each
|
|
|
|
// Shared cross-reference types
|
|
public class SharedTag : IId<int>
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; } = "";
|
|
public string Color { get; set; } = "";
|
|
public int Priority { get; set; }
|
|
public bool IsActive { get; set; }
|
|
public DateTime CreatedAt { get; set; }
|
|
public string Metadata { get; set; } = "";
|
|
}
|
|
|
|
public class SharedCategory : IId<int>
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; } = "";
|
|
public string Description { get; set; } = "";
|
|
public int SortOrder { get; set; }
|
|
public string IconUrl { get; set; } = "";
|
|
public bool IsDefault { get; set; }
|
|
public int? ParentCategoryId { get; set; }
|
|
public DateTime CreatedAt { get; set; }
|
|
public DateTime UpdatedAt { get; set; }
|
|
}
|
|
|
|
public class SharedUser : IId<int>
|
|
{
|
|
public int Id { get; set; }
|
|
public string Username { get; set; } = "";
|
|
public string Email { get; set; } = "";
|
|
public string FirstName { get; set; } = "";
|
|
public string LastName { get; set; } = "";
|
|
public string PhoneNumber { get; set; } = "";
|
|
public bool IsActive { get; set; }
|
|
public UserRole Role { get; set; }
|
|
public DateTime? LastLoginAt { get; set; }
|
|
public DateTime CreatedAt { get; set; }
|
|
public UserPreferences? Preferences { get; set; }
|
|
}
|
|
|
|
public class UserPreferences
|
|
{
|
|
public string Theme { get; set; } = "";
|
|
public string Language { get; set; } = "";
|
|
public bool NotificationsEnabled { get; set; }
|
|
public string EmailDigestFrequency { get; set; } = "";
|
|
}
|
|
|
|
public enum UserRole { User, Manager, Admin }
|
|
public enum ProjectStatus { Planning, Active, OnHold, Completed }
|
|
public enum TaskStatus { Backlog, Todo, InProgress, Review, Done }
|
|
public enum TaskType { Feature, Bug, Improvement, Task }
|
|
public enum Priority { Low, Medium, High }
|
|
|
|
// Level 1: Company (15 properties)
|
|
public class Level1_Company : IId<int>
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; } = "";
|
|
public string LegalName { get; set; } = "";
|
|
public string TaxId { get; set; } = "";
|
|
public DateTime FoundedDate { get; set; }
|
|
public int EmployeeCount { get; set; }
|
|
public decimal AnnualRevenue { get; set; }
|
|
public bool IsPubliclyTraded { get; set; }
|
|
public string? StockSymbol { get; set; }
|
|
public string HeadquartersAddress { get; set; } = "";
|
|
public string Website { get; set; } = "";
|
|
public SharedUser? PrimaryContact { get; set; } // Cross-ref
|
|
public SharedCategory? MainCategory { get; set; } // Cross-ref
|
|
public List<SharedTag> Tags { get; set; } = []; // Cross-ref
|
|
public DateTime CreatedAt { get; set; }
|
|
public DateTime UpdatedAt { get; set; }
|
|
public List<Level2_Department> Departments { get; set; } = [];
|
|
}
|
|
|
|
// Level 2: Department (15 properties)
|
|
public class Level2_Department : IId<int>
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; } = "";
|
|
public string Code { get; set; } = "";
|
|
public string Description { get; set; } = "";
|
|
public decimal Budget { get; set; }
|
|
public int HeadCount { get; set; }
|
|
public string Location { get; set; } = "";
|
|
public string CostCenter { get; set; } = "";
|
|
public bool IsActive { get; set; }
|
|
public SharedUser? Manager { get; set; } // Cross-ref
|
|
public SharedCategory? Category { get; set; } // Cross-ref
|
|
public List<SharedTag> Tags { get; set; } = []; // Cross-ref
|
|
public DateTime EstablishedDate { get; set; }
|
|
public DateTime CreatedAt { get; set; }
|
|
public DateTime UpdatedAt { get; set; }
|
|
public List<Level3_Team> Teams { get; set; } = [];
|
|
}
|
|
|
|
// Level 3: Team (15 properties)
|
|
public class Level3_Team : IId<int>
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; } = "";
|
|
public string Acronym { get; set; } = "";
|
|
public string Description { get; set; } = "";
|
|
public int MemberCount { get; set; }
|
|
public int Capacity { get; set; }
|
|
public double Utilization { get; set; }
|
|
public int SprintLength { get; set; }
|
|
public double VelocityAverage { get; set; }
|
|
public bool IsRemote { get; set; }
|
|
public string Timezone { get; set; } = "";
|
|
public string SlackChannel { get; set; } = "";
|
|
public SharedUser? TeamLead { get; set; } // Cross-ref
|
|
public SharedTag? PrimaryTag { get; set; } // Cross-ref
|
|
public DateTime CreatedAt { get; set; }
|
|
public DateTime UpdatedAt { get; set; }
|
|
public List<Level4_Project> Projects { get; set; } = [];
|
|
}
|
|
|
|
// Level 4: Project (18 properties)
|
|
public class Level4_Project : IId<int>
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; } = "";
|
|
public string Code { get; set; } = "";
|
|
public string Description { get; set; } = "";
|
|
public ProjectStatus Status { get; set; }
|
|
public Priority Priority { get; set; }
|
|
public decimal Budget { get; set; }
|
|
public decimal SpentAmount { get; set; }
|
|
public double ProgressPercent { get; set; }
|
|
public DateTime StartDate { get; set; }
|
|
public DateTime DueDate { get; set; }
|
|
public DateTime? CompletedDate { get; set; }
|
|
public int EstimatedHours { get; set; }
|
|
public int ActualHours { get; set; }
|
|
public int RiskLevel { get; set; }
|
|
public SharedUser? Owner { get; set; } // Cross-ref
|
|
public SharedCategory? Category { get; set; } // Cross-ref
|
|
public List<SharedTag> Tags { get; set; } = []; // Cross-ref
|
|
public DateTime CreatedAt { get; set; }
|
|
public DateTime UpdatedAt { get; set; }
|
|
public List<Level5_Task> Tasks { get; set; } = [];
|
|
}
|
|
|
|
// Level 5: Task (18 properties)
|
|
public class Level5_Task : IId<int>
|
|
{
|
|
public int Id { get; set; }
|
|
public string Title { get; set; } = "";
|
|
public string Description { get; set; } = "";
|
|
public TaskStatus Status { get; set; }
|
|
public Priority Priority { get; set; }
|
|
public TaskType Type { get; set; }
|
|
public int StoryPoints { get; set; }
|
|
public double EstimatedHours { get; set; }
|
|
public double ActualHours { get; set; }
|
|
public DateTime DueDate { get; set; }
|
|
public DateTime? CompletedDate { get; set; }
|
|
public bool IsBlocked { get; set; }
|
|
public string? BlockedReason { get; set; }
|
|
public SharedUser? Assignee { get; set; } // Cross-ref
|
|
public SharedUser? Reporter { get; set; } // Cross-ref
|
|
public List<SharedTag> Labels { get; set; } = []; // Cross-ref
|
|
public DateTime CreatedAt { get; set; }
|
|
public DateTime UpdatedAt { get; set; }
|
|
public List<Level6_SubTask> SubTasks { get; set; } = [];
|
|
}
|
|
|
|
// Level 6: SubTask (11 properties)
|
|
public class Level6_SubTask : IId<int>
|
|
{
|
|
public int Id { get; set; }
|
|
public string Title { get; set; } = "";
|
|
public string Description { get; set; } = "";
|
|
public TaskStatus Status { get; set; }
|
|
public int EstimatedMinutes { get; set; }
|
|
public int ActualMinutes { get; set; }
|
|
public bool IsCompleted { get; set; }
|
|
public DateTime? CompletedAt { get; set; }
|
|
public SharedUser? Assignee { get; set; } // Cross-ref
|
|
public DateTime CreatedAt { get; set; }
|
|
public DateTime UpdatedAt { get; set; }
|
|
public List<Level7_Comment> Comments { get; set; } = [];
|
|
}
|
|
|
|
// Level 7: Comment (10 properties)
|
|
public class Level7_Comment : IId<int>
|
|
{
|
|
public int Id { get; set; }
|
|
public string Text { get; set; } = "";
|
|
public SharedUser? Author { get; set; } // Cross-ref
|
|
public bool IsEdited { get; set; }
|
|
public DateTime? EditedAt { get; set; }
|
|
public int LikeCount { get; set; }
|
|
public int ReplyCount { get; set; }
|
|
public DateTime CreatedAt { get; set; }
|
|
public List<SharedTag> MentionedTags { get; set; } = []; // Cross-ref
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AcJsonSerializer Benchmarks
|
|
|
|
[Benchmark(Description = "AcJsonSerializer (custom)")]
|
|
[BenchmarkCategory("Serialize")]
|
|
public string Serialize_AcJsonSerializer()
|
|
=> AcJsonSerializer.Serialize(_complexGraph);
|
|
|
|
#endregion
|
|
} |