-
Notifications
You must be signed in to change notification settings - Fork 4k
.Net: First round of MEVD DI changes #10960
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
Conversation
Contributes to microsoft#10549
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code LGTM, but IMO if we want to introduce any breaking changes we should introduce more changes (remove most of the overloads, accept factory methods that take ServiceProvider
as an argument). PTAL at my comments and let me know what is your opinion.
/// <param name="serviceId">An optional service id to use as the service key.</param> | ||
/// <param name="serviceCollection">The <see cref="IServiceCollection"/> to which the vector store should be added.</param> | ||
/// <param name="options">Options to further configure the <see cref="IVectorStore"/>.</param> | ||
/// <param name="lifetime">The service lifetime for the store. Defaults to <see cref="ServiceLifetime.Transient"/>.</param> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why Transient
is the default? I would hope Azure AI Search SDK client to be the same as other Azure SDK clients:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree - this is specifically the thing I want to change in the follow-up to this PR, just didn't want to put everything in at once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I agree - see #10549 (comment) and below for the discussion. I'm mainly saving this for a later PR as it's a more important behavioral change, and I didn't want to do it all in one PR.
/// <param name="lifetime">The service lifetime for the store. Defaults to <see cref="ServiceLifetime.Singleton"/>.</param> | ||
/// <returns>The service collection.</returns> | ||
public static IServiceCollection AddKeyedAzureAISearchVectorStore( | ||
this IServiceCollection serviceCollection, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is the objectKey
nullable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't be, thanks.
Uri endpoint, | ||
AzureKeyCredential credential, | ||
AzureAISearchVectorStoreOptions? options = default, | ||
ServiceLifetime lifetime = ServiceLifetime.Transient) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that all these overloads already existed, but I just want to share my experience from HealthChecks project. You can read Xabaril/AspNetCore.Diagnostics.HealthChecks#2040, but to tell the long story short:
- all Azure SDK clients should be registered as singletons by default (they are thread safe)
- users may need to access
IServiceProvider
when creating the option bags (to for example read sth from the config system) - each client may provide various ctors that accept various arguments. To simplify the API we can just allow the users to provide a factory method that returns the client rather than having a dedicated overload for each client ctor (like here with Url, TokenCredential etc)
Having said that, the API shape could be following:
public static IServiceCollection AddAzureAISearchVectorStore(
this IServiceCollection serviceCollection,
Func<IServiceProvider, SearchIndexClient>? clientFactory = default, // when not provided, we just resolve it from the DI
Func<IServiceProvider, AzureAISearchVectorStoreOptions>? optionsFactory = default,
ServiceLifetime lifetime = ServiceLifetime.Singleton)
public static IServiceCollection AddKeyedAzureAISearchVectorStore(
this IServiceCollection serviceCollection,
object serviceKey // mandatory to provide,
Func<IServiceProvider, SearchIndexClient>? clientFactory = default, // when not provided, we just resolve it from the DI
Func<IServiceProvider, AzureAISearchVectorStoreOptions>? optionsFactory = default,
ServiceLifetime lifetime = ServiceLifetime.Singleton)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all Azure SDK clients should be registered as singletons by default (they are thread safe)
Yep, see above.
users may need to access IServiceProvider when creating the option bags (to for example read sth from the config system)
Good point - I overlooked that, thanks! I'm thinking to add an optional Func<IServiceProvider, SearchIndexClient>
parameter to the existing APIs; if it's provided, it's used, otherwise we attempt to resolve the SearchIndexClient from DI. How does that sound?
each client may provide various ctors that accept various arguments. To simplify the API we can just allow the users to provide a factory method that returns the client rather than having a dedicated overload for each client ctor (like here with Url, TokenCredential etc)
Sure, though I think that it's a bit nicer for the easy/default path to just have such sugar on it, so a single UseAzureAISearchVectorStore() call is enough to set everything up. Of course we shouldn't go overboard with it and add many of these.
string collectionName, | ||
Uri endpoint, | ||
TokenCredential tokenCredential, | ||
AzureAISearchVectorStoreRecordCollectionOptions<TRecord>? options = default, | ||
string? serviceId = default) | ||
ServiceLifetime lifetime = ServiceLifetime.Singleton) | ||
=> AddKeyedAzureAISearchVectorStoreRecordCollection(serviceCollection, serviceKey: null, collectionName, endpoint, tokenCredential, options, lifetime); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is my understanding correct that it's going to work only for a single collection? As soon as the user tries to register two, we are going to fail?
I wonder if we should simply always use Keyed DI for collections, were the key would be the collection name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the idea here is that the single-collection scenario is common enough for this to make sense as dedicated sugar... Users do have AddKeyedAzureAISearchVectorStoreRecordCollection it they want to work with multiple collections. Does that make sense?
string
toobject
.Contributes to #10549
/cc @westey-m @adamsitnik @dmytrostruk