Skip to content

Commit

Permalink
First cut at making Lamar code generation able to use variables from …
Browse files Browse the repository at this point in the history
…the outside world. Also bumps to 11.0 because of a breaking API change
  • Loading branch information
Jeremy D. Miller authored and jeremydmiller committed Mar 5, 2023
1 parent a55f0dc commit eb778eb
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 38 deletions.
2 changes: 1 addition & 1 deletion src/Lamar.Diagnostics/Lamar.Diagnostics.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<Description>Adds diagnostic checks to the command line of your Lamar-enabled ASP.Net Core app</Description>
<Version>10.0.2</Version>
<Version>11.0.0</Version>
<Authors>Jeremy D. Miller</Authors>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<DebugType>portable</DebugType>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Lamar Adapter for HostBuilder Integration</Description>
<Version>10.0.2</Version>
<Version>11.0.0</Version>
<Authors>Jeremy D. Miller</Authors>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<DebugType>portable</DebugType>
Expand Down
52 changes: 52 additions & 0 deletions src/Lamar.Testing/IoC/Acceptance/dependency_inlining.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,38 @@
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;
using Xunit;

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;
Expand All @@ -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
Expand All @@ -50,6 +75,16 @@ private void includeType<T>()
theMethod.Frames.AddRange(methods);
}

[Fact]
public void use_external_variable_source()
{
theServices.ForConcreteType<MessageTracker>().Configure.Singleton();
theServices.For<ISession>().Use(c => SessionFactory.Create());
includeType<UsingCustomSourceHandler>();

theCode.ShouldContain("SessionFactory.Create()");
}


[Fact]
public void try_single_handler_no_args()
Expand Down Expand Up @@ -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)
Expand Down
81 changes: 53 additions & 28 deletions src/Lamar/IoC/Frames/ResolverVariables.cs
Original file line number Diff line number Diff line change
@@ -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<Variable>
public class ResolverVariables : IEnumerable<Variable>, IMethodVariables
{
public int VariableSequence { get; set; }

private readonly IList<Variable> _cached = new List<Variable>();
private readonly IList<Variable> _all = new List<Variable>();
private readonly List<Variable> _all = new();
private readonly Dictionary<Instance, Variable> _tracking = new();

public ResolverVariables()
{
Method = this;
}

public ResolverVariables(IList<InjectedServiceField> fields)
public ResolverVariables(IMethodVariables method, IList<InjectedServiceField> 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();
Expand All @@ -36,33 +44,29 @@ public IEnumerator<Variable> 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;
Expand All @@ -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;
}
}
}
11 changes: 5 additions & 6 deletions src/Lamar/IoC/Frames/ServiceVariableSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand All @@ -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);
Expand All @@ -104,7 +103,7 @@ private void useInlineConstruction()
variables.MakeNamesUnique();
}

private void useServiceProvider()
private void useServiceProvider(IMethodVariables method)
{
foreach (var standin in _standins)
{
Expand Down
21 changes: 20 additions & 1 deletion src/Lamar/IoC/Instances/Instance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions src/Lamar/IoC/Instances/ObjectInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Lamar/Lamar.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Fast ASP.Net Core compatible IoC Tool, Successor to StructureMap</Description>
<Version>10.0.2</Version>
<Version>11.0.0</Version>
<Authors>Jeremy D. Miller</Authors>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<DebugType>portable</DebugType>
Expand Down

0 comments on commit eb778eb

Please sign in to comment.