Skip to content

Commit

Permalink
Added auto resolution of IDependencyResolver, few fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Csajtai committed May 9, 2017
1 parent d4ffcf3 commit 205ebac
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 54 deletions.
3 changes: 3 additions & 0 deletions src/stashbox.sln
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal
46 changes: 43 additions & 3 deletions src/stashbox.tests/BuildUpTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Stashbox.Attributes;
using Stashbox.Utils;

Expand Down Expand Up @@ -29,11 +30,50 @@ public void BuildUpTests_BuildUp()
}
}

public interface ITest { }
[TestMethod]
public void BuildUpTests_BuildUp_Scoped()
{
using (var container = new StashboxContainer())
{
container.RegisterScoped<ITest, Test>();

var test1 = new Test1();
var test2 = new Test2();
using (var scope = container.BeginScope())
{

container.WireUpAs<ITest1>(test1);
var inst = scope.BuildUp(test2);

Assert.AreEqual(test2, inst);
Assert.IsNotNull(inst);
Assert.IsNotNull(inst.Test1);
Assert.IsInstanceOfType(inst, typeof(Test2));
Assert.IsInstanceOfType(inst.Test1, typeof(Test1));
Assert.IsInstanceOfType(inst.Test1.Test, typeof(Test));
}

Assert.IsTrue(test1.Test.Disposed);
Assert.IsTrue(test2.Test1.Test.Disposed);
}
}

public interface ITest : IDisposable { bool Disposed { get; } }

public interface ITest1 { ITest Test { get; } }

public class Test : ITest { }
public class Test : ITest
{
public void Dispose()
{
if (this.Disposed)
throw new ObjectDisposedException("disposed");

this.Disposed = true;
}

public bool Disposed { get; private set; }
}

public class Test1 : ITest1
{
Expand Down
34 changes: 34 additions & 0 deletions src/stashbox.tests/StandardResolveTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,30 @@ public void StandardResolveTests_Resolve()
}
}

[TestMethod]
public void StandardResolveTests_Ensure_DependencyResolver_CanBeResolved()
{
using (IStashboxContainer container = new StashboxContainer())
{
container.RegisterScoped<ResolverTest>();
var resolver = container.Resolve<IDependencyResolver>();

var test = container.Resolve<ResolverTest>();

Assert.AreSame(container, resolver);
Assert.AreSame(container, test.DependencyResolver);

using (var scope = container.BeginScope())
{
var scopedResolver = scope.Resolve<IDependencyResolver>();
var test1 = scope.Resolve<ResolverTest>();

Assert.AreSame(scope, scopedResolver);
Assert.AreSame(scope, test1.DependencyResolver);
}
}
}

[TestMethod]
public void StandardResolveTests_Factory()
{
Expand Down Expand Up @@ -515,5 +539,15 @@ public Test5(ITest1 test)
{
}
}

public class ResolverTest
{
public IDependencyResolver DependencyResolver { get; }

public ResolverTest(IDependencyResolver dependencyResolver)
{
this.DependencyResolver = dependencyResolver;
}
}
}
}
8 changes: 8 additions & 0 deletions src/stashbox/Infrastructure/IDependencyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,13 @@ public interface IDependencyResolver : IDisposable
/// <param name="withoutDisposalTracking">If it's set to true the container will exclude the instance from the disposal tracking.</param>
/// <returns>The scope.</returns>
IDependencyResolver PutInstanceInScope(Type typeFrom, object instance, bool withoutDisposalTracking = false);

/// <summary>
/// Builds up an instance, the container will perform injections and extensions on it.
/// </summary>
/// <typeparam name="TTo">The type of the requested instance.</typeparam>
/// <param name="instance">The instance to build up.</param>
/// <returns>The built object.</returns>
TTo BuildUp<TTo>(TTo instance);
}
}
8 changes: 0 additions & 8 deletions src/stashbox/Infrastructure/IStashboxContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,6 @@ public interface IStashboxContainer : IDependencyRegistrator, IDependencyResolve
/// <returns>True if the service can be resolved, otherwise false.</returns>
bool CanResolve(Type typeFrom, object name = null);

