Orleans.Multitenant icon indicating copy to clipboard operation
Orleans.Multitenant copied to clipboard

Exception when trying to resolve 'Orleans.Storage.AdoNetGrainStorage'

Open poliakhd opened this issue 2 years ago • 1 comments

Hey,

If I undestood correctly - this library should have support for any of the Orleans storage providers (correct me if I'm wrong), but I'm having this error with 'Orleans.Storage.AdoNetGrainStorage' (Posgresql):

Orleans.Runtime.OrleansException: Error from storage provider <redacted> during ReadState for grain Type=<redacted> Id=<redacted>|1 Error=
 
Exc level 0: System.InvalidOperationException: A suitable constructor for type 'Orleans.Storage.AdoNetGrainStorage' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)

After a bit of reseach I found that in TenantGrainStorageFactory in line below what being passed to ActivatorUtilities.CreateInstance is T options while AdoNet storage wants IOptions<T>:

return ActivatorUtilities.CreateInstance<TGrainStorage>(services, tenantProviderName, options);

To illustrate: image image

As of now I cloned the code and hijacked into storage grain creation process to continue my testing, but wondering if this can be addressed properly and pushed as nuget?

poliakhd avatar Jun 10 '23 16:06 poliakhd

Thanks for reporting this @poliakhd , I plan to refresh Orleans.Multitenant for Orleans 8 and will look into this as well

VincentH-Net avatar Dec 08 '23 08:12 VincentH-Net

@poliakhd I just saw this issue was not addressed when I intended to - but better late than never!

I reproduced the issue; to address it I added an optional getProviderParameters parameter. This is how you can use it for the ADO.NET grain storage provider:

.AddMultitenantGrainStorageAsDefault<AdoNetGrainStorage, AdoNetGrainStorageOptions, AdoNetGrainStorageOptionsValidator>(
    (silo, name) => silo.AddAdoNetGrainStorage(name, options => options.ConnectionString = sqlConnectionString),

    configureTenantOptions: (options, tenantId) => options.ConnectionString = sqlConnectionString,

    getProviderParameters: (services, providerName, tenantProviderName, options) => [Options.Create(options)]
)

(you could ofc also make the connection string tenant-specific in configureTenantOptions, e.g. separate databases per tenant - that is not in above example)

getProviderParameters is a GrainStorageProviderParametersFactory, which is fully documented:

/// <summary>
/// Factory delegate, used to create parameters for a tenant grain storage provider constructor.<br /> 
/// Allows to e.g. transform the options if the provider expects a different type than <typeparamref name="TGrainStorageOptions"/>,<br />
/// or to retrieve an add extra parameters like <see cref="Configuration.ClusterOptions" />
/// </summary>
/// <typeparam name="TGrainStorageOptions">The provider-specific grain storage options type, e.g. Orleans.Storage.MemoryGrainStorageOptions or Orleans.Storage.AzureTableStorageOptions</typeparam>
/// <param name="services">The silo services</param>
/// <param name="providerName">The name - without the tenant id - of the provider; can be used to access named provider services that are not tenant specific</param>
/// <param name="tenantProviderName">The name - including the tenant id - of the tenant provider; can be used to access named provider services that are tenant specific</param>
/// <param name="options">The options to pass to the provider. Note that configureTenantOptions and options validation have already been executed on this</param>
/// <returns>The tenant storage provider construction parameters to pass to DI. Don't include <paramref name="tenantProviderName"/> in these; it is added automatically</returns>
public delegate object[] GrainStorageProviderParametersFactory<in TGrainStorageOptions>(
    IServiceProvider services,
    string providerName,
    string tenantProviderName,
    TGrainStorageOptions options
);

The new API will be included in the upcoming 2.2 release (ETA next week). Hth!

VincentH-Net avatar Aug 16 '24 17:08 VincentH-Net