Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NCBC-2963: Add named collection dependency injection support
Motivation ---------- As an SDK consumer keeping my concerns separate via dependency injection, it makes sense to be able to inject a specific collection via DI without the receiver knowing the name of the scope or collection. This is an extension of the current approach which allows injection of buckets without knowing the name. Modifications ------------- Separate some of the logic in NamedBucketProxyGenerator into a shared ProxyModuleBuilder class. Switch the proxy generation of INamedBucketProvider classes from lazy to when the DI container is being configured. This also required moving some of our proxy generation internals out of DI into singletons. Add INamedCollectionProvider and the related proxy generation logic. Add IBucketBuilder and IScopeBuilder to allow extending a named bucket provider during registration with a set of one or more named collections. Also use extension methods to support registering the default scope/collection. Add a .NET 5 target to gain access to the DynamicDependencyAttribute, and annotate the members we're accessing via reflection. Add properties to IScope and ICouchbaseCollection to determine if they are "_default". This will help consumers who receive a collection blindly via INamedCollectionProvider recognize default collections. Enable nullable reference types for the entire project. Results ------- SDK consumers may use syntax like this to register collections: ```cs public interface IMyBucket : INamedBucketProvider { } public interface IMyDefaultCollectionProvider : INamedCollectionProvider { } public interface IMyCollectionProvider : INamedCollectionProvider { } services.AddCouchbaseBucket<IMyBucket>("my-bucket", builder => { builder.AddDefaultCollection<IMyDefaultCollectionProvider>(); builder.AddScope("my-scope") .AddCollection<IMyCollection>("my-collection"); }); ``` Syntax like this may be used to consume the collections: ```cs public class MyController : Controller { private readonly IMyCollectionProvider _collectionProvider; public MyController(IMyCollectionProvider collectionProvider) { _collectionProvider = collectionProvider; } public async Task<IActionResult> Get() { var collection = await _collectionProvider.GetCollectionAsync(); // Use the collection and return a result } } ``` SDK consumers on .NET 5 and later should be able to safely enable trimming because we've annotated the members we're accessing via reflection. Change-Id: Iaaea00aa5f1fcef53db64769e024585946d8b120 Reviewed-on: http://review.couchbase.org/c/couchbase-net-client/+/161332 Tested-by: Build Bot <build@couchbase.com> Reviewed-by: Jeffry Morris <jeffrymorris@gmail.com>
- Loading branch information
1 parent
994b0a6
commit 520a6be
Showing
25 changed files
with
857 additions
and
85 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
src/Couchbase.Extensions.DependencyInjection/BucketBuilderExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using System; | ||
|
||
namespace Couchbase.Extensions.DependencyInjection | ||
{ | ||
/// <summary> | ||
/// Extensions for <see cref="IBucketBuilder"/> and <see cref="IScopeBuilder"/>. | ||
/// </summary> | ||
public static class BucketBuilderExtensions | ||
{ | ||
/// <summary> | ||
/// Register an interface based on <see cref="INamedCollectionProvider"/> which will be injected | ||
/// with the default scope/collection. | ||
/// </summary> | ||
/// <typeparam name="T">Interface inherited from <see cref="INamedCollectionProvider"/>. Must not add any members.</typeparam> | ||
/// <param name="builder">The bucket builder.</param> | ||
/// <returns>The <see cref="IScopeBuilder"/> for the default scope, used for chaining.</returns> | ||
public static IScopeBuilder AddDefaultCollection<T>(this IBucketBuilder builder) | ||
where T : class, INamedCollectionProvider => | ||
builder.AddDefaultScope().AddDefaultCollection<T>(); | ||
|
||
/// <summary> | ||
/// Begin building the default scope. | ||
/// </summary> | ||
/// <param name="builder">The bucket builder.</param> | ||
/// <returns>The <see cref="IScopeBuilder"/> for building the scope.</returns> | ||
public static IScopeBuilder AddDefaultScope(this IBucketBuilder builder) => | ||
builder.AddScope("_default"); | ||
|
||
/// <summary> | ||
/// Register an interface based on <see cref="INamedCollectionProvider"/> which will be injected | ||
/// with the default scope/collection. | ||
/// </summary> | ||
/// <typeparam name="T">Interface inherited from <see cref="INamedCollectionProvider"/>. Must not add any members.</typeparam> | ||
/// <param name="builder">The scope builder.</param> | ||
/// <returns>The <see cref="IScopeBuilder"/> for chaining.</returns> | ||
private static IScopeBuilder AddDefaultCollection<T>(this IScopeBuilder builder) | ||
where T : class, INamedCollectionProvider => | ||
builder.AddCollection<T>("_default"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/Couchbase.Extensions.DependencyInjection/IBucketBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using System; | ||
|
||
namespace Couchbase.Extensions.DependencyInjection | ||
{ | ||
/// <summary> | ||
/// Applies additional configuration to a bucket for dependency injection. | ||
/// </summary> | ||
public interface IBucketBuilder | ||
{ | ||
/// <summary> | ||
/// Begin building a scope with one or more collections. | ||
/// </summary> | ||
/// <param name="scopeName">Name of the scope.</param> | ||
/// <returns>The <see cref="IScopeBuilder"/> for building the scope.</returns> | ||
IScopeBuilder AddScope(string scopeName); | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
src/Couchbase.Extensions.DependencyInjection/INamedCollectionProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using System; | ||
using System.Threading.Tasks; | ||
using Couchbase.KeyValue; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Couchbase.Extensions.DependencyInjection | ||
{ | ||
/// <summary> | ||
/// Base interface for injecting specific Couchbase collections. | ||
/// </summary> | ||
/// <remarks> | ||
/// Inherit an empty interface from this interface, and then use <see cref="IScopeBuilder.AddCollection{T}(string)"/> | ||
/// to register the interface in the <see cref="IServiceCollection"/>. | ||
/// </remarks> | ||
/// <example> | ||
/// <code> | ||
/// services.AddCouchbaseBucket<IMyBucket>("my-bucket", builder => { | ||
/// builder.AddDefaultCollection<IMyDefaultCollection>(); | ||
/// builder.AddScope("my-scope") | ||
/// .AddCollection<IMyCollection>("my-collection"); | ||
/// }); | ||
/// </code> | ||
/// </example> | ||
public interface INamedCollectionProvider | ||
{ | ||
/// <summary> | ||
/// Name of the scope. | ||
/// </summary> | ||
string ScopeName { get; } | ||
|
||
/// <summary> | ||
/// Name of the collection. | ||
/// </summary> | ||
string CollectionName { get; } | ||
|
||
/// <summary> | ||
/// Returns the collection. | ||
/// </summary> | ||
/// <returns>The <see cref="ICouchbaseCollection" />.</returns> | ||
ValueTask<ICouchbaseCollection> GetCollectionAsync(); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/Couchbase.Extensions.DependencyInjection/IScopeBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using System; | ||
|
||
namespace Couchbase.Extensions.DependencyInjection | ||
{ | ||
/// <summary> | ||
/// Applies additional configuration to a scope for dependency injection. | ||
/// </summary> | ||
public interface IScopeBuilder | ||
{ | ||
/// <summary> | ||
/// Register an interface based on <see cref="INamedCollectionProvider"/> which will be injected | ||
/// with a specific scope and collection name. | ||
/// </summary> | ||
/// <typeparam name="T">Interface inherited from <see cref="INamedCollectionProvider"/>. Must not add any members.</typeparam> | ||
/// <param name="collectionName">Name of the collection.</param> | ||
/// <returns>The <see cref="IScopeBuilder"/> for chaining.</returns> | ||
IScopeBuilder AddCollection<T>(string collectionName) | ||
where T : class, INamedCollectionProvider; | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/Couchbase.Extensions.DependencyInjection/Internal/BucketBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
using System; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.DependencyInjection.Extensions; | ||
|
||
namespace Couchbase.Extensions.DependencyInjection.Internal | ||
{ | ||
/// <summary> | ||
/// Default implementation of <see cref="IBucketBuilder"/>. | ||
/// </summary> | ||
internal class BucketBuilder : IBucketBuilder | ||
{ | ||
private readonly IServiceCollection _services; | ||
private readonly Type _bucketProviderType; | ||
private readonly bool _tryAddMode; | ||
|
||
public BucketBuilder(IServiceCollection services, Type bucketProviderType, bool tryAddMode) | ||
{ | ||
_services = services ?? throw new ArgumentNullException(nameof(services)); | ||
_bucketProviderType = bucketProviderType ?? throw new ArgumentNullException(nameof(bucketProviderType)); | ||
_tryAddMode = tryAddMode; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public IScopeBuilder AddScope(string scopeName) => new ScopeBuilder(this, scopeName); | ||
|
||
internal void AddCollection(Type collectionProviderType, string scopeName, string collectionName) | ||
{ | ||
var proxyType = | ||
NamedCollectionProxyGenerator.Instance.GetProxy(collectionProviderType, _bucketProviderType, scopeName, collectionName); | ||
|
||
if (_tryAddMode) | ||
{ | ||
_services.TryAddTransient(collectionProviderType, proxyType); | ||
} | ||
else | ||
{ | ||
_services.AddTransient(collectionProviderType, proxyType); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 0 additions & 1 deletion
1
src/Couchbase.Extensions.DependencyInjection/Internal/NamedBucketProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.