Skip to content
Open
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
6 changes: 6 additions & 0 deletions src/Scrutor/IImplementationTypeFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ public interface IImplementationTypeFilter : IFluentInterface
/// <exception cref="ArgumentNullException">If the <paramref name="types"/> argument is <c>null</c>.</exception>
IImplementationTypeFilter AssignableToAny(IEnumerable<Type> types);

/// <summary>
/// Will match all types that are assignable to ServiceKeyAttribute />.
/// </summary>
/// <returns></returns>
IImplementationTypeFilter WithKeyName();

/// <summary>
/// Will match all types that has an attribute of type <typeparamref name="T"/> defined.
/// </summary>
Expand Down
6 changes: 5 additions & 1 deletion src/Scrutor/ImplementationTypeFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ public IImplementationTypeFilter AssignableToAny(IEnumerable<Type> types)
return Where(t => types.Any(t.IsBasedOn));
}


public IImplementationTypeFilter WithKeyName()
=> WithAttribute<ServiceKeyAttribute>();

public IImplementationTypeFilter WithAttribute<T>() where T : Attribute
{
return WithAttribute(typeof(T));
Expand All @@ -52,7 +56,7 @@ public IImplementationTypeFilter WithAttribute(Type attributeType)
Preconditions.NotNull(attributeType, nameof(attributeType));

IncludeCompilerGeneratedTypes |= attributeType == typeof(CompilerGeneratedAttribute);
return Where(t => t.HasAttribute(attributeType));
return Where(t => t.HasAttribute(attributeType) );
}

public IImplementationTypeFilter WithAttribute<T>(Func<T, bool> predicate) where T : Attribute
Expand Down
6 changes: 5 additions & 1 deletion src/Scrutor/LifetimeSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,11 @@ void ISelector.Populate(IServiceCollection services, RegistrationStrategy? strat

var lifetime = GetOrAddLifetime(lifetimes, implementationType);

var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
ServiceKeyAttribute? attr = implementationType.GetCustomAttribute<ServiceKeyAttribute>();

ServiceDescriptor descriptor = attr != null
? new ServiceDescriptor(serviceType, attr.Name, implementationType, lifetime)
: new ServiceDescriptor(serviceType, implementationType, lifetime);

strategy.Apply(services, descriptor);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Scrutor/Scrutor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NoWarn>$(NoWarn);CS1591</NoWarn>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be left on 😄

<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageId>Scrutor</PackageId>
<PackageTags>Dependency;Injection;DI;Scanning;Conventions;Decoration</PackageTags>
Expand Down
11 changes: 11 additions & 0 deletions src/Scrutor/ServiceKeyAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using JetBrains.Annotations;
using System;

namespace Scrutor;

[PublicAPI]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class ServiceKeyAttribute(string name): Attribute
{
public string Name { get; } = name ?? throw new ArgumentNullException(nameof(name));
}
39 changes: 37 additions & 2 deletions test/Scrutor.Tests/ScanningTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -584,10 +584,35 @@ public void ShouldAllowOptInToCompilerGeneratedTypes()
.AsSelf()
.WithTransientLifetime());
});

var compilerGeneratedSubclass = provider.GetService<AllowedCompilerGeneratedSubclass>();
Assert.NotNull(compilerGeneratedSubclass);
}


[Fact]
public void KeyedTransientService()
{
ServiceProvider provider = ConfigureProvider(services =>
{
services.Scan(scan => scan
.FromAssemblyOf<INamedService>()
.AddClasses(classes => classes.AssignableTo<INamedService>())
.AsImplementedInterfaces()
.WithTransientLifetime());

var namedServices = services.GetDescriptors<INamedService>();

Assert.Equal(2, namedServices.Count(x => x.ServiceType == typeof(INamedService)));
});

var transientKeyedService1 = provider.GetKeyedService<INamedService>("t1");
Assert.NotNull(transientKeyedService1);

var transientKeyedService2 = provider.GetKeyedService<INamedService>("t1");
Assert.NotNull(transientKeyedService2);

}
}

// ReSharper disable UnusedTypeParameter
Expand All @@ -597,6 +622,7 @@ public interface ITransientService { }
[ServiceDescriptor(typeof(ITransientService))]
public class TransientService1 : ITransientService { }


public class TransientService2 : ITransientService, IOtherInheritance { }

public class TransientService : ITransientService, IEnumerable<string>
Expand Down Expand Up @@ -671,7 +697,7 @@ public class DefaultAttributes : IDefault3Level2, IDefault1, IDefault2 { }
[CompilerGenerated]
public class CompilerGenerated { }

public class CombinedService2: IDefault1, IDefault2, IDefault3Level2 { }
public class CombinedService2 : IDefault1, IDefault2, IDefault3Level2 { }

public interface IGenericAttribute { }

Expand All @@ -688,6 +714,15 @@ public abstract class AllowedCompilerGeneratedBase { }

[CompilerGenerated]
public class AllowedCompilerGeneratedSubclass : AllowedCompilerGeneratedBase { }


public interface INamedService { }

[ServiceKey("t1")]
public class NameService1 : INamedService { }

[ServiceKey("t2")]
public class NameService2 : INamedService { }
}

namespace Scrutor.Tests.ChildNamespace
Expand Down