JasperFx / lamar

Fast Inversion of Control Tool and Successor to StructureMap
https://jasperfx.github.io/lamar
MIT License
575 stars 119 forks source link

Instance Hash Collisions for Large Service Registry #398

Open jwoodmanbuildium opened 7 months ago

jwoodmanbuildium commented 7 months ago

When an application has a large service graph (thousands of services), instance hash collisions occur, causing the wrong implementations to be resolved at runtime.

Exception info:

ClassName: System.InvalidCastException
Message: Unable to cast object of type '{ImplentationType}' to type '{ServiceType}'.

Stack Trace:
   at (Closure`6 , Scope )
   at Lamar.IoC.Instances.GeneratedInstance.<>c__DisplayClass5_1.<BuildFuncResolver>b__0(Scope s)
   at Lamar.IoC.Instances.GeneratedInstance.Resolve(Scope scope)
   at (Closure`9 , Scope )
   at Lamar.IoC.Instances.GeneratedInstance.<>c__DisplayClass5_1.<BuildFuncResolver>b__0(Scope s)
   at Lamar.IoC.Instances.GeneratedInstance.Resolve(Scope scope)
   at (ArrayClosure , Scope )
   at Lamar.IoC.Instances.GeneratedInstance.<>c__DisplayClass5_1.<BuildFuncResolver>b__0(Scope s)
   at Lamar.IoC.Instances.GeneratedInstance.Resolve(Scope scope)
   at (ArrayClosure , Scope )
   at Lamar.IoC.Instances.GeneratedInstance.<>c__DisplayClass5_1.<BuildFuncResolver>b__0(Scope s)
   at Lamar.IoC.Instances.GeneratedInstance.Resolve(Scope scope)
   at (ArrayClosure , Scope )
   at Lamar.IoC.Instances.GeneratedInstance.<>c__DisplayClass5_1.<BuildFuncResolver>b__0(Scope s)
   at Lamar.IoC.Instances.GeneratedInstance.Resolve(Scope scope)
   at (ArrayClosure , Scope )
   at Lamar.IoC.Instances.GeneratedInstance.<>c__DisplayClass5_1.<BuildFuncResolver>b__0(Scope s)
   at Lamar.IoC.Instances.GeneratedInstance.Resolve(Scope scope)
   at (Closure`1 , Scope )
   at Lamar.IoC.Instances.GeneratedInstance.<>c__DisplayClass5_1.<BuildFuncResolver>b__0(Scope s)
   at Lamar.IoC.Instances.GeneratedInstance.Resolve(Scope scope)
   at (ArrayClosure , Scope )
   at Lamar.IoC.Instances.GeneratedInstance.<>c__DisplayClass5_1.<BuildFuncResolver>b__0(Scope s)
   at Lamar.IoC.Scope.GetInstance(Type serviceType)
   at Lamar.IoC.Scope.GetInstance[T]()

I've added a workaround to recalculate the hash duplicates at startup by updating the instance names, but it would be nice if these collisions could be avoided by Lamar.

e.g. workaround

void MitigateHashCollisions(ServiceRegistry serviceRegistry)
{
    var hashCollisions = serviceRegistry
        .Select(x => x.ImplementationInstance)
        .OfType<Instance>()
        .GroupBy(x => x.Hash)
        .Where(x => x.Select(y => y.ServiceType).Distinct().Count() > 1);

    foreach (var collisionGroup in hashCollisions)
    {
        foreach (var lamarInstance in collisionGroup.Where(x => !x.IsExplicitlyNamed))
        {
            // Updating the name will result in a new hash
            lamarInstance.Name = $"Default_{lamarInstance.ServiceType.Name}";
        }
    }
}
jeremydmiller commented 6 months ago

I'd be happy for a pull request on this one

jwoodmanbuildium commented 1 day ago

@jeremydmiller - Any chance the hashing implementation can be reworked with version 14 to avoid these collisions?