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

Simpleinjector reimplementation #631

Merged
merged 6 commits into from
Dec 2, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
50 changes: 47 additions & 3 deletions src/Splat.SimpleInjector.Tests/DependencyResolverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void SimpleInjectorDependencyResolver_Should_Resolve_View_Model()
{
var container = new Container();
container.Register<ViewModelOne>();
container.UseSimpleInjectorDependencyResolver();
container.UseSimpleInjectorDependencyResolver(new SimpleInjectorInitializer());

var viewModel = Locator.Current.GetService(typeof(ViewModelOne));

Expand All @@ -43,7 +43,7 @@ public void SimpleInjectorDependencyResolver_Should_Resolve_View()
{
var container = new Container();
container.Register<IViewFor<ViewModelOne>, ViewOne>();
container.UseSimpleInjectorDependencyResolver();
container.UseSimpleInjectorDependencyResolver(new SimpleInjectorInitializer());

var view = Locator.Current.GetService(typeof(IViewFor<ViewModelOne>));

Expand All @@ -59,12 +59,56 @@ public void SimpleInjectorDependencyResolver_Should_Resolve_Screen()
{
var container = new Container();
container.RegisterSingleton<IScreen, MockScreen>();
container.UseSimpleInjectorDependencyResolver();
container.UseSimpleInjectorDependencyResolver(new SimpleInjectorInitializer());

var screen = Locator.Current.GetService(typeof(IScreen));

screen.Should().NotBeNull();
screen.Should().BeOfType<MockScreen>();
}

/// <summary>
/// Should not throw during initialization of ReactiveUI.
/// </summary>
[Fact]
public void SimpleInjectorDependencyResolver_Splat_Initialization_ShouldNotThrow()
{
Container container = new Container();
SimpleInjectorInitializer initializer = new SimpleInjectorInitializer();

Locator.SetLocator(initializer);
Locator.CurrentMutable.InitializeSplat();
container.UseSimpleInjectorDependencyResolver(initializer);
}

/// <summary>
/// Should resolve dependency registered during Splat initialization.
/// </summary>
[Fact]
public void SimpleInjectorDependencyResolver_ShouldResolveSplatRegisteredDependency()
{
Container container = new Container();
SimpleInjectorInitializer initializer = new SimpleInjectorInitializer();

Locator.SetLocator(initializer);
Locator.CurrentMutable.InitializeSplat();
container.UseSimpleInjectorDependencyResolver(initializer);

ILogger dependency = Locator.Current.GetService(typeof(ILogger)) as ILogger;
Assert.NotNull(dependency);
}

/// <summary>
/// Should resolve dependency registered during Splat initialization.
/// </summary>
[Fact]
public void SimpleInjectorDependencyResolver_CollectionShouldNeverReturnNull()
{
var container = new Container();
container.UseSimpleInjectorDependencyResolver(new SimpleInjectorInitializer());

var views = Locator.Current.GetServices(typeof(ViewOne));
Assert.NotNull(views);
}
}
}
36 changes: 36 additions & 0 deletions src/Splat.SimpleInjector/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Splat.SimpleInjector

## Using SimpleInjector

Splat.SimpleInjector is an adapter for `IMutableDependencyResolver`. It allows you to register your application dependencies in a SimpleInjector `Container`. You can then use the container as Splat's internal dependency resolver.

### Register the Container

Because of internal SimpleInjector behaviours in order to register it as a container intermediate "dummy" container has to be used - `SimpleInjectorInitializer`.

```cs
SimpleInjectorInitializer initializer = new SimpleInjectorInitializer();
Locator.SetLocator(initializer);

// Register ReactiveUI dependencies
dpvreony marked this conversation as resolved.
Show resolved Hide resolved
Locator.CurrentMutable.InitializeSplat();
Locator.CurrentMutable.InitializeReactiveUI();

// Registering Views
Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly());

// Register SimpleInjector
Container container = new Container();

// Actual SimpleInjector registration
container.UseSimpleInjectorDependencyResolver(initializer);

// SimpleInjector dependency registrations
container.Register<Example>();
container.Register<Example2>();

// Optional overriding default implementations
container.Options.AllowOverridingRegistrations = true;
dpvreony marked this conversation as resolved.
Show resolved Hide resolved
container.Register<IViewLocator, MyCustomViewLocator>();

```
61 changes: 54 additions & 7 deletions src/Splat.SimpleInjector/SimpleInjectorDependencyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,53 @@ public class SimpleInjectorDependencyResolver : IDependencyResolver
/// Initializes a new instance of the <see cref="SimpleInjectorDependencyResolver"/> class.
/// </summary>
/// <param name="container">The container.</param>
public SimpleInjectorDependencyResolver(Container container)
/// <param name="initializer">The initializer.</param>
public SimpleInjectorDependencyResolver(Container container, SimpleInjectorInitializer initializer)
{
_container = container;
_container = container ?? throw new ArgumentNullException(nameof(container));
_ = initializer ?? throw new ArgumentNullException(nameof(initializer));

RegisterFactories(initializer);
}

/// <inheritdoc />
public object GetService(Type serviceType, string contract = null)
{
return _container.GetInstance(serviceType);
try
{
InstanceProducer? registration = _container.GetRegistration(serviceType);
if (registration != null)
{
return registration.GetInstance();
}

IEnumerable<object> registers = _container.GetAllInstances(serviceType);
return registers.LastOrDefault();
}
catch
{
return null;
}
}

