Skip to content

Commit

Permalink
ValueTuple support & ResolveOrDefault()
Browse files Browse the repository at this point in the history
  • Loading branch information
z4kn4fein committed Feb 27, 2022
1 parent ae7d094 commit 3159cb5
Show file tree
Hide file tree
Showing 22 changed files with 492 additions and 95 deletions.
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.1.0
5.2.0
16 changes: 10 additions & 6 deletions docs/advanced/wrappers-resolvers.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ IJob job = funcOfJob(config["connectionString"], new ConsoleLogger());
<!-- div:left-panel -->
### Metadata & Tuple
With the `.WithMetadata()` registration option, you can attach additional information to a service.
To gather this information, you can request the service wrapped in either `Metadata<,>` or `Tuple<,>`.
To gather this information, you can request the service wrapped in either `Metadata<,>`, `ValueTuple<,>`, or `Tuple<,>`.

`Metadata<,>` is a type from the `Stashbox` package, so you might prefer using `Tuple<,>` if you want to avoid referencing Stashbox in certain parts of your project.
`Metadata<,>` is a type from the `Stashbox` package, so you might prefer using `ValueTuple<,>` or `Tuple<,>` if you want to avoid referencing Stashbox in certain parts of your project.

You can also filter a collection of services by their metadata. Requesting `IEnumerable<Tuple<,>>` will yield only those services that have the given type of metadata.
You can also filter a collection of services by their metadata. Requesting `IEnumerable<ValueTuple<,>>` will yield only those services that have the given type of metadata.
<!-- div:right-panel -->

<!-- tabs:start -->
Expand All @@ -110,9 +110,13 @@ var jobWithConnectionString = container.Resolve<Metadata<IJob, string>>();
// prints: "connection-string-to-db"
Console.WriteLine(jobWithConnectionString.Data);

var alsoJobWithConnectionString = container.Resolve<Tuple<IJob, string>>();
var alsoJobWithConnectionString = container.Resolve<ValueTuple<IJob, string>>();
// prints: "connection-string-to-db"
Console.WriteLine(alsoJobWithConnectionString.Item2);

var stillJobWithConnectionString = container.Resolve<Tuple<IJob, string>>();
// prints: "connection-string-to-db"
Console.WriteLine(stillJobWithConnectionString.Item2);
```

##### **Collection filtering**
Expand All @@ -125,10 +129,10 @@ container.Register<IService, Service3>(options => options
.WithMetadata(5));

// the result is: [Service1, Service2]
var servicesWithStringMetadata = container.Resolve<Tuple<IService, string>[]>();
var servicesWithStringMetadata = container.Resolve<ValueTuple<IService, string>[]>();

// the result is: [Service3]
var servicesWithIntMetadata = container.Resolve<Tuple<IService, int>[]>();
var servicesWithIntMetadata = container.Resolve<ValueTuple<IService, int>[]>();
```

<!-- tabs:end -->
Expand Down
8 changes: 4 additions & 4 deletions docs/usage/service-resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,24 +412,24 @@ IEnumerable<IJob> jobs = container.ResolveAll<IJob>();
## Optional resolution

<!-- div:left-panel -->
In cases where it's not guaranteed that a service is resolvable, either because it's not registered or any of its dependencies are missing, you can attempt an optional resolution by using the `nullResultAllowed` parameter of the `.Resolve()` method.
In cases where it's not guaranteed that a service is resolvable, either because it's not registered or any of its dependencies are missing, you can attempt an optional resolution by using the `ResolveOrDefault()` method.

In this case, the resolution request will return with `null` when the attempt fails.
In this case, the resolution request will return with `null` (or `default` in case of type values) when the attempt fails.
<!-- div:right-panel -->

<!-- tabs:start -->
#### **Generic API**
```cs
// returns null when the resolution fails.
IJob job = container.Resolve<IJob>(nullResultAllowed: true);
IJob job = container.ResolveOrDefault<IJob>();

// throws ResolutionFailedException when the resolution fails.
IJob job = container.Resolve<IJob>();
```
#### **Runtime type API**
```cs
// returns null when the resolution fails.
object job = container.Resolve(typeof(IJob), nullResultAllowed: true);
object job = container.ResolveOrDefault(typeof(IJob));

// throws ResolutionFailedException when the resolution fails.
object job = container.Resolve(typeof(IJob));
Expand Down
2 changes: 1 addition & 1 deletion sandbox/stashbox.benchmarks/Stashbox.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="Stashbox" Version="5.0.0" />
<PackageReference Include="Stashbox" Version="5.1.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
Expand Down
4 changes: 3 additions & 1 deletion src/Extensions/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ public static TypeInformation AsTypeInformation(this ParameterInfo parameter,
customAttributes,
parameter.Name,
parameter.HasDefaultValue(),
parameter.DefaultValue);
parameter.DefaultValue,
null);
}

public static TypeInformation AsTypeInformation(this MemberInfo member,
Expand All @@ -148,6 +149,7 @@ public static TypeInformation AsTypeInformation(this MemberInfo member,
customAttributes,
member.Name,
false,
null,
null);
}

Expand Down
40 changes: 38 additions & 2 deletions src/IDependencyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public interface IDependencyResolver : IServiceProvider,
/// <param name="nullResultAllowed">If true, the container will return with null instead of throwing <see cref="ResolutionFailedException"/>.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
object Resolve(Type typeFrom, bool nullResultAllowed = false, object[] dependencyOverrides = null);
[Obsolete("Please use Resolve(type) or ResolveOrDefault(type) instead.")]
object Resolve(Type typeFrom, bool nullResultAllowed, object[] dependencyOverrides = null);

/// <summary>
/// Resolves an instance from the container.
Expand All @@ -32,7 +33,42 @@ public interface IDependencyResolver : IServiceProvider,
/// <param name="nullResultAllowed">If true, the container will return with null instead of throwing <see cref="ResolutionFailedException"/>.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
object Resolve(Type typeFrom, object name, bool nullResultAllowed = false, object[] dependencyOverrides = null);
[Obsolete("Please use Resolve(type, name) or ResolveOrDefault(type, name) instead.")]
object Resolve(Type typeFrom, object name, bool nullResultAllowed, object[] dependencyOverrides = null);

/// <summary>
/// Resolves an instance from the container.
/// </summary>
/// <param name="typeFrom">The type of the requested instance.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
object Resolve(Type typeFrom, object[] dependencyOverrides = null);

/// <summary>
/// Resolves an instance from the container.
/// </summary>
/// <param name="typeFrom">The type of the requested instance.</param>
/// <param name="name">The name of the requested registration.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
object Resolve(Type typeFrom, object name, object[] dependencyOverrides = null);

/// <summary>
/// Resolves an instance from the container or default if the type does not exist.
/// </summary>
/// <param name="typeFrom">The type of the requested instance.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
object ResolveOrDefault(Type typeFrom, object[] dependencyOverrides = null);

/// <summary>
/// Resolves an instance from the container or default if the type does not exist.
/// </summary>
/// <param name="typeFrom">The type of the requested instance.</param>
/// <param name="name">The name of the requested registration.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
object ResolveOrDefault(Type typeFrom, object name, object[] dependencyOverrides = null);

/// <summary>
/// Resolves all registered types of a service.
Expand Down
10 changes: 5 additions & 5 deletions src/IFuncRegistrator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public interface IFuncRegistrator
/// <param name="factory">The factory delegate.</param>
/// <param name="name">The name of the factory registration.</param>
/// <returns>The <see cref="IStashboxContainer"/> instance.</returns>
IStashboxContainer RegisterFunc<TService>(Func<IDependencyResolver, TService> factory, string name = null);
IStashboxContainer RegisterFunc<TService>(Func<IDependencyResolver, TService> factory, object name = null);

/// <summary>
/// Registers a service with a factory resolver.
Expand All @@ -24,7 +24,7 @@ public interface IFuncRegistrator
/// <param name="factory">The factory delegate.</param>
/// <param name="name">The name of the factory registration.</param>
/// <returns>The <see cref="IStashboxContainer"/> instance.</returns>
IStashboxContainer RegisterFunc<T1, TService>(Func<T1, IDependencyResolver, TService> factory, string name = null);
IStashboxContainer RegisterFunc<T1, TService>(Func<T1, IDependencyResolver, TService> factory, object name = null);

/// <summary>
/// Registers a service with a factory resolver.
Expand All @@ -35,7 +35,7 @@ public interface IFuncRegistrator
/// <param name="factory">The factory delegate.</param>
/// <param name="name">The name of the factory registration.</param>
/// <returns>The <see cref="IStashboxContainer"/> instance.</returns>
IStashboxContainer RegisterFunc<T1, T2, TService>(Func<T1, T2, IDependencyResolver, TService> factory, string name = null);
IStashboxContainer RegisterFunc<T1, T2, TService>(Func<T1, T2, IDependencyResolver, TService> factory, object name = null);

/// <summary>
/// Registers a service with a factory resolver.
Expand All @@ -47,7 +47,7 @@ public interface IFuncRegistrator
/// <param name="factory">The factory delegate.</param>
/// <param name="name">The name of the factory registration.</param>
/// <returns>The <see cref="IStashboxContainer"/> instance.</returns>
IStashboxContainer RegisterFunc<T1, T2, T3, TService>(Func<T1, T2, T3, IDependencyResolver, TService> factory, string name = null);
IStashboxContainer RegisterFunc<T1, T2, T3, TService>(Func<T1, T2, T3, IDependencyResolver, TService> factory, object name = null);

/// <summary>
/// Registers a service with a factory resolver.
Expand All @@ -60,6 +60,6 @@ public interface IFuncRegistrator
/// <param name="factory">The factory delegate.</param>
/// <param name="name">The name of the factory registration.</param>
/// <returns>The <see cref="IStashboxContainer"/> instance.</returns>
IStashboxContainer RegisterFunc<T1, T2, T3, T4, TService>(Func<T1, T2, T3, T4, IDependencyResolver, TService> factory, string name = null);
IStashboxContainer RegisterFunc<T1, T2, T3, T4, TService>(Func<T1, T2, T3, T4, IDependencyResolver, TService> factory, object name = null);
}
}
48 changes: 46 additions & 2 deletions src/Resolution/Extensions/DependencyResolverExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public static class DependencyResolverExtensions
/// <param name="nullResultAllowed">If true, the container will return with null instead of throwing <see cref="ResolutionFailedException"/>.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
public static TKey Resolve<TKey>(this IDependencyResolver resolver, bool nullResultAllowed = false, object[] dependencyOverrides = null) =>
[Obsolete("Please use Resolve<Service>() or ResolveOrDefault<Service>() instead.")]
public static TKey Resolve<TKey>(this IDependencyResolver resolver, bool nullResultAllowed, object[] dependencyOverrides = null) =>
(TKey)resolver.Resolve(typeof(TKey), nullResultAllowed, dependencyOverrides);


Expand All @@ -29,9 +30,52 @@ public static TKey Resolve<TKey>(this IDependencyResolver resolver, bool nullRes
/// <param name="nullResultAllowed">If true, the container will return with null instead of throwing <see cref="ResolutionFailedException"/>.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
public static TKey Resolve<TKey>(this IDependencyResolver resolver, object name, bool nullResultAllowed = false, object[] dependencyOverrides = null) =>
[Obsolete("Please use Resolve<Service>(name) or ResolveOrDefault<Service>(name) instead.")]
public static TKey Resolve<TKey>(this IDependencyResolver resolver, object name, bool nullResultAllowed, object[] dependencyOverrides = null) =>
(TKey)resolver.Resolve(typeof(TKey), name, nullResultAllowed, dependencyOverrides);

/// <summary>
/// Resolves an instance from the container.
/// </summary>
/// <typeparam name="TKey">The type of the requested instance.</typeparam>
/// <param name="resolver">The dependency resolver.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
public static TKey Resolve<TKey>(this IDependencyResolver resolver, object[] dependencyOverrides = null) =>
(TKey)resolver.Resolve(typeof(TKey), dependencyOverrides);

/// <summary>
/// Resolves an instance from the container.
/// </summary>
/// <typeparam name="TKey">The type of the requested instance.</typeparam>
/// <param name="resolver">The dependency resolver.</param>
/// <param name="name">The name of the requested registration.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
public static TKey Resolve<TKey>(this IDependencyResolver resolver, object name, object[] dependencyOverrides = null) =>
(TKey)resolver.Resolve(typeof(TKey), name, dependencyOverrides);

/// <summary>
/// Resolves an instance from the container or default if the type does not exist.
/// </summary>
/// <typeparam name="TKey">The type of the requested instance.</typeparam>
/// <param name="resolver">The dependency resolver.</param>
/// <param name="name">The name of the requested registration.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
public static TKey ResolveOrDefault<TKey>(this IDependencyResolver resolver, object name, object[] dependencyOverrides = null) =>
(TKey)(resolver.ResolveOrDefault(typeof(TKey), name, dependencyOverrides) ?? default(TKey));

/// <summary>
/// Resolves an instance from the container or default if the type does not exist.
/// </summary>
/// <typeparam name="TKey">The type of the requested instance.</typeparam>
/// <param name="resolver">The dependency resolver.</param>
/// <param name="dependencyOverrides">A collection of objects which are used to override certain dependencies of the requested service.</param>
/// <returns>The resolved object.</returns>
public static TKey ResolveOrDefault<TKey>(this IDependencyResolver resolver, object[] dependencyOverrides = null) =>
(TKey)(resolver.ResolveOrDefault(typeof(TKey), dependencyOverrides) ?? default(TKey));

/// <summary>
/// Returns a factory method which can be used to activate a type.
/// </summary>
Expand Down
Loading

0 comments on commit 3159cb5

Please sign in to comment.