Skip to content

Commit

Permalink
Lamar supports keyed services through the .NET spec
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremydmiller committed Nov 15, 2023
1 parent 0c35818 commit cab25a0
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,10 @@ public void get_a_named_instance()
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/StructureMap.Testing/Examples/Resolving/SimpleScenarios.cs#L26-L48' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_getinstance-by-name-1' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## .NET Core Keyed Services

While Lamar (and StructureMap before that) has supported the idea of named service registrations in production applications since 2004 (!),
.NET finally discovered this usage in .NET 8. Lamar 12.1.0 introduces native support for [keyed services](https://weblogs.asp.net/ricardoperes/net-8-dependency-injection-changes-keyed-services) according to the new .NET standard as shown below:

snippet: sample_adding_keyed_services
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using StructureMap.Testing.Widget;
using Xunit;

namespace Lamar.Testing.IoC.Acceptance;

#if NET8_0_OR_GREATER
public class IKeyedServiceProvider_compliance
{
#region sample_adding_keyed_services

[Fact]
public void register_by_name_using_dot_net_core_syntax()
{
var container = Container.For(services =>
{
services.AddKeyedSingleton<IWidget, AWidget>("one");
services.AddKeyedScoped<IWidget>("two", (_, _) => new BWidget());
services.AddKeyedSingleton<IWidget>("three", new CWidget());
services.AddKeyedSingleton<CWidget>("C1");
services.AddKeyedSingleton<CWidget>("C2");
services.AddKeyedSingleton<CWidget>("C3");
});

container.GetInstance<IWidget>("one").ShouldBeOfType<AWidget>();
container.GetKeyedService<IWidget>("one").ShouldBeOfType<AWidget>();

container.GetInstance<IWidget>("two").ShouldBeOfType<BWidget>();
container.GetKeyedService<IWidget>("two").ShouldBeOfType<BWidget>();

container.GetInstance<IWidget>("three").ShouldBeOfType<CWidget>();
container.GetKeyedService<IWidget>("three").ShouldBeOfType<CWidget>();

container.GetInstance<CWidget>("C1").ShouldBeOfType<CWidget>();
container.GetKeyedService<CWidget>("C2").ShouldBeOfType<CWidget>();

container.GetKeyedService<CWidget>("C2")
.ShouldBeSameAs(container.GetKeyedService<CWidget>("C2"));

container.GetKeyedService<CWidget>("C2")
.ShouldNotBeSameAs(container.GetKeyedService<CWidget>("C3"));
}

#endregion
}
#endif
6 changes: 6 additions & 0 deletions src/Lamar/IServiceContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@
using System.Reflection;
using JasperFx.CodeGeneration.Model;
using Lamar.Diagnostics;
using Microsoft.Extensions.DependencyInjection;

namespace Lamar;

public interface IServiceContext : IServiceProvider, IDisposable, IAsyncDisposable

#if NET8_0_OR_GREATER
,IKeyedServiceProvider
#endif

{
/// <summary>
/// Provides queryable access to the configured serviceType's and Instances of this Container.
Expand Down
38 changes: 37 additions & 1 deletion src/Lamar/IoC/Instances/Instance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,45 @@ internal IEnumerable<Assembly> ReferencedAssemblies()

public static Instance For(ServiceDescriptor service)
{
if (service.ImplementationInstance is Instance instance)
#if NET8_0_OR_GREATER
if (service.IsKeyedService)
{
var name = service.ServiceKey?.ToString();
Instance instance = null;
if (service.KeyedImplementationInstance != null)
{
instance = new ObjectInstance(service.ServiceType, service.KeyedImplementationInstance);
}
else if (service.KeyedImplementationFactory != null)
{
Func<IServiceProvider, object> factory = s =>
{
if (service.KeyedImplementationFactory != null)
{
return service.KeyedImplementationFactory(s, name);
}
return null;
};

instance = new LambdaInstance(service.ServiceType, factory, service.Lifetime);
}
else
{
instance = new ConstructorInstance(service.ServiceType, service.KeyedImplementationType, service.Lifetime);
}

if (name.IsNotEmpty()) instance.Name = name;

return instance;


}
#endif

if (service.ImplementationInstance is Instance i)
{
return i;
}

if (service.ImplementationInstance != null)
Expand Down
12 changes: 12 additions & 0 deletions src/Lamar/IoC/Scope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -473,4 +473,16 @@ public Lazy<T> LazyFor<T>()
{
return new Lazy<T>(GetInstance<T>);
}

#if NET8_0_OR_GREATER
public object GetKeyedService(Type serviceType, object serviceKey)
{
return TryGetInstance(serviceType, serviceKey.ToString());
}

public object GetRequiredKeyedService(Type serviceType, object serviceKey)
{
return GetInstance(serviceType, serviceKey.ToString());
}
#endif
}

0 comments on commit cab25a0

Please sign in to comment.