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
37 changes: 37 additions & 0 deletions src/LightInject.Tests/LazyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,43 @@ public void CanGetInstance_LazyForUnknownService_ReturnsFalse(string serviceName
Assert.False(container.CanGetInstance(typeof(Lazy<IFoo>), serviceName));
}

[Fact]
public void GetInstance_LazyForUnknownNamedService_ThrowsInvalidOperationException()
{
var container = CreateContainer();

container.Register<IFoo, Foo>();

var anotherLazyFoo = container.GetInstance<Lazy<IFoo>>("AnotherFoo");

Assert.Throws<InvalidOperationException>(() => anotherLazyFoo.Value);
}

[Fact]
public void GetInstance_LazyForUnknownNamedServiceWithFallback_ReturnsLazyThatResolvesNamedService()
{
var container = CreateContainer();

container.RegisterFallback((serviceType, serviceName) => serviceType == typeof(IFoo) && serviceName == "AnotherFoo", request => new Foo());

var anotherLazyFoo = container.GetInstance<Lazy<IFoo>>("AnotherFoo");

Assert.IsType<Foo>(anotherLazyFoo.Value);
}

[Fact]
public void GetInstance_LazyForKnownNamedService_ReturnsLazyThatResolvesNamedService()
{
var container = CreateContainer();

container.Register<IFoo, Foo>();
container.Register<IFoo, AnotherFoo>("AnotherFoo");

var anotherLazyFoo = container.GetInstance<Lazy<IFoo>>("AnotherFoo");

Assert.IsType<AnotherFoo>(anotherLazyFoo.Value);
}

[Fact]
public void GetInstance_NamedService_ReturnsLazyThatResolvesNamedService()
{
Expand Down
62 changes: 48 additions & 14 deletions src/LightInject/LightInject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4488,7 +4488,7 @@ private Action<IEmitter> CreateEmitMethodForUnknownService(Type serviceType, str
}
else if (serviceType.IsLazy())
{
emitter = CreateEmitMethodBasedOnLazyServiceRequest(serviceType);
emitter = CreateEmitMethodBasedOnLazyServiceRequest(serviceType, serviceName);
}
else if (serviceType.IsFuncWithParameters())
{
Expand Down Expand Up @@ -4667,27 +4667,55 @@ private Action<IEmitter> CreateEmitMethodForReadOnlyCollectionServiceRequest(Typ
};
}

private Action<IEmitter> CreateEmitMethodBasedOnLazyServiceRequest(Type serviceType)
private Action<IEmitter> CreateEmitMethodBasedOnLazyServiceRequest(Type serviceType, string serviceName)
{
var returnType = serviceType.GetTypeInfo().GenericTypeArguments.Last();
var createScopedLazyMethod = LazyHelper.CreateScopedLazyMethod.MakeGenericMethod(returnType);
return e =>

if (string.IsNullOrWhiteSpace(serviceName))
{
e.PushConstant(constants.Add(this), typeof(ServiceContainer));
var createScopedLazyMethod = LazyHelper.CreateScopedLazyMethod.MakeGenericMethod(returnType);
return e =>
{
e.PushConstant(constants.Add(this), typeof(ServiceContainer));

int scopeManagerIndex = CreateScopeManagerIndex();
int scopeManagerIndex = CreateScopeManagerIndex();

// Push the scope into the stack
e.PushArgument(1);
// Push the scope into the stack
e.PushArgument(1);

// Push the scope manager into the stack.
e.PushConstant(scopeManagerIndex, typeof(IScopeManager));
// Push the scope manager into the stack.
e.PushConstant(scopeManagerIndex, typeof(IScopeManager));

// Get the scope
e.Emit(OpCodes.Call, ScopeLoader.GetThisOrCurrentScopeMethod);
// Get the scope
e.Emit(OpCodes.Call, ScopeLoader.GetThisOrCurrentScopeMethod);

e.Emit(OpCodes.Call, createScopedLazyMethod);
};
e.Emit(OpCodes.Call, createScopedLazyMethod);
};
}
else
{
var createNamedScopedLazyMethod = LazyHelper.CreateNamedScopedLazyMethod.MakeGenericMethod(returnType);
return e =>
{
e.PushConstant(constants.Add(this), typeof(ServiceContainer));

int scopeManagerIndex = CreateScopeManagerIndex();

// Push the scope into the stack
e.PushArgument(1);

// Push the scope manager into the stack.
e.PushConstant(scopeManagerIndex, typeof(IScopeManager));

// Get the scope
e.Emit(OpCodes.Call, ScopeLoader.GetThisOrCurrentScopeMethod);

// Push serviceName
e.Emit(OpCodes.Ldstr, serviceName);

e.Emit(OpCodes.Call, createNamedScopedLazyMethod);
};
}
}

private ThreadSafeDictionary<string, ServiceRegistration> GetOpenGenericServiceRegistrations(Type openGenericServiceType)
Expand Down Expand Up @@ -8959,17 +8987,23 @@ internal static class LazyHelper
{
public static readonly MethodInfo CreateScopedLazyMethod;

public static readonly MethodInfo CreateNamedScopedLazyMethod;

public static readonly MethodInfo CreateScopedLazyFromDelegateMethod;

static LazyHelper()
{
CreateScopedLazyMethod = typeof(LazyHelper).GetTypeInfo().GetDeclaredMethod("CreateScopedLazy");
CreateNamedScopedLazyMethod = typeof(LazyHelper).GetTypeInfo().GetDeclaredMethod("CreateNamedScopedLazy");
CreateScopedLazyFromDelegateMethod = typeof(LazyHelper).GetTypeInfo().GetDeclaredMethod("CreateScopedLazyFromDelegate");
}

public static Lazy<T> CreateScopedLazy<T>(ServiceContainer serviceContainer, Scope scope)
=> new Lazy<T>(() => (T)serviceContainer.GetInstance(typeof(T), scope));

public static Lazy<T> CreateNamedScopedLazy<T>(ServiceContainer serviceContainer, Scope scope, string serviceName)
=> new Lazy<T>(() => (T)serviceContainer.GetInstance(typeof(T), scope, serviceName));

public static Lazy<T> CreateScopedLazyFromDelegate<T>(GetInstanceDelegate getInstanceDelegate, object[] constants, Scope scope)
=> new Lazy<T>(() => (T)getInstanceDelegate(constants, scope));
}
Expand Down
Loading