Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to override a named registration in Lamar #343

Open
torfig opened this issue Jun 8, 2022 · 2 comments
Open

Unable to override a named registration in Lamar #343

torfig opened this issue Jun 8, 2022 · 2 comments

Comments

@torfig
Copy link

torfig commented Jun 8, 2022

I'm using Lamar for DI and the IHostBuilder.OverrideServices method to introduce fakes/mocks during integration testing which works fine until I want to replace a named instance.

builder.OverrideServices(services =>
{
    services.For<IUserController>().Use(Mocks.UserController.Object);
    services.For<ICompanyRepositoryProvider>().Use(Mocks.CompanyRepositoryProvider.Object);
    services.For<IGeoCodingDataAccess>().Use(Mocks.GeoCodingDataAccess.Object);
    services.For<IDbRepository().Use(Mocks.DataAccessRepository.Object).Named("AlreadyRegisteredName");
});

As soon as I add a named instance in OverrideServices that is already registered elsewhere, I get the following error:

System.InvalidOperationException
      HResult=0x80131509
      Message=Referenced instance of Company.DbRepository.Interfaces.IDbRepository named 'AlreadyRegisteredName' does not exist
      Source=Lamar
      StackTrace:
       at Lamar.IoC.Instances.ReferencedInstance.<createPlan>d__10.MoveNext()
       at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate)
       at Lamar.IoC.Instances.Instance.CreatePlan(ServiceGraph services)
       at Lamar.IoC.Instances.ConstructorInstance.buildOutConstructorArguments(ServiceGraph services)
       at Lamar.IoC.Instances.ConstructorInstance.createPlan(ServiceGraph services)
       at Lamar.IoC.Instances.Instance.CreatePlan(ServiceGraph services)
       at Lamar.IoC.Instances.ConstructorInstance.buildOutConstructorArguments(ServiceGraph services)
       at Lamar.IoC.Instances.ConstructorInstance.createPlan(ServiceGraph services)
       at Lamar.IoC.Instances.Instance.CreatePlan(ServiceGraph services)
       at Lamar.IoC.Instances.ConstructorInstance.buildOutConstructorArguments(ServiceGraph services)
       at Lamar.IoC.Instances.ConstructorInstance.createPlan(ServiceGraph services)
       at Lamar.IoC.Instances.Instance.CreatePlan(ServiceGraph services)
       at Lamar.ServiceGraph.planResolutionStrategies()
       at Lamar.ServiceGraph.buildOutMissingResolvers()
       at LamarCodeGeneration.Util.PerfTimer.Record(String text, Action action)
       at Lamar.ServiceGraph.Initialize(PerfTimer timer)
       at Lamar.IoC.Scope..ctor(IServiceCollection services, PerfTimer timer)
       at Lamar.Container..ctor(IServiceCollection services)
       at Lamar.Microsoft.DependencyInjection.LamarServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder)
       at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
       at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
       at Microsoft.Extensions.Hosting.HostBuilder.Build()
       at Company.Integration.Orchestrator.IntegrationsOrc.Program.Main(String[] args) in C:\Repos\integration-orc-service\Clients\IntegrationsOrc\Program.cs:line 64

I'm unsure if this has any relevance, but we're using a policy to reference the correct registered name when resolving the IDbRepository (and use an Attribute on the IDbRepository parameter to specify the name to resolve to)

 public class NamedInstancePolicy<T> : ConfiguredInstancePolicy
 {
     /// <inheritdoc/>
     protected override void apply(IConfiguredInstance instance)
     {
         foreach (var param in instance.ImplementationType.GetConstructors()
                                                                  .SelectMany(x => x.GetParameters())
                                                                  .Where(x => x.ParameterType == typeof(T)))
         {
             // Primary route is to resolve the instance based on a custom attribute
             var ipAttribute = param.GetCustomAttribute<InstancePolicyNameAttribute>();
             if (ipAttribute == null)
             {
                 // The IDbConnection parameter was not marked with the correct attribute
                 throw new InvalidOperationException($"A {param.ParameterType.Name} parameter in {instance.Name} does not contain the required InstancePolicyNameAttribute to define which instance should be resolved.");
             }

             // Parameter was correctly set up, assign the named instance to the constructor parameter
             instance.Ctor<T>(param.Name).IsNamedInstance(ipAttribute.InstanceName);
         }
     }
 }

I'm using the latest version of Lamar (8.0.1) and asp.net core 6.0.

This same error occurs if I register the same type with the same name twice. Any ideas how to resolve this? Maybe I could remove the previous named registration somehow (I'd need a way to do that within the OverrideServices method)?

@magkal
Copy link

magkal commented Jun 19, 2024

Did you find a solution for this? I've run into the same issue with named registrations.

@rizi
Copy link
Contributor

rizi commented Jun 19, 2024

Did you find a solution for this? I've run into the same issue with named registrations.

I don't know if this works when using builder.OverrideService()...., but generally this is what work's for us:

/// <summary>
/// Registers the given instance with the given namen and removes the last already available registration for this type and with this name.
/// </summary>
/// <param name="instance">The instance which should be registered with the given name.</param>
/// <param name="name">The name of the registration.</param>
/// <typeparam name="TType">The type of the given instance.</typeparam>
/// <param name="serviceRegistry">The service registry is needed to remove already existing registrations.</param>
public static TType OverrideNamed<TType>(this TType instance, string name, ServiceRegistry serviceRegistry) where TType : Instance
{
    if (instance == null)
        throw new ArgumentNullException(nameof(instance));
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException(@"Value cannot be null or whitespace.", nameof(name));
    if (serviceRegistry == null)
        throw new ArgumentNullException(nameof(serviceRegistry));

    serviceRegistry.RemoveAll(serviceDescriptor => serviceDescriptor.ServiceType == instance.ServiceType && serviceDescriptor.ImplementationInstance is Instance currentInstance && currentInstance.Name.Equals(name, StringComparison.OrdinalIgnoreCase));

    instance.Named(name);

    return instance;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants