### Install Scrutor Package Source: https://github.com/khellang/scrutor/blob/master/README.md Commands to install the Scrutor NuGet package using either the Package Manager Console or the .NET Core CLI. ```powershell Install-Package Scrutor ``` ```bash dotnet add package Scrutor ``` -------------------------------- ### WithServiceKey for Keyed Services Registration Source: https://context7.com/khellang/scrutor/llms.txt Illustrates how to register keyed services during assembly scanning using the `WithServiceKey()` method. This allows for both static key assignments and dynamic key generation based on the implementation type. The examples show different scanning configurations and how to resolve these keyed services. ```csharp var services = new ServiceCollection(); // Register with a static key services.Scan(scan => scan .FromTypes() .AsImplementedInterfaces() .WithServiceKey("my-key") .WithSingletonLifetime()); // Register with dynamic key based on type services.Scan(scan => scan .FromTypes() .AsSelf() .WithServiceKey(type => type.Name) .WithTransientLifetime()); var provider = services.BuildServiceProvider(); // Resolve keyed services var service1 = provider.GetRequiredKeyedService("TransientService1"); var service2 = provider.GetRequiredKeyedService("TransientService2"); // Multiple services with same key var allKeyed = provider.GetKeyedServices("my-key"); ``` -------------------------------- ### Scrutor Service Type Registration Methods Source: https://context7.com/khellang/scrutor/llms.txt Illustrates various service type selectors in Scrutor for registering scanned types, including AsSelf, As, AsImplementedInterfaces, AsSelfWithInterfaces, and AsMatchingInterface, with examples for different lifetime scopes and custom filtering. ```csharp var services = new ServiceCollection(); services.Scan(scan => scan .FromAssemblyOf() // Register as the concrete type itself .AddClasses(classes => classes.AssignableTo()) .AsSelf() .WithTransientLifetime() // Register as a specific interface .AddClasses(classes => classes.AssignableTo()) .As() .WithScopedLifetime() // Register as all implemented interfaces // If MyServiceImpl implements IService, IDisposable, it registers for both .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithSingletonLifetime() // Filter which interfaces to register .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces(type => type != typeof(IDisposable)) .WithTransientLifetime() // Register as self AND forward interfaces to the same instance // Ensures singleton behavior across all registrations .AddClasses(classes => classes.AssignableTo()) .AsSelfWithInterfaces() .WithSingletonLifetime() // Auto-match interface by naming convention // MyService registers as IMyService .AddClasses() .AsMatchingInterface() .WithTransientLifetime() // Matching interface with custom filter .AddClasses() .AsMatchingInterface((implementationType, filter) => filter.InNamespaceOf(implementationType)) .WithTransientLifetime() // Custom type selector .AddClasses(classes => classes.AssignableTo()) .As(implementationType => implementationType.GetInterfaces() .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IHandler<>))) .WithTransientLifetime()); var provider = services.BuildServiceProvider(); // AsSelfWithInterfaces ensures true singleton across all service types var instance1 = provider.GetRequiredService(); var instance2 = provider.GetRequiredService(); // instance1 and instance2 are the same object ``` -------------------------------- ### Scrutor AddClasses Type Filtering Examples Source: https://context7.com/khellang/scrutor/llms.txt Demonstrates filtering public, non-abstract classes from assemblies using AddClasses with predicates like AssignableTo, InNamespaces, WithAttribute, and custom Where clauses. It also shows how to include non-public classes. ```csharp var services = new ServiceCollection(); services.Scan(scan => scan .FromAssemblyOf() // Register all public classes (default behavior) .AddClasses() .AsSelf() .WithTransientLifetime() // Filter by interface implementation .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithScopedLifetime() // Filter by namespace .AddClasses(classes => classes.InNamespaces("MyApp.Services", "MyApp.Handlers")) .AsImplementedInterfaces() .WithTransientLifetime() // Filter by exact namespace (excludes child namespaces) .AddClasses(classes => classes.InExactNamespaceOf()) .AsSelf() .WithTransientLifetime() // Filter by attribute .AddClasses(classes => classes.WithAttribute()) .AsImplementedInterfaces() .WithSingletonLifetime() // Filter with attribute predicate .AddClasses(classes => classes.WithAttribute(attr => attr.IsEnabled)) .AsImplementedInterfaces() .WithTransientLifetime() // Exclude types with specific attribute .AddClasses(classes => classes.WithoutAttribute()) .AsImplementedInterfaces() .WithTransientLifetime() // Multiple assignable types .AddClasses(classes => classes.AssignableToAny(typeof(IHandler), typeof(IValidator))) .AsImplementedInterfaces() .WithTransientLifetime() // Custom predicate .AddClasses(classes => classes.Where(type => type.Name.EndsWith("Service"))) .AsImplementedInterfaces() .WithTransientLifetime() // Include internal/non-public classes .AddClasses(publicOnly: false) .AsSelf() .WithTransientLifetime()); var provider = services.BuildServiceProvider(); ``` -------------------------------- ### Decorate Services with Factory Functions Source: https://context7.com/khellang/scrutor/llms.txt Shows how to use factory functions for decoration, allowing access to the inner service instance and the IServiceProvider for dependency resolution. ```csharp var services = new ServiceCollection(); services.AddSingleton(); services.AddSingleton(); services.Decorate((inner, provider) => { var logger = provider.GetRequiredService(); return new LoggingDecorator(inner, logger); }); ``` -------------------------------- ### Configure Assembly Selection Sources Source: https://context7.com/khellang/scrutor/llms.txt Shows various methods for selecting assemblies to scan, including FromAssemblyOf, FromAssembliesOf, FromApplicationDependencies, and FromTypes. ```csharp var services = new ServiceCollection(); services.Scan(scan => scan .FromAssemblyOf() .AddClasses() .AsImplementedInterfaces() .WithTransientLifetime() .FromAssembliesOf(typeof(IService1), typeof(IService2)) .AddClasses() .AsSelf() .WithScopedLifetime() .FromApplicationDependencies() .AddClasses(classes => classes.InNamespaces("MyApp.Services")) .AsImplementedInterfaces() .WithSingletonLifetime()); services.Scan(scan => scan .FromTypes() .AsImplementedInterfaces() .WithSingletonLifetime()); ``` -------------------------------- ### Accessing Original Services with DecoratedService Handle Source: https://context7.com/khellang/scrutor/llms.txt Demonstrates how to capture a handle to decorated services to retrieve the original, non-decorated service instance from the provider. This is useful for inspecting or interacting with the underlying service. ```csharp var services = new ServiceCollection(); services.AddSingleton(); services.AddSingleton(); // Capture handle to decorated services services.Decorate(out DecoratedService decorated); var provider = services.BuildServiceProvider(); // Get the decorated (wrapper) service var instance = provider.GetRequiredService(); // instance is Decorator wrapping Decorated // Get the original service using the handle var original = provider.GetRequiredDecoratedService(decorated); // original is Decorated (the inner service) // Get all original services when multiple were registered IEnumerable allOriginals = provider.GetDecoratedServices(decorated); // Returns: [Decorated, OtherDecorated] // Multiple decoration levels with handles DecoratedService decorated1, decorated2; services.Decorate(out decorated1); services.Decorate(out decorated2); // decorated1 gives access to the first decorated service // decorated2 gives access to the second decorated service var underlying1 = provider.GetRequiredDecoratedService(decorated1); var underlying2 = provider.GetRequiredDecoratedService(decorated2); ``` -------------------------------- ### Decorate Registered Services Source: https://github.com/khellang/scrutor/blob/master/README.md Illustrates how to wrap existing service registrations with decorator patterns, including support for dependency injection within the decorator itself. ```csharp var collection = new ServiceCollection(); collection.AddSingleton(); collection.Decorate(); collection.Decorate((inner, provider) => new OtherDecorator(inner, provider.GetRequiredService())); var serviceProvider = collection.BuildServiceProvider(); ``` -------------------------------- ### Decorate Services with Decorator Types Source: https://context7.com/khellang/scrutor/llms.txt Demonstrates how to wrap existing service registrations using the Decorate method. This allows chaining multiple decorators to a single service interface. ```csharp using Microsoft.Extensions.DependencyInjection; using Scrutor; public interface IDecoratedService { string GetValue(); } public class Decorated : IDecoratedService { public string GetValue() => "Original"; } var services = new ServiceCollection(); services.AddSingleton(); services.Decorate(); services.Decorate(); var provider = services.BuildServiceProvider(); var instance = provider.GetRequiredService(); ``` -------------------------------- ### Handle Duplicate Registrations with RegistrationStrategy Source: https://context7.com/khellang/scrutor/llms.txt Shows how to control behavior when a service is already registered in the container. Strategies include appending, skipping, replacing existing registrations, or throwing an exception. ```csharp var services = new ServiceCollection(); // Default behavior: Append (adds duplicate registrations) services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithTransientLifetime()); // Skip: Don't register if service type already exists services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .UsingRegistrationStrategy(RegistrationStrategy.Skip) .AsImplementedInterfaces() .WithTransientLifetime()); // Replace: Remove existing registrations for the service type services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .UsingRegistrationStrategy(RegistrationStrategy.Replace()) .AsImplementedInterfaces() .WithTransientLifetime()); // Replace by implementation type instead of service type services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .UsingRegistrationStrategy(RegistrationStrategy.Replace(ReplacementBehavior.ImplementationType)) .AsImplementedInterfaces() .WithTransientLifetime()); // Replace by both service and implementation type services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .UsingRegistrationStrategy(RegistrationStrategy.Replace(ReplacementBehavior.All)) .AsImplementedInterfaces() .WithTransientLifetime()); // Throw: Exception if service type already registered try { services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .UsingRegistrationStrategy(RegistrationStrategy.Throw) .AsImplementedInterfaces() .WithTransientLifetime()); } catch (DuplicateTypeRegistrationException ex) { Console.WriteLine($"Duplicate registration for: {ex.ServiceType}"); } var provider = services.BuildServiceProvider(); ``` -------------------------------- ### Configure Service Lifetimes with Scrutor Source: https://context7.com/khellang/scrutor/llms.txt Demonstrates how to apply different service lifetimes to classes discovered via assembly scanning. It includes static lifetime methods and a dynamic approach using a delegate to determine lifetime based on implementation type. ```csharp var services = new ServiceCollection(); services.Scan(scan => scan .FromAssemblyOf() // Transient: new instance every time .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithTransientLifetime() // Scoped: one instance per scope .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithScopedLifetime() // Singleton: one instance for application lifetime .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithSingletonLifetime() // Dynamic lifetime based on type .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithLifetime(implementationType => { if (implementationType.Name.Contains("Singleton")) return ServiceLifetime.Singleton; if (implementationType.Name.Contains("Scoped")) return ServiceLifetime.Scoped; return ServiceLifetime.Transient; })); var provider = services.BuildServiceProvider(); ``` -------------------------------- ### Scan Assemblies for Service Registration Source: https://github.com/khellang/scrutor/blob/master/README.md Demonstrates how to use the Scan extension method to register classes from an assembly based on interface implementation and lifetime requirements. ```csharp var collection = new ServiceCollection(); collection.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithTransientLifetime() .AddClasses(classes => classes.AssignableTo()) .As() .WithScopedLifetime() .AddClasses(classes => classes.AssignableTo(typeof(IOpenGeneric<>))) .AsImplementedInterfaces() .AddClasses(classes => classes.AssignableTo(typeof(IQueryHandler<,>))) .AsImplementedInterfaces()); ``` -------------------------------- ### ServiceDescriptorAttribute for Attribute-Based Registration Source: https://context7.com/khellang/scrutor/llms.txt Demonstrates using the ServiceDescriptorAttribute to configure service registration directly on classes. This includes specifying service types, lifetimes, and keyed services. The code shows how to apply these attributes and then use Scrutor's Scan method with `UsingAttributes()` to register them. ```csharp using Microsoft.Extensions.DependencyInjection; using Scrutor; // Basic attribute with interface type [ServiceDescriptor(typeof(ITransientService))] public class TransientService1 : ITransientService { } // Generic attribute syntax (C# 11+) [ServiceDescriptor] public class MyServiceImpl : IMyService { } // Specify lifetime [ServiceDescriptor(typeof(IScopedService), ServiceLifetime.Scoped)] public class ScopedService : IScopedService { } [ServiceDescriptor(ServiceLifetime.Singleton)] public class SingletonService : ISingletonService { } // Multiple attributes for multiple registrations [ServiceDescriptor(typeof(ITransientServiceToCombine))] [ServiceDescriptor(typeof(IScopedServiceToCombine), ServiceLifetime.Scoped)] [ServiceDescriptor(typeof(ISingletonServiceToCombine), ServiceLifetime.Singleton)] public class CombinedService : ITransientServiceToCombine, IScopedServiceToCombine, ISingletonServiceToCombine { } // Default attribute (registers as self and all interfaces) [ServiceDescriptor] public class DefaultAttributeService : IDefault1, IDefault2 { } // Keyed service registration [ServiceDescriptor(typeof(IKeyedService), ServiceLifetime.Transient, "my-key")] public class KeyedTransientService : IKeyedService { } [ServiceDescriptor(ServiceLifetime.Scoped, "scoped-key")] public class KeyedScopedService : IKeyedService { } // Usage var services = new ServiceCollection(); services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.WithAttribute(), .UsingAttributes()); // Or filter to specific interfaces services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .UsingAttributes()); var provider = services.BuildServiceProvider(); // Resolve services var transient = provider.GetRequiredService(); var scoped = provider.GetRequiredService(); var keyed = provider.GetRequiredKeyedService("my-key"); ``` -------------------------------- ### Register Services via Assembly Scanning Source: https://context7.com/khellang/scrutor/llms.txt Demonstrates the Scan method to automatically register services based on interface implementation and service lifetime. This approach reduces manual registration boilerplate in ServiceCollection. ```csharp using Microsoft.Extensions.DependencyInjection; using Scrutor; var services = new ServiceCollection(); services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithTransientLifetime() .AddClasses(classes => classes.AssignableTo()) .As() .WithScopedLifetime() .AddClasses(classes => classes.AssignableTo(typeof(IQueryHandler<,>))) .AsImplementedInterfaces() .WithScopedLifetime()); var provider = services.BuildServiceProvider(); ``` -------------------------------- ### Service Type Registration Methods Source: https://context7.com/khellang/scrutor/llms.txt Explains the different ways to register scanned service types using methods like AsSelf(), As(), AsImplementedInterfaces(), AsSelfWithInterfaces(), and AsMatchingInterface(). ```APIDOC ## Service Type Registration Methods Service type selectors determine how scanned types are registered. The following methods provide different registration strategies: ### Registration Methods - **AsSelf()**: Registers the service with its concrete type. ```csharp .AddClasses(classes => classes.AssignableTo()) .AsSelf() .WithTransientLifetime() ``` - **As()**: Registers the service with a specific interface or base type. ```csharp .AddClasses(classes => classes.AssignableTo()) .As() .WithScopedLifetime() ``` - **AsImplementedInterfaces()**: Registers the service with all of its implemented interfaces. ```csharp .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithSingletonLifetime() ``` You can also filter which interfaces to register: ```csharp .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces(type => type != typeof(IDisposable)) .WithTransientLifetime() ``` - **AsSelfWithInterfaces()**: Registers the service with its concrete type and all implemented interfaces, ensuring singleton behavior across all registrations. ```csharp .AddClasses(classes => classes.AssignableTo()) .AsSelfWithInterfaces() .WithSingletonLifetime() ``` - **AsMatchingInterface()**: Auto-matches interfaces by naming convention (e.g., `MyService` registers as `IMyService`). ```csharp .AddClasses() .AsMatchingInterface() .WithTransientLifetime() ``` A custom filter can be applied: ```csharp .AddClasses() .AsMatchingInterface((implementationType, filter) => filter.InNamespaceOf(implementationType)) .WithTransientLifetime() ``` - **As(Func> selector)**: Allows for custom type selection logic. ```csharp .AddClasses(classes => classes.AssignableTo()) .As(implementationType => implementationType.GetInterfaces() .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IHandler<>))) .WithTransientLifetime() ``` ### Response N/A (These methods configure dependency injection services) ``` -------------------------------- ### Open Generic Type Decoration in C# Source: https://context7.com/khellang/scrutor/llms.txt Illustrates how Scrutor can decorate open generic types, enabling a single decorator registration to apply to all closed generic implementations. This simplifies the decoration of generic service interfaces. ```csharp public interface IEventHandler where TEvent : IEvent { Task HandleAsync(TEvent @event); } public class MyEvent : IEvent { } public class MyEventHandler : IEventHandler { public Task HandleAsync(MyEvent @event) => Task.CompletedTask; } public class LoggingEventHandler : IEventHandler where TEvent : IEvent { private readonly IEventHandler _inner; private readonly ILogger _logger; public LoggingEventHandler(IEventHandler inner, ILogger logger) { _inner = inner; _logger = logger; } public async Task HandleAsync(TEvent @event) { _logger.Log($ ``` ```csharp Handling {typeof(TEvent).Name} }); await _inner.HandleAsync(@event); _logger.Log($"Handled {typeof(TEvent).Name} }); } } // Usage var services = new ServiceCollection(); services.AddSingleton(); // Register handlers via scanning services.Scan(scan => scan .FromAssemblyOf() .AddClasses(c => c.AssignableTo(typeof(IEventHandler<>))) .AsImplementedInterfaces() .WithTransientLifetime()); // Decorate all IEventHandler<> implementations services.Decorate(typeof(IEventHandler<>), typeof(LoggingEventHandler<>)); var provider = services.BuildServiceProvider(); // All event handlers are now decorated var handler = provider.GetRequiredService>(); // handler is LoggingEventHandler wrapping MyEventHandler ``` -------------------------------- ### Scanning Compiler-Generated Types with C# Source: https://context7.com/khellang/scrutor/llms.txt Explains how to configure Scrutor to include compiler-generated types during assembly scanning, which is essential for frameworks like Avalonia that utilize them for UI elements. This is achieved by using `.WithAttribute()`. ```csharp using System.Runtime.CompilerServices; using Avalonia.Controls; var services = new ServiceCollection(); services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes // Opt-in to compiler-generated types .WithAttribute() // Filter to specific namespace .InNamespaces("MyApp.Desktop.Views") // Filter to window/control types .AssignableToAny(typeof(Window), typeof(UserControl))) // Register as self to avoid interface complexity .AsSelf() .WithSingletonLifetime()); var provider = services.BuildServiceProvider(); // Compiler-generated views are now resolvable var mainWindow = provider.GetService(); ``` -------------------------------- ### Scan Compiled UI Types Source: https://github.com/khellang/scrutor/blob/master/README.md Shows how to include compiler-generated types during assembly scanning, which is often required when working with UI frameworks like Avalonia. ```csharp .AddClasses(classes => classes .WithAttribute() .InNamespaces("MyApp.Desktop.Views") .AssignableToAny( typeof(Window), typeof(UserControl) ) .AsSelf() .WithSingletonLifetime()) ``` -------------------------------- ### TryDecorate for Safe Service Decoration Source: https://context7.com/khellang/scrutor/llms.txt Illustrates the use of TryDecorate to safely attempt service decoration. It returns a boolean indicating success, preventing exceptions if the service type is not registered. ```csharp var services = new ServiceCollection(); // Returns false if service is not registered bool decorated = services.TryDecorate(); services.AddSingleton(); // Now succeeds decorated = services.TryDecorate(); ``` -------------------------------- ### AddClasses - Type Filtering Source: https://context7.com/khellang/scrutor/llms.txt Demonstrates how to use the AddClasses method with various filters to select specific types for registration, such as filtering by assignable types, namespaces, attributes, or custom predicates. ```APIDOC ## AddClasses - Type Filtering The `AddClasses()` method filters public, non-abstract classes from selected assemblies. It accepts an optional action to further filter types using predicates like `AssignableTo`, `InNamespaces`, `WithAttribute`, and more. ### Method `AddClasses(Action typeFilter = null, bool publicOnly = true)` ### Endpoint N/A (This is a library method, not an API endpoint) ### Parameters #### Type Filter Predicates - **AssignableTo** (Type) - Filters types that implement a specific interface or inherit from a base class. - **InNamespaces** (params string[]) - Filters types within the specified namespaces. - **InExactNamespaceOf** (Type) - Filters types within the exact namespace of the provided type (excludes child namespaces). - **WithAttribute** (Type) - Filters types that have a specific attribute applied. - **WithAttribute** (Type, Func) - Filters types with a specific attribute and applies a predicate to the attribute instance. - **WithoutAttribute** (Type) - Excludes types with a specific attribute. - **Where** (Func) - Applies a custom predicate to filter types. #### Other Parameters - **publicOnly** (bool) - Defaults to `true`. If `false`, includes internal and non-public classes. ### Request Example ```csharp services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithScopedLifetime()); ``` ### Response N/A (This method configures dependency injection services) ``` -------------------------------- ### Service Lifetime Configuration Source: https://context7.com/khellang/scrutor/llms.txt Methods to define the lifecycle of registered services, including transient, scoped, singleton, and dynamic lifetime resolution. ```APIDOC ## Service Lifetime Configuration ### Description Defines how long a service instance persists within the dependency injection container. ### Methods - **WithTransientLifetime()**: Creates a new instance every time the service is requested. - **WithScopedLifetime()**: Creates one instance per scope (e.g., per HTTP request). - **WithSingletonLifetime()**: Creates one instance for the entire application lifetime. - **WithLifetime(Func)**: Dynamically determines the lifetime based on the implementation type. ### Request Example ```csharp services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .AsImplementedInterfaces() .WithTransientLifetime()); ``` ``` -------------------------------- ### Registration Strategy API Source: https://context7.com/khellang/scrutor/llms.txt Configures how the container handles duplicate service registrations when a type is already registered. ```APIDOC ## RegistrationStrategy API ### Description Controls the behavior when a service type is already registered in the ServiceCollection. ### Strategies - **RegistrationStrategy.Skip**: Ignores new registrations if the service type already exists. - **RegistrationStrategy.Append**: Adds the new registration alongside existing ones (default). - **RegistrationStrategy.Replace()**: Removes existing registrations for the service type. - **RegistrationStrategy.Throw**: Throws a DuplicateTypeRegistrationException if the service type is already registered. ### Request Example ```csharp services.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) .UsingRegistrationStrategy(RegistrationStrategy.Replace()) .AsImplementedInterfaces() .WithTransientLifetime()); ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.