JasperFx / lamar

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

Adding support to be able to register UseLamar() with IWebHostBuilder in .NET Core 3 Projects? #203

Closed stambunan closed 4 years ago

stambunan commented 4 years ago

I have a .NET Core 3 Web API that will be deployed in AWS Lambda with an API Gateway connecting to it using proxy integration. The Lambda Entry point requires an override with the following method signature:

protected override void Init(IWebHostBuilder) { builder.UseStartup<Startup>(); } See https://aws.amazon.com/blogs/developer/net-core-3-0-on-lambda-with-aws-lambdas-custom-runtime/

The #if NETSTANDARD2_0 in WebHostBuilderExtensions.cs prevents me from using the helper method. I added it manually into the project and it seems to work properly.

Should a new nuget package be available to allow the usage of UseLamar() with IWebHostBuilder in .NET Core 3 or have a helper method called UseLamarForAWSWebHostBuilder() ?

jeremydmiller commented 4 years ago

If you want it, I'll take a pull request to add it back in. I didn't know that would be necessary or desirable in 3.0

wendellmva commented 4 years ago

@stambunan could you please share your solution cause I am running into the same problem. tx

wendellmva commented 4 years ago

this is behavior is only found in upgraded projects not in newly created ones

stambunan commented 4 years ago

@jeremydmiller I'll create a PR to add it back in. @wendellmva what I did was I created a static extension class with the above inserted to get it to work.

public static class LamarCustomExtension
    {
        public static IWebHostBuilder UseLamarForWebHostBuilder<T>(this IWebHostBuilder builder, Action<WebHostBuilderContext, T> configure = null) where T : ServiceRegistry, new()
        {
            return builder.ConfigureServices((context, services) =>
            {
                var registry = new T();
                configure?.Invoke(context, registry);

                services.AddLamar(registry);
            });
        }

        public static IWebHostBuilder UseLamarForWebHostBuilder(this IWebHostBuilder builder, Action<WebHostBuilderContext, ServiceRegistry> configure = null)
        {
            return builder.UseLamarForWebHostBuilder<ServiceRegistry>(configure);
        }
    }

which got used in the lambda entry point:

public class LambdaEntryPoint : APIGatewayProxyFunction
    {
        protected override void Init(IWebHostBuilder builder)
        {
            builder
                .UseLamarForWebHostBuilder()
                .UseStartup<Startup>();
        }
    }
segantim commented 4 years ago

I have a similar (Same) issue, although the workaround above didn't work for me.

This is my code from my .Net Core 3.1 service Program.c

        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
            //CreateHostBuilder(args).Build().Run();
        }
        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseContentRoot(Directory.GetCurrentDirectory())
                .ConfigureLogging((hostingContext, logging) =>
                {
                    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                    logging.AddConsole();
                    logging.AddDebug();
                })
                .UseIISIntegration()
                .UseLamarForWebHostBuilder()    
                .UseStartup<Startup>()
                .CaptureStartupErrors(true)
                .UseSetting("detailedErrors", "true");
    }

I also tried to use it as a standard HostBuilder, and both give me the same error.

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>()
                      .CaptureStartupErrors(true)
                      .UseSetting("detailedErrors", "true");
        })
        .ConfigureLogging((hostingContext, logging) =>
        {
            logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
            logging.AddConsole();
            logging.AddDebug();
        })
        .UseLamar(); 
}

Stack Trace:

