Skip to content

Commit

Permalink
All tests passed
Browse files Browse the repository at this point in the history
  • Loading branch information
seesharper committed Oct 15, 2024
1 parent fb5c064 commit 1d28ff7
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 19 deletions.
33 changes: 32 additions & 1 deletion src/LightInject.Tests/MicrosoftTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ internal override IServiceContainer CreateContainer()
options.EnableCurrentScope = false;
options.OptimizeForLargeObjectGraphs = false;
options.EnableOptionalArguments = false;
options.EnableVariance = true;
})
{
AssemblyScanner = new NoOpAssemblyScanner()
Expand Down Expand Up @@ -704,6 +705,35 @@ public void ClosedServicesPreferredOverOpenGenericServices()
Assert.IsType<FakeService>(service);
}

[Fact]
public void ResolvingEnumerableContainingOpenGenericServiceUsesCorrectSlot()
{
var container = CreateContainer();
var rootScope = container.BeginScope();

// Arrange
// TestServiceCollection collection = new();
// collection.AddTransient<IFakeOpenGenericService<PocoClass>, FakeService>();
// collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>));
// collection.AddSingleton<PocoClass>(); // needed for FakeOpenGenericService<>
// IServiceProvider provider = CreateServiceProvider(collection);

container.RegisterTransient<IFakeOpenGenericService<PocoClass>, FakeService>();
container.RegisterTransient(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>));
container.Register<PocoClass>(new RootScopeLifetime(rootScope));

// Act
IFakeOpenGenericService<PocoClass> service = rootScope.GetInstance<IFakeOpenGenericService<PocoClass>>();
IFakeOpenGenericService<PocoClass>[] services = rootScope.GetAllInstances<IFakeOpenGenericService<PocoClass>>().ToArray();

// Assert
Assert.IsType<FakeService>(service);
Assert.Equal(2, services.Length);
Assert.True(services.Any(s => s.GetType() == typeof(FakeService)));
Assert.True(services.Any(s => s.GetType() == typeof(FakeOpenGenericService<PocoClass>)));
}


