Idea for improvement of DI

Hi everyone.

I found very useful a new collection and scope features. But now constructors look a little weird.

        SomeConstructor(ISomeNamedBucketProvider nameBucketProvider, string scopeName, string collectionName)

plus a typical use case.

        var bucket = await _namedBucketProvider.GetBucketAsync();
        var scope = await bucket.ScopeAsync(_scopeName);
        var collection = await scope.CollectionAsync(_collectionName);

I use only one collection per instance. Probably a lot of my colleagues have the same use cases. What do you think about adding such interfaces?

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddCouchbaseCollection<T>(this IServiceCollection services, string bucketName, string scopeName = "_default", string collectionName = "_default")
        where T : class, INamedCollectionProvider
    { }
    public static IServiceCollection AddCouchbaseCollection<T, TBucketProvier>(this IServiceCollection services, string scopeName = "_default", string collectionName = "_default") 
        where T : class, INamedCollectionProvider
        where TBucketProvier: class, INamedBucketProvider
    { }
    public static IServiceCollection AddCouchbaseScope<T>(this IServiceCollection services, string bucketName, string scopeName = "_default") 
        where T : class, INamedScopeProvider
    { }
    public static IServiceCollection AddCouchbaseScope<T, TBucketProvier>(this IServiceCollection services, string scopeName = "_default")
        where T : class, INamedScopeProvider
        where TBucketProvier : class, INamedBucketProvider
    { }
}

public interface INamedScopeProvider
{
    public string BucketName { get; }
    public string ScopeName { get; }

    public ValueTask<IScope> GetScopeAsync();
}

public interface INamedCollectionProvider
{
    public string BucketName { get; }
    public string ScopeName { get; }
    public string CollectionName { get; }

    public ValueTask<ICouchbaseCollection> GetCollectionAsync();
}

Hi @Ramirag -

Looks interesting, have you considered doing an implementation and pushing a pull request? The SDK is open source and contributions like this can be very useful to others.

Feel free to reach out if you need any assistance.

-Jeff

Interesting idea. It will be useful to get experience with code generation. But I have to find time for it.

Thoughts @btburnett3?

I’ve got some ideas, I’ll try to work up a PoC in the next few days.

1 Like

@jmorris @Ramirag

I’ve pushed up a proposed approach to DI of collections, I’d love some feedback from everyone:

http://review.couchbase.org/c/couchbase-net-client/+/161332

The syntax to register I’m proposing is:

public interface IMyBucket : INamedBucketProvider
{
}

public interface IMyDefaultCollectionProvider : INamedCollectionProvider
{
}

public interface IMyCollectionProvider : INamedCollectionProvider
{
}

services.AddCouchbaseBucket<IMyBucket>("my-bucket", builder => {
    builder.AddDefaultCollection<IMyDefaultCollectionProvider>();
    builder.AddCollection<IMyCollection>("my-scope", "my-collection");
});

One decision I made, which may or may not be correct, was to avoid the intermediate INamedScopeProvider. So far I haven’t come up with a good reason why you would inject a scope, since it’s really just an intermediate layer to get to collections anyway. But I’m open to arguments on this point.

Another alternative syntax which may be worth considering, which reduces repetition of the scope name during building:

services.AddCouchbaseBucket<IMyBucket>("my-bucket", builder => {
    builder.AddDefaultCollection<IMyDefaultCollectionProvider>();
    builder.AddScope("my-scope")
        .AddCollection<IMyCollection>("my-collection")
        .AddCollection<IMyOtherCollection>("my-other-collection");
});

Thanks,
Brant

1 Like

After some further thought, I went ahead and changed to my second idea, AddScope chained with AddCollection calls. I think that will be much cleaner since many scopes will have multiple collections.

1 Like

I have only one more suggestion. It can be useful to understand do we need to add a key prefix or not, without using constans.

public interface INamedScopeProvider
{
    public string BucketName { get; }
    public string ScopeName { get; }
    public bool IsDefaultScope { get; }
}

public interface INamedCollectionProvider
{
    public string BucketName { get; }
    public string ScopeName { get; }
    public string CollectionName { get; }
    public bool IsDefaultScope { get; }
    public bool IsDefaultCollection { get; }
}

@ramirag

That’s a good point. But I’d argue those properties are probably better placed directly on IScope and ICouchbaseCollection. That way they’d be available more broadly. Thoughts?

1 Like

Definitely, it would be better.

Hi @jmorris, @btburnett3.

You forgot to update NuGet Gallery | Couchbase.Extensions.DependencyInjection 3.2.0 to 3.2.3

@Ramirag -

We will be releasing a 3.2.4 version next week.

Jeff