JasperFx/alba

Lamar interaction differs between Debug and Release mode

Pondidum opened this issue · 2 comments

This issue "moved" from the Lamar repo to here as it seems more likely this is a problem in Alba (or Xunit, I suppose.)

The problem occurs when an object is registered outside the Startup class in Alba, using .ConfigureServices, and a ServiceRegistrywithin the application makes use of that registered type to register another type. This works fine when running in Debug mode, but under Release, the second type registration is not found. Probably better explained with a test...

  • If you run this in Debug mode, the test passes, however, if run in Release, it fails.
  • If you remove the use of ServiceRegistries, and make the services.For... call in ConfigureContainer, it works in both Release and Debug
  • WhatDidIScan indicates that in Debug mode the root assembly is my testapp (LamarBug), but under Release is Microsoft.AspNetCore.Hosting (full output at the bottom of this issue)
  • If I use the Registry directly (services.IncludeRegistry<RestRegistry>();) the test passes

I have a workaround in place, so this isn't drastically urgent, but just caused a lot of head-scratching when I found it.

Package versions used:

<PackageReference Include="Alba.AspNetCore2" Version="1.4.3" />
<PackageReference Include="Lamar" Version="1.1.2" />
<PackageReference Include="Lamar.Microsoft.DependencyInjection" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />

I can upload the test project if that would help.

[Fact]
public Task<IScenarioResult> Test1()
{
	var storage = new InMemoryStorage();
	var system = SystemUnderTest.ForStartup<Startup>();

	system.Configure(builder => builder.UseLamar());
	system.ConfigureServices(services =>
	{
		services.AddSingleton<IStorage>(storage);
	});

	return system.Scenario(_ =>
	{
		_.Get.Url("/Toggles");
		_.StatusCodeShouldBe(HttpStatusCode.OK);
		_.ContentShouldBe("{\"message\":\"LamarBug.InMemorySession\"}");
	});
}

public interface IStorage
{
	IStorageSession CreateSession();
}

public interface IStorageSession
{
}

public class InMemoryStorage : IStorage
{
	public IStorageSession CreateSession() => new InMemorySession();
}

public class InMemorySession : IStorageSession
{
}

public class Startup
{
	public void ConfigureContainer(ServiceRegistry services)
	{
		services.AddMvc();

		services.Scan(_ =>
		{
			_.TheCallingAssembly();
			_.WithDefaultConventions();
			_.LookForRegistries();
		});
		
		//services.For<IStorageSession>().Use(c => c.GetInstance<IStorage>().CreateSession()).Scoped();
	}

	public void Configure(IApplicationBuilder app, IHostingEnvironment env)
	{
		app.UseMvc();
	}
}


[Route("Toggles")]
public class TogglesController : Controller
{
	private readonly IStorageSession _session;

	public TogglesController(IStorageSession session)
	{
		_session = session;
	}

	[Route("")]
	[HttpGet]
	public async Task<IActionResult> Get()
	{
		return new JsonResult(new { Message = _session.GetType().ToString() });
	}
}

public class RestRegistry : ServiceRegistry
{
	public RestRegistry()
	{
		Scan(a =>
		{
			a.TheCallingAssembly();
			a.WithDefaultConventions();
		});

		For<IStorageSession>().Use(c => c.GetInstance<IStorage>().CreateSession()).Scoped();
	}
}

Stack Trace:

System.InvalidOperationException : Unable to resolve service for type 'LamarBug.IStorageSession' while attempting to activate 'LamarBug.TogglesController'.
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
   at lambda_method(Closure , IServiceProvider , Object[] )
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.<CreateActivator>b__0(ControllerContext controllerContext)
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
   at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
   at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
   at Alba.SystemUnderTestExtensions.Scenario(ISystemUnderTest system, Action`1 configure)
   at Alba.SystemUnderTestExtensions.Scenario(ISystemUnderTest system, Action`1 configure)
--- End of stack trace from previous location where exception was thrown ---

Output of WhatDidIScan()
Debug:

2018-10-22 21:17:54 [Verbose] All Scanners
================================================================

Assemblies
----------
* LamarBug, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Conventions
--------
* Default I[Name]/[Name] registration convention
* Lamar.Scanning.Conventions.FindRegistriesScanner

Assemblies
----------
* LamarBug, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Conventions
--------
* Default I[Name]/[Name] registration convention

No problems were encountered in exporting types from Assemblies

And Release

2018-10-22 21:17:02 [Verbose] All Scanners
================================================================

Assemblies
----------
* Microsoft.AspNetCore.Hosting, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60

Conventions
--------
* Default I[Name]/[Name] registration convention
* Lamar.Scanning.Conventions.FindRegistriesScanner

No problems were encountered in exporting types from Assemblies

@Pondidum I'm going to lazily claim that this goes away with Alba 2 because it stops doing anything special for ASP.Net Core bootstrapping. Sorry for the very, very slow response here:/

@jeremydmiller I'd forgotten about it to be honest - I used a workaround in the end anyway!