[Fact]
public void AttemptingToResolveNonexistentServiceReturnsNull()
{
Expand Down Expand Up @@ -860,7 +890,8 @@ public void ResolvesMixedOpenClosedGenericsAsEnumerable()
Assert.NotNull(enumerable[1]);
Assert.NotNull(enumerable[2]);

// NOTE IS this important? Assert.Equal(instance, enumerable[2]);
Assert.Equal(instance, enumerable[2]);
// NOTE IS this important?
// Since we register the open generic service in flight, this comes last.
Assert.True(enumerable[0] is FakeService, string.Join(", ", enumerable.Select(e => e.GetType())));
Assert.IsType<FakeService>(enumerable[0]);
Expand Down
80 changes: 62 additions & 18 deletions src/LightInject/LightInject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#define USE_ASYNCDISPOSABLE
#endif


/*********************************************************************************
The MIT License (MIT)
Expand Down Expand Up @@ -38,6 +37,7 @@ LightInject version 6.6.4

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
Expand All @@ -52,7 +52,7 @@ LightInject version 6.6.4
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Performance")]
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("MaintainabilityRules", "SA1403", Justification = "One source file")]
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("DocumentationRules", "SA1649", Justification = "One source file")]

[assembly: DebuggerDisplay("{LightInject.TypeNameFormatter.GetHumanFriendlyTypeName(this)}", Target = typeof(Type))]
namespace LightInject
{
using System;
Expand Down Expand Up @@ -2618,13 +2618,15 @@ public LogEntry(LogLevel level, string message)

internal class EmitMethodInfo
{
public EmitMethodInfo(Action<IEmitter> emitMethod, int registrationOrder, bool createdFromWildCardService)
public EmitMethodInfo(Type serviceType, Action<IEmitter> emitMethod, int registrationOrder, bool createdFromWildCardService)
{
ServiceType = serviceType;
EmitMethod = emitMethod;
RegistrationOrder = registrationOrder;
CreatedFromWildcardService = createdFromWildCardService;
}

public Type ServiceType { get; }
public Action<IEmitter> EmitMethod { get; private set; }

public int RegistrationOrder { get; private set; }
Expand Down Expand Up @@ -2662,7 +2664,7 @@ public override bool Equals(object obj)

public override string ToString()
{
return ServiceType.Name + " - " + ServiceName;
return ServiceType + " - " + ServiceName;
}
}

Expand Down Expand Up @@ -2762,6 +2764,7 @@ public ServiceContainer(ContainerOptions options)
o.VarianceFilter = options.VarianceFilter;
o.EnableOptionalArguments = options.EnableOptionalArguments;
o.OptimizeForLargeObjectGraphs = options.OptimizeForLargeObjectGraphs;
o.AllowMultipleRegistrations = options.AllowMultipleRegistrations;
})
{
}
Expand Down Expand Up @@ -2970,16 +2973,16 @@ public IServiceRegistry Register(ServiceRegistration serviceRegistration)
serviceRegistration.Lifetime = DefaultLifetime;
}
var services = GetAvailableServices(serviceRegistration.ServiceType);
registrationOrder++;
serviceRegistration.RegistrationOrder = registrationOrder;
var sr = serviceRegistration;
services.AddOrUpdate(
serviceRegistration.ServiceName,
s => AddServiceRegistration(sr),
(k, existing) => UpdateServiceRegistration(existing, sr));
var serviceKey = new ServiceKey(serviceRegistration.ServiceType, serviceRegistration.ServiceName);
var registrationList = allRegistrations.GetOrAdd(serviceKey, t => new List<ServiceRegistration>());
registrationOrder++;
registrationList.Add(serviceRegistration);
serviceRegistration.RegistrationOrder = registrationOrder;
return this;
}

Expand Down Expand Up @@ -3417,8 +3420,8 @@ public IServiceRegistry RegisterOrdered(Type serviceType, ServiceRegistration[]
Action<IEmitter> emitAction = (emitter) => EmitEnumerable(emitters.ToArray(), serviceType, emitter);

Type enumerableType = typeof(IEnumerable<>).MakeGenericType(serviceType);

RegisterEmitMethod(enumerableType, string.Empty, emitAction);
registrationOrder++;
RegisterEmitMethod(enumerableType, string.Empty, registrationOrder, emitAction);
return this;
}

Expand Down Expand Up @@ -3930,8 +3933,8 @@ private Action<IEmitter> TryGetFallbackEmitMethod(Type serviceType, string servi
if (rule != null)
{
emitMethod = CreateServiceEmitterBasedOnFactoryRule(rule, serviceType, serviceName);

RegisterEmitMethod(serviceType, serviceName, emitMethod);
registrationOrder++;
RegisterEmitMethod(serviceType, serviceName, registrationOrder, emitMethod);
}

return emitMethod;
Expand Down Expand Up @@ -4064,15 +4067,15 @@ private Action<IEmitter> GetRegisteredEmitMethodWithoutMicrosoftCompatibility(Ty
private ServiceRegistration AddServiceRegistration(ServiceRegistration serviceRegistration)
{
var emitMethod = ResolveEmitMethod(serviceRegistration);
RegisterEmitMethod(serviceRegistration.ServiceType, serviceRegistration.ServiceName, emitMethod, serviceRegistration.IsCreatedFromWildcardService);
RegisterEmitMethod(serviceRegistration.ServiceType, serviceRegistration.ServiceName, serviceRegistration.RegistrationOrder, emitMethod, serviceRegistration.IsCreatedFromWildcardService);

return serviceRegistration;
}

private void RegisterEmitMethod(Type serviceType, string serviceName, Action<IEmitter> emitMethod, bool createdFromWildcardService = false)
private void RegisterEmitMethod(Type serviceType, string serviceName, int registrationOrder, Action<IEmitter> emitMethod, bool createdFromWildcardService = false)
{
registrationOrder++;
var emitMethodInfo = new EmitMethodInfo(emitMethod, registrationOrder, createdFromWildcardService);
// registrationOrder++;
var emitMethodInfo = new EmitMethodInfo(serviceType, emitMethod, registrationOrder, createdFromWildcardService);
allEmitters.GetOrAdd(new ServiceKey(serviceType, serviceName), _ => new List<EmitMethodInfo>()).Add(emitMethodInfo);

GetEmitMethods(serviceType).TryAdd(serviceName, emitMethod);
Expand All @@ -4090,7 +4093,7 @@ private ServiceRegistration UpdateServiceRegistration(ServiceRegistration existi
Action<IEmitter> emitMethod = ResolveEmitMethod(newRegistration);
var serviceEmitters = GetEmitMethods(newRegistration.ServiceType);
registrationOrder++;
var emitMethodInfo = new EmitMethodInfo(emitMethod, registrationOrder, false);
var emitMethodInfo = new EmitMethodInfo(existingRegistration.ServiceType, emitMethod, registrationOrder, false);
allEmitters.GetOrAdd(new ServiceKey(newRegistration.ServiceType, newRegistration.ServiceName), _ => new List<EmitMethodInfo>()).Add(emitMethodInfo);
serviceEmitters[newRegistration.ServiceName] = emitMethod;
return newRegistration;
Expand Down Expand Up @@ -4939,7 +4942,7 @@ private Action<IEmitter> CreateEmitMethodForEnumerableServiceServiceRequest(Type
}


var constructableOpenGenericServices = openGenericServiceRegistrations.Select(r => new { r.Lifetime, r.ServiceName, closedGenericImplementingType = GenericArgumentMapper.TryMakeGenericType(actualServiceType, r.ImplementingType) })
var constructableOpenGenericServices = openGenericServiceRegistrations.Select(r => new { r.RegistrationOrder, r.Lifetime, r.ServiceName, closedGenericImplementingType = GenericArgumentMapper.TryMakeGenericType(actualServiceType, r.ImplementingType) })
.Where(t => t.closedGenericImplementingType != null);

foreach (var constructableOpenGenericService in constructableOpenGenericServices)
Expand All @@ -4950,8 +4953,11 @@ private Action<IEmitter> CreateEmitMethodForEnumerableServiceServiceRequest(Type
ImplementingType = constructableOpenGenericService.closedGenericImplementingType,
ServiceName = constructableOpenGenericService.ServiceName,
Lifetime = CloneLifeTime(constructableOpenGenericService.Lifetime) ?? DefaultLifetime,
RegistrationOrder = constructableOpenGenericService.RegistrationOrder,
};
Register(serviceRegistration);
//Register(serviceRegistration);
AddServiceRegistration(serviceRegistration);

}
}

Expand All @@ -4973,7 +4979,7 @@ private Action<IEmitter> CreateEmitMethodForEnumerableServiceServiceRequest(Type
else
{
var serviceKeys = allEmitters.Keys.Where(k => actualServiceType.IsAssignableFrom(k.ServiceType) && k.ServiceName == serviceName).ToList();
emitMethods = serviceKeys.SelectMany(k => allEmitters[k]).Select(emi => emi.EmitMethod).ToList();
emitMethods = serviceKeys.SelectMany(k => allEmitters[k]).OrderBy(emi => emi.RegistrationOrder).Select(emi => emi.EmitMethod).ToList();
}

}
Expand Down Expand Up @@ -9360,4 +9366,42 @@ public static Type GetEnumerableType(this Type returnType) =>
private static Type CreateEnumerableType(Type type) =>
typeof(IEnumerable<>).MakeGenericType(type);
}

public static class TypeNameFormatter
{
public static string GetHumanFriendlyTypeName(Type type)
{
StringBuilder humanFriendlyName = new StringBuilder();
if (type.IsGenericType && !type.IsGenericTypeDefinition)
{

humanFriendlyName.Append(type.Name.Substring(0, type.Name.IndexOf('`')));
humanFriendlyName.Append('<');
foreach (Type argument in type.GenericTypeArguments)
{
humanFriendlyName.Append(GetHumanFriendlyTypeName(argument));
humanFriendlyName.Append(", ");
}
humanFriendlyName.Remove(humanFriendlyName.Length - 2, 2);
humanFriendlyName.Append('>');
}
else if (type.IsGenericTypeDefinition)
{
humanFriendlyName.Append(type.Name.Substring(0, type.Name.IndexOf('`')));
humanFriendlyName.Append('<');
foreach (Type parameter in type.GetGenericArguments())
{
humanFriendlyName.Append(parameter.Name);
humanFriendlyName.Append(", ");
}
humanFriendlyName.Remove(humanFriendlyName.Length - 2, 2);
humanFriendlyName.Append('>');
}
else
{
humanFriendlyName.Append(type.Name);
}
return humanFriendlyName.ToString();
}
}
}

0 comments on commit 1d28ff7

Please sign in to comment.