This commit is contained in:
Mathias Beaulieu-Duncan 2025-11-10 03:40:25 -05:00
parent dea62c2434
commit 87273f6fcf
Signed by: mathias
GPG Key ID: 1C16CF05BAF9162D
8 changed files with 1275 additions and 51 deletions

View File

@ -42,7 +42,8 @@
"WebFetch(domain:blog.rsuter.com)", "WebFetch(domain:blog.rsuter.com)",
"WebFetch(domain:natemcmaster.com)", "WebFetch(domain:natemcmaster.com)",
"WebFetch(domain:www.nuget.org)", "WebFetch(domain:www.nuget.org)",
"Bash(mkdir:*)" "Bash(mkdir:*)",
"Bash(nul)"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

3
.gitignore vendored
View File

@ -3,6 +3,9 @@
## ##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
nul
Svrnty.Sample/nul
.research/ .research/
# User-specific files # User-specific files

View File

@ -6,46 +6,45 @@ namespace Svrnty.CQRS.Abstractions.Discovery;
public sealed class CommandMeta : ICommandMeta public sealed class CommandMeta : ICommandMeta
{ {
private readonly string _name;
private readonly string _lowerCamelCaseName;
public CommandMeta(Type commandType, Type serviceType, Type commandResultType) public CommandMeta(Type commandType, Type serviceType, Type commandResultType)
{ {
CommandType = commandType; CommandType = commandType;
ServiceType = serviceType; ServiceType = serviceType;
CommandResultType = commandResultType; CommandResultType = commandResultType;
// Cache reflection and computed values once in constructor
var nameAttribute = commandType.GetCustomAttribute<CommandNameAttribute>();
_name = nameAttribute?.Name ?? commandType.Name.Replace("Command", string.Empty);
_lowerCamelCaseName = ComputeLowerCamelCaseName(_name);
} }
public CommandMeta(Type commandType, Type serviceType) public CommandMeta(Type commandType, Type serviceType)
{ {
CommandType = commandType; CommandType = commandType;
ServiceType = serviceType; ServiceType = serviceType;
// Cache reflection and computed values once in constructor
var nameAttribute = commandType.GetCustomAttribute<CommandNameAttribute>();
_name = nameAttribute?.Name ?? commandType.Name.Replace("Command", string.Empty);
_lowerCamelCaseName = ComputeLowerCamelCaseName(_name);
} }
private CommandNameAttribute NameAttribute => CommandType.GetCustomAttribute<CommandNameAttribute>(); private static string ComputeLowerCamelCaseName(string name)
public string Name
{ {
get if (string.IsNullOrEmpty(name))
{
var name = NameAttribute?.Name ?? CommandType.Name.Replace("Command", string.Empty);
return name; return name;
}
var firstLetter = char.ToLowerInvariant(name[0]);
return $"{firstLetter}{name.Substring(1)}";
} }
public string Name => _name;
public Type CommandType { get; } public Type CommandType { get; }
public Type ServiceType { get; } public Type ServiceType { get; }
public Type CommandResultType { get; } public Type CommandResultType { get; }
public string LowerCamelCaseName => _lowerCamelCaseName;
public string LowerCamelCaseName
{
get
{
if (string.IsNullOrEmpty(Name))
return Name;
var name = Name;
var firstLetter = Char.ToLowerInvariant(name[0]);
var ret = $"{firstLetter}{name.Substring(1)}";
return ret;
}
}
} }

View File

@ -6,23 +6,20 @@ namespace Svrnty.CQRS.Abstractions.Discovery;
public class QueryMeta : IQueryMeta public class QueryMeta : IQueryMeta
{ {
private readonly string _name;
public QueryMeta(Type queryType, Type serviceType, Type queryResultType) public QueryMeta(Type queryType, Type serviceType, Type queryResultType)
{ {
QueryType = queryType; QueryType = queryType;
ServiceType = serviceType; ServiceType = serviceType;
QueryResultType = queryResultType; QueryResultType = queryResultType;
// Cache reflection and computed value once in constructor
var nameAttribute = queryType.GetCustomAttribute<QueryNameAttribute>();
_name = nameAttribute?.Name ?? queryType.Name.Replace("Query", string.Empty);
} }
protected virtual QueryNameAttribute NameAttribute => QueryType.GetCustomAttribute<QueryNameAttribute>(); public virtual string Name => _name;
public virtual string Name
{
get
{
var name = NameAttribute?.Name ?? QueryType.Name.Replace("Query", string.Empty);
return name;
}
}
public virtual Type QueryType { get; } public virtual Type QueryType { get; }
public virtual Type ServiceType { get; } public virtual Type ServiceType { get; }
@ -33,13 +30,13 @@ public class QueryMeta : IQueryMeta
{ {
get get
{ {
if (string.IsNullOrEmpty(Name)) // Use virtual Name property so derived classes can override
return Name;
var name = Name; var name = Name;
if (string.IsNullOrEmpty(name))
return name;
var firstLetter = char.ToLowerInvariant(name[0]); var firstLetter = char.ToLowerInvariant(name[0]);
var ret = $"{firstLetter}{name[1..]}"; return $"{firstLetter}{name[1..]}";
return ret;
} }
} }
} }

