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

Fix #56 autofac ambiguous stepdef and hook required #127 issue #139

Merged
merged 6 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* Allow creating single target (netstandard2.0) plugins
* MsTest: Use ClassCleanupBehavior.EndOfClass instead of custom implementation (preparation for MsTest v4.0)
* Fix: #71 StackOverflowException when using [StepArgumentTransformation] with same input and output type (for example string)
* Fix: Autofac without hook does not run GlobalDependencies (#127)
* Fix: Reqnroll.Autofac shows wrongly ambiguous step definition (#56)

# v1.0.1 - 2024-02-16

Expand Down
274 changes: 137 additions & 137 deletions Plugins/Reqnroll.Autofac.ReqnrollPlugin/AutofacPlugin.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Reqnroll.Bindings;
using Reqnroll.Infrastructure;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Reqnroll.Autofac;
public class ConfigurationMethodsProvider(ITestAssemblyProvider _testAssemblyProvider) : IConfigurationMethodsProvider
{
public IEnumerable<MethodInfo> GetConfigurationMethods()
{
return _testAssemblyProvider.TestAssembly.GetTypes()
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Linq;
using System.Reflection;
using Autofac;
using Reqnroll;

namespace Reqnroll.Autofac.ReqnrollPlugin
{
Expand All @@ -13,7 +12,7 @@ public static class ContainerBuilderExtensions
/// <summary>
/// Add Reqnroll binding for classes in the assembly where typeof TAssemblyType resides.
/// </summary>
/// <typeparam name="TAssemblyType">The type in an assembly to search for bindings.</typeparam>
/// <typeparam name="TAssemblyType">Any type in an assembly to search for bindings.</typeparam>
/// <param name="builder">The builder.</param>
/// <returns>The builder.</returns>
public static ContainerBuilder AddReqnrollBindings<TAssemblyType>(this ContainerBuilder builder) => builder.AddReqnrollBindings(typeof(TAssemblyType));
Expand All @@ -22,12 +21,20 @@ public static class ContainerBuilderExtensions
/// Add Reqnroll binding for classes in the assembly where the type resides.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="type">The type in an assembly to search for bindings.</param>
/// <param name="type">Any type in an assembly to search for bindings.</param>
/// <returns>The builder.</returns>
public static ContainerBuilder AddReqnrollBindings(this ContainerBuilder builder, Type type)
public static ContainerBuilder AddReqnrollBindings(this ContainerBuilder builder, Type type) => builder.AddReqnrollBindings(type.Assembly);

/// <summary>
/// Add Reqnroll binding for classes in an assembly.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="assembly">The assembly to search for bindings.</param>
/// <returns>The builder.</returns>
public static ContainerBuilder AddReqnrollBindings(this ContainerBuilder builder, Assembly assembly)
{
builder
.RegisterAssemblyTypes(type.Assembly)
.RegisterAssemblyTypes(assembly)
.Where(t => Attribute.IsDefined(t, typeof(BindingAttribute)))
.SingleInstance();
return builder;
Expand Down
120 changes: 56 additions & 64 deletions Plugins/Reqnroll.Autofac.ReqnrollPlugin/ContainerBuilderFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,85 +3,77 @@
using System.Reflection;
using Autofac;
using Reqnroll.Autofac.ReqnrollPlugin;
using Reqnroll.Bindings;
using Reqnroll.Bindings.Discovery;
using Reqnroll.Infrastructure;
using ContainerBuilder = Autofac.ContainerBuilder;

namespace Reqnroll.Autofac
namespace Reqnroll.Autofac;

public class ContainerBuilderFinder : IContainerBuilderFinder
{
public class ContainerBuilderFinder : IContainerBuilderFinder
private readonly IConfigurationMethodsProvider _configurationMethodsProvider;
private readonly Lazy<Func<ContainerBuilder, ContainerBuilder>> _createConfigureGlobalContainer;
private readonly Lazy<Func<ContainerBuilder, ContainerBuilder>> _createConfigureScenarioContainer;
private readonly Lazy<Func<ContainerBuilder, ContainerBuilder>> _legacyCreateScenarioContainerBuilder;
private readonly Lazy<Func<ILifetimeScope>> _getFeatureLifetimeScope;

public ContainerBuilderFinder(IConfigurationMethodsProvider configurationMethodsProvider)
{
private readonly IBindingRegistry bindingRegistry;
private readonly IRuntimeBindingRegistryBuilder bindingRegistryBuilder;
private readonly ITestAssemblyProvider testAssemblyProvider;
private readonly Lazy<Func<ContainerBuilder, ContainerBuilder>> createConfigureGlobalContainer;
private readonly Lazy<Func<ContainerBuilder, ContainerBuilder>> createConfigureScenarioContainer;
private readonly Lazy<Func<ContainerBuilder, ContainerBuilder>> createScenarioContainerBuilder;
private readonly Lazy<Func<ILifetimeScope>> getFeatureLifetimeScope;
_configurationMethodsProvider = configurationMethodsProvider;

public ContainerBuilderFinder(IBindingRegistry bindingRegistry, IRuntimeBindingRegistryBuilder bindingRegistryBuilder, ITestAssemblyProvider testAssemblyProvider)
static ContainerBuilder InvokeVoidAndReturnBuilder(ContainerBuilder containerBuilder, MethodInfo methodInfo)
{
this.bindingRegistry = bindingRegistry;
this.bindingRegistryBuilder = bindingRegistryBuilder;
this.testAssemblyProvider = testAssemblyProvider;
static ContainerBuilder invokeVoidAndReturnBuilder(ContainerBuilder containerBuilder, MethodInfo methodInfo)
{
methodInfo.Invoke(null, new[] { containerBuilder });
return containerBuilder;
}
createConfigureGlobalContainer = new Lazy<Func<ContainerBuilder, ContainerBuilder>>(() => FindCreateScenarioContainerBuilder(typeof(GlobalDependenciesAttribute), typeof(void), invokeVoidAndReturnBuilder), true);
createConfigureScenarioContainer = new Lazy<Func<ContainerBuilder, ContainerBuilder>>(() => FindCreateScenarioContainerBuilder(typeof(ScenarioDependenciesAttribute), typeof(void), invokeVoidAndReturnBuilder), true);
createScenarioContainerBuilder = new Lazy<Func<ContainerBuilder, ContainerBuilder>>(() => FindCreateScenarioContainerBuilder(typeof(ScenarioDependenciesAttribute), typeof(ContainerBuilder), (c, m) => (ContainerBuilder)m.Invoke(null, null)), true);
getFeatureLifetimeScope = new Lazy<Func<ILifetimeScope>>(() => FindLifetimeScope(typeof(FeatureDependenciesAttribute), typeof(ILifetimeScope), m => (ILifetimeScope)m.Invoke(null, null)));
methodInfo.Invoke(null, [ containerBuilder ]);
return containerBuilder;
}
_createConfigureGlobalContainer = new Lazy<Func<ContainerBuilder, ContainerBuilder>>(() => FindCreateScenarioContainerBuilder(typeof(GlobalDependenciesAttribute), typeof(void), InvokeVoidAndReturnBuilder), true);
_createConfigureScenarioContainer = new Lazy<Func<ContainerBuilder, ContainerBuilder>>(() => FindCreateScenarioContainerBuilder(typeof(ScenarioDependenciesAttribute), typeof(void), InvokeVoidAndReturnBuilder), true);
_legacyCreateScenarioContainerBuilder = new Lazy<Func<ContainerBuilder, ContainerBuilder>>(() => FindCreateScenarioContainerBuilder(typeof(ScenarioDependenciesAttribute), typeof(ContainerBuilder), (_, m) => (ContainerBuilder)m.Invoke(null, null)), true);
_getFeatureLifetimeScope = new Lazy<Func<ILifetimeScope>>(() => FindLifetimeScope(typeof(FeatureDependenciesAttribute), typeof(ILifetimeScope), m => (ILifetimeScope)m.Invoke(null, null)));
}

public Func<ContainerBuilder, ContainerBuilder> GetConfigureGlobalContainer()
{
bindingRegistryBuilder.BuildBindingsFromAssembly(testAssemblyProvider.TestAssembly);
return createConfigureGlobalContainer.Value;
}
public Func<ContainerBuilder, ContainerBuilder> GetConfigureGlobalContainer()
{
return _createConfigureGlobalContainer.Value;
}

public Func<ContainerBuilder, ContainerBuilder> GetConfigureScenarioContainer()
{
return createConfigureScenarioContainer.Value;
}
public Func<ContainerBuilder, ContainerBuilder> GetConfigureScenarioContainer()
{
return _createConfigureScenarioContainer.Value;
}

public Func<ContainerBuilder, ContainerBuilder> GetCreateScenarioContainerBuilder()
{
return createScenarioContainerBuilder.Value;
}
// For legacy support: configuration methods that return a container builder.
// It is recommended to use the void methods that get a container builder as a parameter
public Func<ContainerBuilder, ContainerBuilder> GetLegacyCreateScenarioContainerBuilder()
{
return _legacyCreateScenarioContainerBuilder.Value;
}

public Func<ILifetimeScope> GetFeatureLifetimeScope()
{
return getFeatureLifetimeScope.Value;
}
public Func<ILifetimeScope> GetFeatureLifetimeScope()
{
return _getFeatureLifetimeScope.Value;
}

protected virtual Func<ILifetimeScope> FindLifetimeScope(Type attributeType, Type returnType, Func<MethodInfo, ILifetimeScope> invoke)
{
var method = GetMethod(attributeType, returnType);
protected virtual Func<ILifetimeScope> FindLifetimeScope(Type attributeType, Type returnType, Func<MethodInfo, ILifetimeScope> invoke)
{
var method = GetMethod(attributeType, returnType);

return method == null
? null
: () => invoke(method);
}
return method == null
? null
: () => invoke(method);
}

protected virtual Func<ContainerBuilder, ContainerBuilder> FindCreateScenarioContainerBuilder(Type attributeType, Type returnType, Func<ContainerBuilder, MethodInfo, ContainerBuilder> invoke)
{
var method = GetMethod(attributeType, returnType);
protected virtual Func<ContainerBuilder, ContainerBuilder> FindCreateScenarioContainerBuilder(Type attributeType, Type returnType, Func<ContainerBuilder, MethodInfo, ContainerBuilder> invoke)
{
var method = GetMethod(attributeType, returnType);

return method == null
? null
: containerBuilder => invoke(containerBuilder, method);
}
return method == null
? null
: containerBuilder => invoke(containerBuilder, method);
}

private MethodInfo GetMethod(Type attributeType, Type returnType)
{
return bindingRegistry.GetBindingAssemblies()
.SelectMany(x => x.GetTypes())
.SelectMany(x => x.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
.Where(x => x.ReturnType == returnType)
.FirstOrDefault(x => Attribute.IsDefined(x, attributeType));
}
private MethodInfo GetMethod(Type attributeType, Type returnType)
{
return _configurationMethodsProvider.GetConfigurationMethods()
.Where(x => x.ReturnType == returnType)
.FirstOrDefault(x => Attribute.IsDefined(x, attributeType));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Collections.Generic;
using System.Reflection;

namespace Reqnroll.Autofac;

public interface IConfigurationMethodsProvider
{
IEnumerable<MethodInfo> GetConfigurationMethods();
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public interface IContainerBuilderFinder

Func<ContainerBuilder, ContainerBuilder> GetConfigureGlobalContainer();

Func<ContainerBuilder, ContainerBuilder> GetCreateScenarioContainerBuilder();
Func<ContainerBuilder, ContainerBuilder> GetLegacyCreateScenarioContainerBuilder();

Func<ILifetimeScope> GetFeatureLifetimeScope();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>

<RootNamespace>Reqnroll.Autofac</RootNamespace>
</PropertyGroup>

<ItemGroup>
Expand Down
8 changes: 8 additions & 0 deletions Reqnroll/BoDi/IObjectContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ public interface IObjectContainer : IDisposable
/// <param name="name">A name to resolve named instance, otherwise null.</param>
IStrategyRegistration RegisterFactoryAs<TInterface>(Func<IObjectContainer, TInterface> factoryDelegate, string name = null);

/// <summary>
/// Registers an instance produced by <paramref name="factoryDelegate"/>. The delegate will be called only once and the instance it returned will be returned in each resolution.
/// </summary>
/// <typeparam name="TInterface">Interface to register as.</typeparam>
/// <param name="factoryDelegate">The function to run to obtain the instance.</param>
/// <param name="name">A name to resolve named instance, otherwise null.</param>
IStrategyRegistration RegisterFactoryAs<TInterface>(Func<TInterface> factoryDelegate, string name = null);

/// <summary>
/// Resolves an implementation object for an interface or type.
/// </summary>
Expand Down
Loading