/// <summary>
/// Builds up an instance, the container will perform injections and extensions on it.
/// </summary>
/// <typeparam name="TTo">The type of the requested instance.</typeparam>
/// <param name="instance">The instance to build up.</param>
/// <returns>The built object.</returns>
TTo BuildUp<TTo>(TTo instance);

/// <summary>
/// Configures the container.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions src/stashbox/Resolution/ActivationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public Delegate ActivateFactory(Type type, Type[] parameterTypes, IResolutionSco

private object Activate(ResolutionInfo resolutionInfo, Type type, object name = null)
{
if (type == Constants.ResolverType)
return resolutionInfo.ResolutionScope;

if (resolutionInfo.ResolutionScope.HasScopedInstances)
{
var instance = resolutionInfo.ResolutionScope.GetScopedInstanceOrDefault(type);
Expand Down
5 changes: 4 additions & 1 deletion src/stashbox/Resolution/ResolutionStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ internal ResolutionStrategy(IResolverSelector resolverSelector)
public Expression BuildResolutionExpression(IContainerContext containerContext, ResolutionInfo resolutionInfo, TypeInformation typeInformation,
InjectionParameter[] injectionParameters)
{
if (typeInformation.Type == Constants.ResolverType)
return Expression.Convert(Constants.ScopeExpression, Constants.ResolverType);

if (resolutionInfo.ParameterExpressions != null && resolutionInfo.ParameterExpressions.Any(p => p.Type == typeInformation.Type))
return resolutionInfo.ParameterExpressions.Last(p => p.Type == typeInformation.Type);

Expand All @@ -32,7 +35,7 @@ public Expression BuildResolutionExpression(IContainerContext containerContext,
if (resolutionInfo.ResolutionScope.HasScopedInstances)
{
var instance = resolutionInfo.ResolutionScope.GetScopedInstanceOrDefault(typeInformation.Type);
if(instance != null)
if (instance != null)
return Expression.Constant(instance);
}

Expand Down
83 changes: 42 additions & 41 deletions src/stashbox/ResolutionScope.cs
Original file line number Diff line number Diff line change
@@ -1,83 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Stashbox.BuildUp.Expressions;
using Stashbox.Entity;
using Stashbox.Infrastructure;
using Stashbox.Infrastructure.Registration;
using Stashbox.Infrastructure.Resolution;
using Stashbox.Utils;

namespace Stashbox
{
/// <summary>
/// Represents a resolution scope.
/// </summary>
public class ResolutionScope : ResolutionScopeBase, IDependencyResolver
internal class ResolutionScope : ResolutionScopeBase, IDependencyResolver
{
private readonly IActivationContext activationContext;

/// <summary>
/// Constructs a resolution scope.
/// </summary>
/// <param name="activationContext">The dependency resolver.</param>
public ResolutionScope(IActivationContext activationContext)
private readonly IServiceRegistrator serviceRegistrator;
private readonly IExpressionBuilder expressionBuilder;
private readonly IResolutionScope rootScope;

public ResolutionScope(IActivationContext activationContext, IServiceRegistrator serviceRegistrator,
IExpressionBuilder expressionBuilder, IResolutionScope rootScope)
{
this.activationContext = activationContext;
this.serviceRegistrator = serviceRegistrator;
this.expressionBuilder = expressionBuilder;
this.rootScope = rootScope;
}

/// <inheritdoc />

public TKey Resolve<TKey>(bool nullResultAllowed = false) =>
(TKey)this.activationContext.Activate(typeof(TKey), this, nullResultAllowed);

/// <inheritdoc />

public object Resolve(Type typeFrom, bool nullResultAllowed = false) =>
this.activationContext.Activate(typeFrom, this, nullResultAllowed);

/// <inheritdoc />

public TKey Resolve<TKey>(object name, bool nullResultAllowed = false) =>
(TKey)this.activationContext.Activate(typeof(TKey), this, name, nullResultAllowed);

/// <inheritdoc />

public object Resolve(Type typeFrom, object name, bool nullResultAllowed = false) =>
this.activationContext.Activate(typeFrom, this, name, nullResultAllowed);

/// <inheritdoc />

public IEnumerable<TKey> ResolveAll<TKey>() =>
(IEnumerable<TKey>)this.activationContext.Activate(typeof(IEnumerable<TKey>), this);

/// <inheritdoc />

public IEnumerable<object> ResolveAll(Type typeFrom) =>
(IEnumerable<object>)this.activationContext.Activate(typeof(IEnumerable<>).MakeGenericType(typeFrom), this);

/// <inheritdoc />

public Delegate ResolveFactory(Type typeFrom, object name = null, bool nullResultAllowed = false, params Type[] parameterTypes) =>
this.activationContext.ActivateFactory(typeFrom, parameterTypes, this, name, nullResultAllowed);

/// <inheritdoc />

public Func<TService> ResolveFactory<TService>(object name = null, bool nullResultAllowed = false) =>
this.ResolveFactory(typeof(TService), name, nullResultAllowed) as Func<TService>;

/// <inheritdoc />

public Func<T1, TService> ResolveFactory<T1, TService>(object name = null, bool nullResultAllowed = false) =>
this.ResolveFactory(typeof(TService), name, nullResultAllowed, typeof(T1)) as Func<T1, TService>;

/// <inheritdoc />

public Func<T1, T2, TService> ResolveFactory<T1, T2, TService>(object name = null, bool nullResultAllowed = false) =>
this.ResolveFactory(typeof(TService), name, nullResultAllowed, typeof(T1), typeof(T2)) as Func<T1, T2, TService>;

/// <inheritdoc />

public Func<T1, T2, T3, TService> ResolveFactory<T1, T2, T3, TService>(object name = null, bool nullResultAllowed = false) =>
this.ResolveFactory(typeof(TService), name, nullResultAllowed, typeof(T1), typeof(T2), typeof(T3)) as Func<T1, T2, T3, TService>;

/// <inheritdoc />

public Func<T1, T2, T3, T4, TService> ResolveFactory<T1, T2, T3, T4, TService>(object name = null, bool nullResultAllowed = false) =>
this.ResolveFactory(typeof(TService), name, nullResultAllowed, typeof(T1), typeof(T2), typeof(T3), typeof(T4)) as Func<T1, T2, T3, T4, TService>;

/// <inheritdoc />
public IDependencyResolver BeginScope() => new ResolutionScope(this.activationContext);

/// <inheritdoc />

public IDependencyResolver BeginScope() => new ResolutionScope(this.activationContext, this.serviceRegistrator,
this.expressionBuilder, this.rootScope);

public IDependencyResolver PutInstanceInScope<TFrom>(TFrom instance, bool withoutDisposalTracking = false) =>
this.PutInstanceInScope(typeof(TFrom), instance, withoutDisposalTracking);

/// <inheritdoc />

public IDependencyResolver PutInstanceInScope(Type typeFrom, object instance, bool withoutDisposalTracking = false)
{
Shield.EnsureNotNull(typeFrom, nameof(typeFrom));
Expand All @@ -89,5 +79,16 @@ public IDependencyResolver PutInstanceInScope(Type typeFrom, object instance, bo

return this;
}

/// <inheritdoc />
public TTo BuildUp<TTo>(TTo instance)
{
var typeTo = instance.GetType();
var registration = this.serviceRegistrator.PrepareContext(typeTo, typeTo);
var expr = this.expressionBuilder.CreateFillExpression(registration.CreateServiceRegistration(false),
Expression.Constant(instance), ResolutionInfo.New(this, this.rootScope), typeTo);
var factory = expr.CompileDelegate(Constants.ScopeExpression);
return (TTo)factory(this);
}
}
}
3 changes: 2 additions & 1 deletion src/stashbox/StashboxContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ public IStashboxContainer CreateChildContainer() =>
new StashboxContainer(this, this.containerExtensionManager.CreateCopy(), this.resolverSelector);

/// <inheritdoc />
public IDependencyResolver BeginScope() => new ResolutionScope(this.activationContext);
public IDependencyResolver BeginScope() => new ResolutionScope(this.activationContext,
this.ServiceRegistrator, this.expressionBuilder, this);

/// <inheritdoc />
public void Configure(Action<IContainerConfigurator> config) =>
Expand Down

0 comments on commit 205ebac

Please sign in to comment.