View File

@ -7,16 +7,37 @@ namespace Svrnty.CQRS.Discovery;
public sealed class CommandDiscovery : ICommandDiscovery public sealed class CommandDiscovery : ICommandDiscovery
{ {
private readonly IEnumerable<ICommandMeta> _commandMetas; private readonly List<ICommandMeta> _commandMetas;
private readonly Dictionary<string, ICommandMeta> _commandsByName;
private readonly Dictionary<Type, ICommandMeta> _commandsByType;
public CommandDiscovery(IEnumerable<ICommandMeta> commandMetas) public CommandDiscovery(IEnumerable<ICommandMeta> commandMetas)
{ {
_commandMetas = commandMetas; // Materialize the enumerable to a list once
_commandMetas = commandMetas.ToList();
// Build lookup dictionaries for O(1) access
_commandsByName = new Dictionary<string, ICommandMeta>(_commandMetas.Count);
_commandsByType = new Dictionary<Type, ICommandMeta>(_commandMetas.Count);
foreach (var meta in _commandMetas)
{
_commandsByName[meta.Name] = meta;
_commandsByType[meta.CommandType] = meta;
}
} }
public IEnumerable<ICommandMeta> GetCommands() => _commandMetas; public IEnumerable<ICommandMeta> GetCommands() => _commandMetas;
public ICommandMeta FindCommand(string name) => _commandMetas.FirstOrDefault(t => t.Name == name);
public ICommandMeta FindCommand(Type commandType) => _commandMetas.FirstOrDefault(t => t.CommandType == commandType); public ICommandMeta FindCommand(string name) =>
public bool CommandExists(string name) => _commandMetas.Any(t => t.Name == name); _commandsByName.TryGetValue(name, out var meta) ? meta : null;
public bool CommandExists(Type commandType) => _commandMetas.Any(t => t.CommandType == commandType);
public ICommandMeta FindCommand(Type commandType) =>
_commandsByType.TryGetValue(commandType, out var meta) ? meta : null;
public bool CommandExists(string name) =>
_commandsByName.ContainsKey(name);
public bool CommandExists(Type commandType) =>
_commandsByType.ContainsKey(commandType);
} }

View File

@ -7,17 +7,38 @@ namespace Svrnty.CQRS.Discovery;
public sealed class QueryDiscovery : IQueryDiscovery public sealed class QueryDiscovery : IQueryDiscovery
{ {
private readonly IEnumerable<IQueryMeta> _queryMetas; private readonly List<IQueryMeta> _queryMetas;
private readonly Dictionary<string, IQueryMeta> _queriesByName;
private readonly Dictionary<Type, IQueryMeta> _queriesByType;
public QueryDiscovery(IEnumerable<IQueryMeta> queryMetas) public QueryDiscovery(IEnumerable<IQueryMeta> queryMetas)
{ {
_queryMetas = queryMetas; // Materialize the enumerable to a list once
_queryMetas = queryMetas.ToList();
// Build lookup dictionaries for O(1) access
_queriesByName = new Dictionary<string, IQueryMeta>(_queryMetas.Count);
_queriesByType = new Dictionary<Type, IQueryMeta>(_queryMetas.Count);
foreach (var meta in _queryMetas)
{
_queriesByName[meta.Name] = meta;
_queriesByType[meta.QueryType] = meta;
}
} }
public IEnumerable<IQueryMeta> GetQueries() => _queryMetas; public IEnumerable<IQueryMeta> GetQueries() => _queryMetas;
public IQueryMeta FindQuery(string name) => _queryMetas.FirstOrDefault(t => t.Name == name);
public IQueryMeta FindQuery(Type queryType) => _queryMetas.FirstOrDefault(t => t.QueryType == queryType); public IQueryMeta FindQuery(string name) =>
public bool QueryExists(string name) => _queryMetas.Any(t => t.Name == name); _queriesByName.TryGetValue(name, out var meta) ? meta : null;
public bool QueryExists(Type queryType) => _queryMetas.Any(t => t.QueryType == queryType);
public IQueryMeta FindQuery(Type queryType) =>
_queriesByType.TryGetValue(queryType, out var meta) ? meta : null;
public bool QueryExists(string name) =>
_queriesByName.ContainsKey(name);
public bool QueryExists(Type queryType) =>
_queriesByType.ContainsKey(queryType);
} }

View File

@ -22,13 +22,13 @@ public static class ServiceCollectionExtensions
public static IServiceCollection AddDefaultQueryDiscovery(this IServiceCollection services) public static IServiceCollection AddDefaultQueryDiscovery(this IServiceCollection services)
{ {
services.TryAddTransient<IQueryDiscovery, QueryDiscovery>(); services.TryAddSingleton<IQueryDiscovery, QueryDiscovery>();
return services; return services;
} }
public static IServiceCollection AddDefaultCommandDiscovery(this IServiceCollection services) public static IServiceCollection AddDefaultCommandDiscovery(this IServiceCollection services)
{ {
services.TryAddTransient<ICommandDiscovery, CommandDiscovery>(); services.TryAddSingleton<ICommandDiscovery, CommandDiscovery>();
return services; return services;
} }
} }

File diff suppressed because it is too large Load Diff