[aspnetcorev2_inprocess.dll] Event Log: 'Application '/LM/W3SVC/2/ROOT/rieservices' with physical root 'C:\TFS\source\RIE\RIEPortal\Main\Source\RIEExternal\Source\DOF.RIE.External.Services\' hit unexpected managed exception, exception code = '0xe0434352'. First 30KB characters of captured stdout and stderr logs:
Unhandled exception. System.InvalidOperationException: Detected some kind of bi-directional dependency while trying to discover and plan a missing service registration. Examining types: Microsoft.Identity.Client.ClientCredential, Microsoft.Identity.Client.ClientAssertionCertificate, System.Security.Cryptography.X509Certificates.X509Certificate2, System.Security.Cryptography.X509Certificates.X509Certificate
   at Lamar.ServiceGraph.TryToCreateMissingFamily(Type serviceType)
   at Lamar.ServiceGraph.addMissingFamily(Type serviceType)
   at Lamar.ServiceGraph.ResolveFamily(Type serviceType)
   at Lamar.ServiceGraph.FindDefault(Type serviceType)
   at Lamar.IoC.Instances.ConstructorInstance.<>c__DisplayClass35_0.<couldBuild>b__0(ParameterInfo p)
   at System.Linq.Enumerable.All[TSource](IEnumerable`1 source, Func`2 predicate)
   at Lamar.IoC.Instances.ConstructorInstance.couldBuild(ConstructorInfo ctor, ServiceGraph services)
   at Lamar.IoC.Instances.ConstructorInstance.<>c__DisplayClass36_0.<DetermineConstructor>b__1(ConstructorInfo x)
   at System.Linq.OrderedEnumerable`1.TryGetFirst(Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at Lamar.IoC.Instances.ConstructorInstance.DetermineConstructor(ServiceGraph services, String& message)
   at Lamar.ServiceGraph.CouldBuild(Type concreteType)
   at Lamar.ConcreteFamilyPolicy.Build(Type type, ServiceGraph serviceGraph)
   at Lamar.ServiceGraph.<>c__DisplayClass65_0.<TryToCreateMissingFamily>b__1(IFamilyPolicy x)
   at LamarCodeGeneration.Util.EnumerableExtensions.FirstValue[TItem,TReturn](IEnumerable`1 enumerable, Func`2 func)
   at Lamar.ServiceGraph.TryToCreateMissingFamily(Type serviceType)
   at Lamar.ServiceGraph.addMissingFamily(Type serviceType)
   at Lamar.ServiceGraph.ResolveFamily(Type serviceType)
   at Lamar.ServiceGraph.FindDefault(Type serviceType)
   at Lamar.IoC.Instances.ConstructorInstance.<>c__DisplayClass35_0.<couldBuild>b__0(ParameterInfo p)
   at System.Linq.Enumerable.All[TSource](IEnumerable`1 source, Func`2 predicate)
   at Lamar.IoC.Instances.ConstructorInstance.couldBuild(ConstructorInfo ctor, ServiceGraph services)
   at Lamar.IoC.Instances.ConstructorInstance.<>c__DisplayClass36_0.<DetermineConstructor>b__1(ConstructorInfo x)
   at System.Linq.OrderedEnumerable`1.TryGetFirst(Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at Lamar.IoC.Instances.ConstructorInstance.DetermineConstructor(ServiceGraph services, String& message)
   at Lamar.ServiceGraph.CouldBuild(Type concreteType)
   at Lamar.ConcreteFamilyPolicy.Build(Type type, ServiceGraph serviceGraph)
   at Lamar.ServiceGraph.<>c__DisplayClass65_0.<TryToCreateMissingFamily>b__1(IFamilyPolicy x)
   at LamarCodeGeneration.Util.EnumerableExtensions.FirstValue[TItem,TReturn](IEnumerable`1 enumerable, Func`2 func)
   at Lamar.ServiceGraph.TryToCreateMissingFamily(Type serviceType)
   at Lamar.ServiceGraph.addMissingFamily(Type serviceType)
   at Lamar.ServiceGraph.ResolveFamily(Type serviceType)
   at Lamar.ServiceGraph.FindDefault(Type serviceType)
   at Lamar.IoC.Instances.ConstructorInstance.<>c__DisplayClass35_0.<couldBuild>b__0(ParameterInfo p)
   at System.Linq.Enumerable.All[TSource](IEnumerable`1 source, Func`2 predicate)
   at Lamar.IoC.Instances.ConstructorInstance.couldBuild(ConstructorInfo ctor, ServiceGraph services)
   at Lamar.IoC.Instances.ConstructorInstance.<>c__DisplayClass36_0.<DetermineConstructor>b__1(ConstructorInfo x)
   at System.Linq.OrderedEnumerable`1.TryGetFirst(Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at Lamar.IoC.Instances.ConstructorInstance.DetermineConstructor(ServiceGraph services, String& message)
   at Lamar.ServiceGraph.CouldBuild(Type concreteType)
   at Lamar.ConcreteFamilyPolicy.Build(Type type, ServiceGraph serviceGraph)
   at Lamar.ServiceGraph.<>c__DisplayClass65_0.<TryToCreateMissingFamily>b__1(IFamilyPolicy x)
   at LamarCodeGeneration.Util.EnumerableExtensions.FirstValue[TItem,TReturn](IEnumerable`1 enumerable, Func`2 func)
   at Lamar.ServiceGraph.TryToCreateMissingFamily(Type serviceType)
   at Lamar.ServiceGraph.addMissingFamily(Type serviceType)
   at Lamar.ServiceGraph.ResolveFamily(Type serviceType)
   at Lamar.ServiceGraph.FindDefault(Type serviceType)
   at Lamar.IoC.Instances.ConstructorInstance.<>c__DisplayClass35_0.<couldBuild>b__0(ParameterInfo p)
   at System.Linq.Enumerable.All[TSource](IEnumerable`1 source, Func`2 predicate)
   at Lamar.IoC.Instances.ConstructorInstance.couldBuild(ConstructorInfo ctor, ServiceGraph services)
   at Lamar.IoC.Instances.ConstructorInstance.<>c__DisplayClass36_0.<DetermineConstructor>b__1(ConstructorInfo x)
   at System.Linq.OrderedEnumerable`1.TryGetFirst(Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at Lamar.IoC.Instances.ConstructorInstance.DetermineConstructor(ServiceGraph services, String& message)
   at Lamar.ServiceGraph.CouldBuild(Type concreteType)
   at Lamar.ConcreteFamilyPolicy.Build(Type type, ServiceGraph serviceGraph)
   at Lamar.ServiceGraph.<>c__DisplayClass65_0.<TryToCreateMissingFamily>b__1(IFamilyPolicy x)
   at LamarCodeGeneration.Util.EnumerableExtensions.FirstValue[TItem,TReturn](IEnumerable`1 enumerable, Func`2 func)
   at Lamar.ServiceGraph.TryToCreateMissingFamily(Type serviceType)
   at Lamar.ServiceGraph.addMissingFamily(Type serviceType)
   at Lamar.ServiceGraph.ResolveFamily(Type serviceType)
   at Lamar.IoC.Instances.ConstructorInstance.explainWhyConstructorCannotBeUsed(Type implementationType, ConstructorInfo constructor, ServiceGraph services)
   at Lamar.IoC.Instances.ConstructorInstance.DetermineConstructor(ServiceGraph services, String& message)
   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.LamarServiceProviderFactory.CreateServiceProvider(ServiceRegistry containerBuilder)
   at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
   at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
   at Microsoft.Extensions.Hosting.HostBuilder.Build()
   at DOF.RIE.External.Services.Program.Main(String[] args) in C:\TFS\source\RIE\RIEPortal\Main\Source\RIEExternal\Source\DOF.RIE.External.Services\Program.cs:line 20
' 
End Event Log Message.
stambunan commented 4 years ago

@segantim That looks like a bi-directional dependency issue? Is there something within your registration that causes A to depend on B and B to depend on A ? My issue was on using Amazon's Custom Runtime library with .NET Core 3.0 as I was trying to deploy a lambda. The custom runtime library only exposes IWebHostBuilder. If IHostBuilder was available, I would be using that.

segantim commented 4 years ago

@stambunan The first code listing (with useLamar() instead of UseLamarForWebHostBuilder()) worked fine under .Net core 2.2. I'm trying to get the code up to 3.1, unfortunately I ran into this. Sorry to jump on the thread, but I suspect it's a similar issue for a few of the bugs, and I thought it might be a waste to open a new bug. It's a standard .Net Core Web Service that does logging / entity framework, there's really nothing tricky or special about the code. I didn't write the code, so I'm a little behind the 8 ball here. There may be something lurking in my code, just not sure what it could be.

segantim commented 4 years ago

@stambunan An update... As part of my startup.cs I have the following code. Once I removed it, it seems to resolve the issue, but I don't know what else this will break. I assume since all the dll's are in this folder, one of them was causing the issue. I'm just unsure how this worked in 2.2 now

services.Scan(s =>
            {
                s.AssembliesFromApplicationBaseDirectory();
                s.WithDefaultConventions();
            });