From eb778eb26ea8f89bf50ba7d50eaff22814fad88a Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Sun, 5 Mar 2023 12:53:19 -0600 Subject: [PATCH] First cut at making Lamar code generation able to use variables from the outside world. Also bumps to 11.0 because of a breaking API change --- .../Lamar.Diagnostics.csproj | 2 +- ...Lamar.Microsoft.DependencyInjection.csproj | 2 +- .../IoC/Acceptance/dependency_inlining.cs | 52 ++++++++++++ src/Lamar/IoC/Frames/ResolverVariables.cs | 81 ++++++++++++------- src/Lamar/IoC/Frames/ServiceVariableSource.cs | 11 ++- src/Lamar/IoC/Instances/Instance.cs | 21 ++++- src/Lamar/IoC/Instances/ObjectInstance.cs | 2 + src/Lamar/Lamar.csproj | 2 +- 8 files changed, 135 insertions(+), 38 deletions(-) diff --git a/src/Lamar.Diagnostics/Lamar.Diagnostics.csproj b/src/Lamar.Diagnostics/Lamar.Diagnostics.csproj index 2677945a..00a0d564 100644 --- a/src/Lamar.Diagnostics/Lamar.Diagnostics.csproj +++ b/src/Lamar.Diagnostics/Lamar.Diagnostics.csproj @@ -2,7 +2,7 @@ Adds diagnostic checks to the command line of your Lamar-enabled ASP.Net Core app - 10.0.2 + 11.0.0 Jeremy D. Miller net6.0;net7.0 portable diff --git a/src/Lamar.Microsoft.DependencyInjection/Lamar.Microsoft.DependencyInjection.csproj b/src/Lamar.Microsoft.DependencyInjection/Lamar.Microsoft.DependencyInjection.csproj index 216d2783..889d25a1 100644 --- a/src/Lamar.Microsoft.DependencyInjection/Lamar.Microsoft.DependencyInjection.csproj +++ b/src/Lamar.Microsoft.DependencyInjection/Lamar.Microsoft.DependencyInjection.csproj @@ -1,7 +1,7 @@  Lamar Adapter for HostBuilder Integration - 10.0.2 + 11.0.0 Jeremy D. Miller net6.0;net7.0 portable diff --git a/src/Lamar.Testing/IoC/Acceptance/dependency_inlining.cs b/src/Lamar.Testing/IoC/Acceptance/dependency_inlining.cs index 93d1077c..ac50dee1 100644 --- a/src/Lamar.Testing/IoC/Acceptance/dependency_inlining.cs +++ b/src/Lamar.Testing/IoC/Acceptance/dependency_inlining.cs @@ -4,7 +4,9 @@ using System.Threading.Tasks; using JasperFx.CodeGeneration; using JasperFx.CodeGeneration.Frames; +using JasperFx.CodeGeneration.Model; using JasperFx.Core; +using Lamar.IoC.Frames; using Microsoft.Extensions.DependencyInjection; using StructureMap.Testing.GenericWidgets; using StructureMap.Testing.Widget; @@ -12,6 +14,28 @@ namespace Lamar.Testing.IoC.Acceptance; +public static class SessionFactory +{ + public static ISession Create() + { + return new Session(); + } +} + +public class SessionVariableSource : IVariableSource +{ + public bool Matches(Type type) + { + return type == typeof(ISession); + } + + public Variable Create(Type type) + { + var @call = new MethodCall(typeof(SessionFactory), nameof(SessionFactory.Create)); + return @call.ReturnVariable; + } +} + public class dependency_inlining { private string _code; @@ -25,6 +49,7 @@ public dependency_inlining() theAssembly = new GeneratedAssembly(new GenerationRules("Lamar.Generated")); theType = theAssembly.AddType("GeneratedClass", typeof(Message1Handler)); theMethod = theType.MethodFor("Handle"); + theMethod.Sources.Add(new SessionVariableSource()); } private string theCode @@ -50,6 +75,16 @@ private void includeType() theMethod.Frames.AddRange(methods); } + [Fact] + public void use_external_variable_source() + { + theServices.ForConcreteType().Configure.Singleton(); + theServices.For().Use(c => SessionFactory.Create()); + includeType(); + + theCode.ShouldContain("SessionFactory.Create()"); + } + [Fact] public void try_single_handler_no_args() @@ -514,6 +549,23 @@ public void Dispose() } } +public class UsingCustomSourceHandler +{ + private readonly ISession _session; + private readonly MessageTracker _tracker; + + public UsingCustomSourceHandler(ISession session, MessageTracker tracker) + { + _session = session; + _tracker = tracker; + } + + public void Handle(Message1 message) + { + + } +} + public class NoArgMethod { public void Handle(Message1 message) diff --git a/src/Lamar/IoC/Frames/ResolverVariables.cs b/src/Lamar/IoC/Frames/ResolverVariables.cs index 7f39ac90..b449c85b 100644 --- a/src/Lamar/IoC/Frames/ResolverVariables.cs +++ b/src/Lamar/IoC/Frames/ResolverVariables.cs @@ -1,31 +1,39 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using JasperFx.Core; using Lamar.IoC.Instances; using JasperFx.CodeGeneration.Model; using Microsoft.Extensions.DependencyInjection; -using JasperFx.CodeGeneration.Util; namespace Lamar.IoC.Frames { - public class ResolverVariables : IEnumerable + public class ResolverVariables : IEnumerable, IMethodVariables { public int VariableSequence { get; set; } - private readonly IList _cached = new List(); - private readonly IList _all = new List(); + private readonly List _all = new(); + private readonly Dictionary _tracking = new(); public ResolverVariables() { + Method = this; } - public ResolverVariables(IList fields) + public ResolverVariables(IMethodVariables method, IList fields) { + Method = method; _all.AddRange(fields); - _cached.AddRange(fields); + + foreach (var field in fields) + { + _tracking[field.Instance] = field; + } } + public IMethodVariables Method { get; } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); @@ -36,33 +44,29 @@ public IEnumerator GetEnumerator() return _all.GetEnumerator(); } - public Variable[] AllFor(Instance instance) - { - return _cached.Where(x => x.RefersTo(instance)).ToArray(); - } - public Variable Resolve(Instance instance, BuildMode mode) { - if (instance.Lifetime == ServiceLifetime.Transient) + if (_tracking.TryGetValue(instance, out var variable)) { - var transient = instance.CreateVariable(mode, this, false); - - - - _all.Add(transient); - - - - - return transient; + return variable; } + + var fromOutside = Method.TryFindVariable(instance.ServiceType, VariableSource.NotServices); + if (fromOutside != null && !(fromOutside is ServiceStandinVariable)) + { + _all.Add(fromOutside); + _tracking[instance] = fromOutside; - var variable = AllFor(instance).SingleOrDefault(); - if (variable == null) + return fromOutside; + } + + variable = instance.CreateVariable(mode, this, false); + _all.Add(variable); + + // Don't track it for possible reuse if it's transient + if (instance.Lifetime == ServiceLifetime.Scoped) { - variable = instance.CreateVariable(mode, this, false); - _all.Add(variable); - _cached.Add(variable); + _tracking[instance] = variable; } return variable; @@ -80,5 +84,26 @@ public void MakeNamesUnique() } } } + + Variable IMethodVariables.FindVariable(Type type) + { + return null; + } + + Variable IMethodVariables.FindVariableByName(Type dependency, string name) + { + return null; + } + + bool IMethodVariables.TryFindVariableByName(Type dependency, string name, out Variable variable) + { + variable = default; + return false; + } + + Variable IMethodVariables.TryFindVariable(Type type, VariableSource source) + { + return null; + } } } diff --git a/src/Lamar/IoC/Frames/ServiceVariableSource.cs b/src/Lamar/IoC/Frames/ServiceVariableSource.cs index 9c1185e7..f611a700 100644 --- a/src/Lamar/IoC/Frames/ServiceVariableSource.cs +++ b/src/Lamar/IoC/Frames/ServiceVariableSource.cs @@ -62,14 +62,13 @@ public Variable Create(Type type) public void ReplaceVariables(IMethodVariables method) { - // TODO -- MORE HERE!!!! if (_usesNestedContainerDirectly || _standins.Any(x => x.Instance.RequiresServiceProvider(method))) { - useServiceProvider(); + useServiceProvider(method); } else { - useInlineConstruction(); + useInlineConstruction(method); } } @@ -85,10 +84,10 @@ public void StartNewMethod() _standins.Clear(); } - private void useInlineConstruction() + private void useInlineConstruction(IMethodVariables method) { // THIS NEEDS TO BE SCOPED PER METHOD!!! - var variables = new ResolverVariables(_fields); + var variables = new ResolverVariables(method, _fields); foreach (var standin in _standins) { var variable = variables.Resolve(standin.Instance, BuildMode.Inline); @@ -104,7 +103,7 @@ private void useInlineConstruction() variables.MakeNamesUnique(); } - private void useServiceProvider() + private void useServiceProvider(IMethodVariables method) { foreach (var standin in _standins) { diff --git a/src/Lamar/IoC/Instances/Instance.cs b/src/Lamar/IoC/Instances/Instance.cs index 489a84a8..7ec6e5c4 100644 --- a/src/Lamar/IoC/Instances/Instance.cs +++ b/src/Lamar/IoC/Instances/Instance.cs @@ -125,7 +125,26 @@ public virtual object QuickResolve(Scope scope) public int Hash { get; set; } - public virtual bool RequiresServiceProvider(IMethodVariables method) => Dependencies.Any(x => x.RequiresServiceProvider(method) || x.ImplementationType == typeof(IContainer) || x.ImplementationType == typeof(IServiceProvider)); + public virtual bool RequiresServiceProvider(IMethodVariables method) + { + if (Lifetime == ServiceLifetime.Singleton) return false; + + foreach (var dependency in Dependencies) + { + // Always no if a singleton + if (dependency.Lifetime == ServiceLifetime.Singleton) continue; + + if (dependency.ServiceType == typeof(IContainer) || + dependency.ServiceType == typeof(IServiceProvider)) return true; + + // This is for variables that might be created outside of the container + if (method.TryFindVariable(dependency.ServiceType, VariableSource.NotServices) != null) continue; + + if (dependency.RequiresServiceProvider(method)) return true; + } + + return false; + } public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Transient; diff --git a/src/Lamar/IoC/Instances/ObjectInstance.cs b/src/Lamar/IoC/Instances/ObjectInstance.cs index ac8042cc..820b5b4d 100644 --- a/src/Lamar/IoC/Instances/ObjectInstance.cs +++ b/src/Lamar/IoC/Instances/ObjectInstance.cs @@ -22,6 +22,8 @@ public ObjectInstance(Type serviceType, object service) : base(serviceType, serv } public object Service { get; } + + public override Variable CreateVariable(BuildMode mode, ResolverVariables variables, bool isRoot) { diff --git a/src/Lamar/Lamar.csproj b/src/Lamar/Lamar.csproj index 579f2fd8..c928e58b 100644 --- a/src/Lamar/Lamar.csproj +++ b/src/Lamar/Lamar.csproj @@ -1,7 +1,7 @@  Fast ASP.Net Core compatible IoC Tool, Successor to StructureMap - 10.0.2 + 11.0.0 Jeremy D. Miller net6.0;net7.0 portable