/// <inheritdoc />
public IEnumerable<object> GetServices(Type serviceType, string contract = null) =>
_container.GetAllInstances(serviceType);
public IEnumerable<object> GetServices(Type serviceType, string contract = null)
{
try
{
return _container.GetAllInstances(serviceType);
}
catch
{
InstanceProducer? registration = _container.GetRegistration(serviceType);
if (registration != null)
{
return new object[] { registration.GetInstance() };
}

return Enumerable.Empty<object>();
}
}

/// <inheritdoc />
public bool HasRegistration(Type serviceType, string contract = null)
Expand All @@ -44,8 +77,11 @@ public bool HasRegistration(Type serviceType, string contract = null)
}

/// <inheritdoc />
public void Register(Func<object> factory, Type serviceType, string contract = null) =>
_container.Register(serviceType, factory);
public void Register(Func<object> factory, Type serviceType, string contract = null)
{
// The function does nothing because there should be no registration called on this object.
// Anyway, Locator.SetLocator performs some unnecessary registrations.
}

/// <inheritdoc />
public void UnregisterCurrent(Type serviceType, string contract = null)
Expand Down Expand Up @@ -84,5 +120,16 @@ protected virtual void Dispose(bool disposing)
_container = null;
}
}

private void RegisterFactories(SimpleInjectorInitializer initializer)
{
foreach (KeyValuePair<Type, List<Func<object>>> typeFactories in initializer.RegisteredFactories)
{
_container.Collection.Register(
typeFactories.Key,
typeFactories.Value.Select(n =>
new TransientSimpleInjectorRegistration(_container, typeFactories.Key, n)));
}
}
}
}
86 changes: 86 additions & 0 deletions src/Splat.SimpleInjector/SimpleInjectorInitializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Splat.SimpleInjector
{
/// <summary>
/// sad.
/// </summary>
#pragma warning disable CA1063 // Implement IDisposable Correctly
public class SimpleInjectorInitializer : IDependencyResolver
#pragma warning restore CA1063 // Implement IDisposable Correctly
{
/// <summary>
/// Gets dictionary of registered factories.
/// </summary>
public Dictionary<Type, List<Func<object>>> RegisteredFactories { get; }
dpvreony marked this conversation as resolved.
Show resolved Hide resolved
= new Dictionary<Type, List<Func<object>>>();

/// <inheritdoc />
public object GetService(Type serviceType, string contract = null)
{
Func<object> fact = RegisteredFactories[serviceType].LastOrDefault();
dpvreony marked this conversation as resolved.
Show resolved Hide resolved
return fact?.Invoke();
}

/// <inheritdoc/>
public IEnumerable<object> GetServices(Type serviceType, string contract = null)
{
return RegisteredFactories[serviceType]
dpvreony marked this conversation as resolved.
Show resolved Hide resolved
.Select(n => n());
}

/// <inheritdoc />
public bool HasRegistration(Type serviceType, string contract = null)
{
return RegisteredFactories.TryGetValue(serviceType, out List<Func<object>> values)
dpvreony marked this conversation as resolved.
Show resolved Hide resolved
&& values.Any();
}

/// <inheritdoc />
public void Register(Func<object> factory, Type serviceType, string contract = null)
{
if (!RegisteredFactories.ContainsKey(serviceType))
dpvreony marked this conversation as resolved.
Show resolved Hide resolved
{
RegisteredFactories.Add(serviceType, new List<Func<object>>());
}

RegisteredFactories[serviceType].Add(factory);
}

/// <inheritdoc />
public void UnregisterCurrent(Type serviceType, string contract = null)
{
throw new NotImplementedException();
}

/// <inheritdoc />
public void UnregisterAll(Type serviceType, string contract = null)
{
if (RegisteredFactories.ContainsKey(serviceType))
dpvreony marked this conversation as resolved.
Show resolved Hide resolved
{
RegisteredFactories.Remove(serviceType);
}
}

/// <inheritdoc />
public IDisposable ServiceRegistrationCallback(Type serviceType, string contract, Action<IDisposable> callback)
{
throw new NotImplementedException();
}

/// <inheritdoc />
#pragma warning disable CA1063 // Implement IDisposable Correctly
public void Dispose()
#pragma warning restore CA1063 // Implement IDisposable Correctly
{
}
}
}
2 changes: 1 addition & 1 deletion src/Splat.SimpleInjector/Splat.SimpleInjector.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net461</TargetFrameworks>
<Description>SimpleInjector adapter for Splat</Description>
<LangVersion>latest</LangVersion>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
5 changes: 3 additions & 2 deletions src/Splat.SimpleInjector/SplatSimpleInjectorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ public static class SplatSimpleInjectorExtensions
/// Initializes an instance of <see cref="SimpleInjectorDependencyResolver"/> that overrides the default <see cref="Locator"/>.
/// </summary>
/// <param name="container">Simple Injector container.</param>
/// <param name="initializer">Initializer.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Dispose handled by locator.")]
public static void UseSimpleInjectorDependencyResolver(this Container container) =>
Locator.SetLocator(new SimpleInjectorDependencyResolver(container));
public static void UseSimpleInjectorDependencyResolver(this Container container, SimpleInjectorInitializer initializer) =>
Locator.SetLocator(new SimpleInjectorDependencyResolver(container, initializer));
}
}
21 changes: 21 additions & 0 deletions src/Splat.SimpleInjector/TransientSimpleInjectorRegistration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using System.Linq.Expressions;
using SimpleInjector;

namespace Splat.SimpleInjector
{
internal class TransientSimpleInjectorRegistration : Registration
{
public TransientSimpleInjectorRegistration(Container container, Type implementationType, Func<object> instanceCreator = null)
: base(Lifestyle.Transient, container, implementationType, instanceCreator)
{
}

public override Expression BuildExpression() => BuildTransientExpression();
}
}