Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,69 +8,74 @@ namespace CommonFramework.DependencyInjection;
/// </summary>
public static class ServiceCollectionValidationExtensions
{
/// <summary>
/// Registers a validator of type <typeparamref name="TValidator"/> with the service collection.
/// </summary>
/// <typeparam name="TValidator">The type of the validator to register. Must implement <see cref="IServiceCollectionValidator"/> and have a parameterless constructor.</typeparam>
/// <param name="services">The service collection to add the validator to.</param>
/// <returns>The original <see cref="IServiceCollection"/> for chaining.</returns>
public static IServiceCollection AddValidator<TValidator>(this IServiceCollection services)
where TValidator : IServiceCollectionValidator, new()
extension(IServiceCollection services)
{
return services.AddValidator(new TValidator());
}

/// <summary>
/// Registers an instance of <see cref="IServiceCollectionValidator"/> with the service collection.
/// </summary>
/// <param name="services">The service collection to add the validator to.</param>
/// <param name="validator">The validator instance to register.</param>
/// <returns>The original <see cref="IServiceCollection"/> for chaining.</returns>
public static IServiceCollection AddValidator(this IServiceCollection services, IServiceCollectionValidator validator)
{
return services.AddSingleton(validator);
}
/// <summary>
/// Registers a validator of type <typeparamref name="TValidator"/> with the service collection.
/// </summary>
/// <typeparam name="TValidator">The type of the validator to register. Must implement <see cref="IServiceCollectionValidator"/> and have a parameterless constructor.</typeparam>
/// <returns>The original <see cref="IServiceCollection"/> for chaining.</returns>
public IServiceCollection AddValidator<TValidator>()
where TValidator : IServiceCollectionValidator, new()
{
return services.AddValidator(new TValidator());
}

/// <summary>
/// Performs manual validation of the <see cref="IServiceCollection"/> using all registered validators.
/// </summary>
/// <param name="services">The service collection to validate.</param>
/// <param name="options">Optional parameter providing additional options for validation. Can be <c>null</c>.</param>
/// <returns>The original <see cref="IServiceCollection"/> for chaining.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if any of the registered validators report errors. The exception message contains all aggregated validation errors.
/// </exception>
/// <remarks>
/// This method is a temporary workaround. The default DI container does not natively support
/// collecting validation errors at build time. Ideally, validators would be invoked automatically
/// during <c>BuildServiceProvider</c>, and all errors would be aggregated into a single exception
/// thrown by the DI engine.
/// </remarks>
public static IServiceCollection Validate(this IServiceCollection services, object? options = null)
{
var validationResult = services
.GetValidators()
.Select(validator => validator.Validate(services, options))
.Aggregate(ValidationResult.Success, (v1, v2) => v1 + v2);
/// <summary>
/// Registers an instance of <see cref="IServiceCollectionValidator"/> with the service collection.
/// </summary>
/// <param name="validator">The validator instance to register.</param>
/// <returns>The original <see cref="IServiceCollection"/> for chaining.</returns>
public IServiceCollection AddValidator(IServiceCollectionValidator validator)
{
return services.AddSingleton(validator);
}

if (!validationResult.IsSuccess)
/// <summary>
/// Performs manual validation of the <see cref="IServiceCollection"/> using all registered validators.
/// </summary>
/// <param name="options">Optional parameter providing additional options for validation. Can be <c>null</c>.</param>
/// <returns>The original <see cref="IServiceCollection"/> for chaining.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if any of the registered validators report errors. The exception message contains all aggregated validation errors.
/// </exception>
/// <remarks>
/// This method is a temporary workaround. The default DI container does not natively support
/// collecting validation errors at build time. Ideally, validators would be invoked automatically
/// during <c>BuildServiceProvider</c>, and all errors would be aggregated into a single exception
/// thrown by the DI engine.
/// </remarks>
public IServiceCollection Validate(object? options = null)
{
var message = string.Join(Environment.NewLine, validationResult.Errors);
var validationResult = services
.GetValidators()
.Select(validator => validator.Validate(services, options))
.Aggregate(ValidationResult.Success, (v1, v2) => v1 + v2);

throw new InvalidOperationException(message);
if (!validationResult.IsSuccess)
{
var message = string.Join(Environment.NewLine, validationResult.Errors);

throw new InvalidOperationException(message);
}

return services;
}

return services;
}
private IEnumerable<IServiceCollectionValidator> GetValidators()
{
return

private static IEnumerable<IServiceCollectionValidator> GetValidators(this IServiceCollection services)
{
return
from sd in services
where sd.Lifetime == ServiceLifetime.Singleton
&& !sd.IsKeyedService
&& sd.ServiceType == typeof(IServiceCollectionValidator)
&& sd.ImplementationInstance != null
select (IServiceCollectionValidator)sd.ImplementationInstance;
from sd in services

where sd is { Lifetime: ServiceLifetime.Singleton, IsKeyedService: false } && sd.ServiceType == typeof(IServiceCollectionValidator)

let validator = sd.ImplementationInstance as IServiceCollectionValidator

where validator != null

select validator;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ public interface IVisualIdentityInfoSource
VisualIdentityInfo<TDomainObject> GetVisualIdentityInfo<TDomainObject>();

VisualIdentityInfo<TDomainObject>? TryGetVisualIdentityInfo<TDomainObject>();

VisualIdentityInfo GetVisualIdentityInfo(Type domainObjectType);

VisualIdentityInfo? TryGetVisualIdentityInfo(Type domainObjectType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,32 @@ public class VisualIdentityInfoSource(IVisualIdentityPropertyExtractor propertyE
}

}).WithLock();
public VisualIdentityInfo<TDomainObject>? TryGetVisualIdentityInfo<TDomainObject>()
{
return (VisualIdentityInfo<TDomainObject>?)this.TryGetVisualIdentityInfo(typeof(TDomainObject));
}

public VisualIdentityInfo GetVisualIdentityInfo(Type domainObjectType)
{
return this.TryGetVisualIdentityInfo(domainObjectType) ?? throw this.GetMissedError(domainObjectType);
}

public VisualIdentityInfo<TDomainObject>? TryGetVisualIdentityInfo<TDomainObject>()
{
return (VisualIdentityInfo<TDomainObject>?)this.cache[typeof(TDomainObject)];
}
public VisualIdentityInfo? TryGetVisualIdentityInfo(Type domainObjectType)
{
return this.cache[domainObjectType];
}

public VisualIdentityInfo<TDomainObject> GetVisualIdentityInfo<TDomainObject>()
public VisualIdentityInfo<TDomainObject> GetVisualIdentityInfo<TDomainObject>()
{
return this.TryGetVisualIdentityInfo<TDomainObject>() ??
throw new Exception($"{nameof(VisualIdentityInfo)} for {typeof(TDomainObject).Name} not found");
return this.TryGetVisualIdentityInfo<TDomainObject>() ?? throw this.GetMissedError(typeof(TDomainObject));
}

private static VisualIdentityInfo<TDomainObject> CreateVisualIdentityInfo<TDomainObject>(Expression<Func<TDomainObject, string>> namePath)
private Exception GetMissedError(Type domainObjectType)
{
return new Exception($"{nameof(VisualIdentityInfo)} for {domainObjectType.Name} not found");
}

private static VisualIdentityInfo<TDomainObject> CreateVisualIdentityInfo<TDomainObject>(Expression<Func<TDomainObject, string>> namePath)
{
return new VisualIdentityInfo<TDomainObject>(namePath);
}
Expand Down
2 changes: 1 addition & 1 deletion src/__SolutionItems/CommonAssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[assembly: AssemblyProduct("CommonFramework")]
[assembly: AssemblyCompany("IvAt")]

[assembly: AssemblyVersion("1.6.3.0")]
[assembly: AssemblyVersion("1.6.4.0")]
[assembly: AssemblyInformationalVersion("changes at build")]

#if DEBUG
Expand Down
Loading