Я столкнулся с ошибкой выполнения при использовании autofac в сочетании с гибридным кешем и Redis. Я надеюсь, что кто-нибудь сможет объяснить, почему возникает эта ошибка.
При использовании autofac для разрешения экземпляра HybridCache в сочетании с redis autofac выдает исключение DependencyResolutionException, которое DefaultHybridCache пытается создать другой экземпляр самого себя. Это происходит при попытке разрешить экземпляр HybridcCache.
Полная ошибка:
DependencyResolutionException: конструктор типа «Microsoft.Extensions.Caching.Hybrid.Internal.DefaultHybridCache» попытался создать другой экземпляр самого себя. Это не разрешено, поскольку служба настроена на разрешение только одного экземпляра за весь срок действия.
Это происходит, когда я регистрирую Redis с общим мультиплексором. Я не уверен, почему это важно, код воспроизведения см. ниже. При использовании системы DI по умолчанию в .NET ошибок нет.
Итак, короткий вопрос: является ли это ошибкой в autofac (или библиотеке redis/hybridcache) или я неправильно использую библиотеки?
Минимальное воспроизведение выглядит следующим образом:
Добавление autofac в серверное приложение blazor, настроенное, как показано ниже, дает исключение при разрешении ГирбидКэш.
using Autofac.Extensions.DependencyInjection;
using BlazorAppAutofac.Components;
using BlazorAppAutofac.Services;
using StackExchange.Redis;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddHybridCache();
IConnectionMultiplexer connectionMultiplexer = ConnectionMultiplexer.Connect(new ConfigurationOptions
{
EndPoints = { $"localhost:6379" },
AbortOnConnectFail = false,
});
builder.Services.AddSingleton(connectionMultiplexer);
builder.Services.AddStackExchangeRedisCache(options =>
{
options.ConnectionMultiplexerFactory = () => Task.FromResult(connectionMultiplexer);
options.InstanceName = "BlazorAppAutofac";
});
builder.Services.AddTransient();
При использовании приведенного ниже кода для регистрации Redis (без добавления мультиплексора) исключение не возникает.
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("localhost:6379");
options.InstanceName = "BlazorAppAutofac";
});
В Home.razor
@inject IMessageService MessageService
В MessageService.cs
public class MessageService : IMessageService
{
private readonly HybridCache _cache;
public MessageService(HybridCache cache)
{
_cache = cache;
}
}
Я ожидал, что оба варианта регистрации будут работать, поскольку они также работают с внедрением зависимостей по умолчанию.
Интересно, что в коде StackExchangeRedis есть некоторый код, который пытается обойти эту проблему.
internal sealed class RedisCacheImpl : RedisCache
{
private readonly IServiceProvider _services;
internal override bool IsHybridCacheActive()
=> _services.GetService() is not null;
public RedisCacheImpl(IOptions optionsAccessor, ILogger logger, IServiceProvider services)
: base(optionsAccessor, logger)
{
_services = services; // important: do not check for HybridCache here due to dependency - creates a cycle
}
public RedisCacheImpl(IOptions optionsAccessor, IServiceProvider services)
: base(optionsAccessor)
{
_services = services; // important: do not check for HybridCache here due to dependency - creates a cycle
}
}
Трассировка стека:
DependencyResolutionException: The constructor of type 'Microsoft.Extensions.Caching.Hybrid.Internal.DefaultHybridCache' attempted to create another instance of itself. This is not permitted because the service is configured to only allowed a single instance per lifetime scope.
Autofac.Core.Lifetime.LifetimeScope.CreateSharedInstance(Guid id, Func creator)
Autofac.Core.Lifetime.LifetimeScope.CreateSharedInstance(Guid primaryId, Nullable qualifyingId, Func creator)
Autofac.Core.Resolving.Middleware.SharingMiddleware.Execute(ResolveRequestContext context, Action next)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+c__DisplayClass14_0.b__1(ResolveRequestContext context)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+c__DisplayClass14_0.b__1(ResolveRequestContext context)
Autofac.Core.Resolving.Middleware.CircularDependencyDetectorMiddleware.Execute(ResolveRequestContext context, Action next)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+c__DisplayClass14_0.b__1(ResolveRequestContext context)
Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext context)
Autofac.Core.Resolving.ResolveOperation.InvokePipeline(ref ResolveRequest request, DefaultResolveRequestContext requestContext)
Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ref ResolveRequest request)
Autofac.Core.Resolving.ResolveOperation.Autofac.Core.Resolving.IResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ref ResolveRequest request)
Autofac.Core.Resolving.Pipeline.DefaultResolveRequestContext.ResolveComponent(ref ResolveRequest request)
Autofac.Core.Activators.Reflection.AutowiringParameter+c__DisplayClass0_0.b__0()
Autofac.Core.Activators.Reflection.BoundConstructor.Instantiate()
Autofac.Core.Activators.Reflection.ReflectionActivator+c__DisplayClass14_0.b__0(ResolveRequestContext context, Action next)
Autofac.Core.Resolving.Middleware.DelegateMiddleware.Execute(ResolveRequestContext context, Action next)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+c__DisplayClass14_0.b__1(ResolveRequestContext context)
Autofac.Core.Resolving.Middleware.DisposalTrackingMiddleware.Execute(ResolveRequestContext context, Action next)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+c__DisplayClass14_0.b__1(ResolveRequestContext context)
Autofac.Extensions.DependencyInjection.KeyedServiceMiddleware.Execute(ResolveRequestContext context, Action next)
Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder+c__DisplayClass14_0.b__1(ResolveRequestContext context)
Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action next)
Подробнее здесь: https://stackoverflow.com/questions/797 ... with-redis