Зарегистрируйте тип как InstancePerRequest с исключением (ями)

Я использую AutoFac в своем приложении Web API (используя последние версии, доступные на момент публикации этого вопроса). Одна из моих служебных зависимостей - это AuditService который использует экземпляр по DbContext тип (давайте назовем это MyDbContext на данный момент). Большинство моих услуг и MyDbContext Все типы зарегистрированы с использованием InstancePerRequest. Для меня AuditService Я хочу сделать исключение, я всегда хочу внедрить собственный (новый) экземпляр моего MyDbContext,

Вопрос: Как зарегистрироваться в AutoFac, как мне зарегистрировать AuditService таким образом, что он всегда получает собственный (новый) экземпляр MyDbContext?


Что может сработать:

  • Я мог бы жестко закодировать создание MyDbContext в конструкторе AuditService таким образом обойдя AutoFac все вместе.
  • Я мог бы использовать PropertyInjection или MethodInjection и предоставить новый экземпляр MyDbContext в событии Life Time OnActivating
  • Я мог бы определить второй интерфейс на MyDbContext и предоставить вторую регистрацию и использование InstancePerOwned,

Должен ли я выбрать один из указанных выше вариантов (если это так, я бы склонялся к 3) или я упустил что-то простое? Есть ли способ определить, что я хочу в своем регистрационном коде?


// registration called in web api startup code
public void RegisterAutofac(ContainerBuilder builder)
{
    builder.RegisterType<MyDbContext>()
        .As<IMyDbContext>()
        .InstancePerRequest();

    builder.RegisterType<BusinessService>()
        .As<IBusinessService>()
        .InstancePerRequest();

    builder.RegisterType<AuditService>()
        .As<IAuditService>()
        .InstancePerRequest();
}


public class AuditService
{
    // expects an isolated instance on this request
    private readonly IMyDbContext _dbContext;
    public AuditService(IMyDbContext dbContext)
    {
        _dbContext = dbContext;
    }
}

public class BusinessService
{
    // expect a shared IMyDbContext instance across the request
    private readonly IMyDbContext _dbContext;
    public BusinessService(IMyDbContext dbContext)
    {
        _dbContext = dbContext;
    }
}

Решение проблемы с InstancePerOwned

Это вызывает исключение

builder.RegisterType<MyDbContext>()
    .As<IMyDbContext>()
    .InstancePerRequest()
    .InstancePerOwned<AuditService>();

Autofac.Core.DependencyResolutionException: "Из области, в которой был запрошен экземпляр, не видна область с тегом, совпадающим с" AuditService ". Если вы видите это во время выполнения веб-приложения, это обычно означает, что компонент зарегистрирован как per-HTTP" запрос запрашивается компонентом SingleInstance() (или аналогичным сценарием).В рамках веб-интеграции всегда запрашивайте зависимости из средства разрешения зависимостей или области действия запроса, а не из самого контейнера.

at Autofac.Core.Lifetime.MatchingScopeLifetime.FindScope(ISharingLifetimeScope mostNestedVisibleScope)
at Autofac.Core.Resolving.InstanceLookup..ctor(IComponentRegistration registration, IResolveOperation context, ISharingLifetimeScope mostNestedVisibleScope, IEnumerable`1 parameter

Я пытался изменить порядок InstancePerOwned а также InstancePerRequest звонки, но это, кажется, не имеет никакого эффекта, то же самое MyDbContext Экземпляр повторно используется для обоих BusinessService а также AuditService экземпляры в том же запросе. Это было проверено с object.ReferenceEquals из в ApiController и прошло в обоих экземплярах _dbContext поля.

builder.RegisterType<MyDbContext>()
    .As<IMyDbContext>()
    .InstancePerOwned<AuditService>()
    .InstancePerRequest();

1 ответ

Попробуйте переключиться с InstancePerRequest в InstancePerLifetimeScope, В большинстве приложений это, как правило, ведет себя одинаково и является способом обмена регистрациями между приложениями, которые в любом случае имеют семантику для каждого запроса и не имеют ее. (То есть это довольно распространено.)

Когда у вас есть InstancePerLifetimeScope на вашем объекте контекста, вы можете использовать Owned<T> в вашем AuditService конструктор, чтобы получить свежую копию.

Так...

builder.RegisterType<MyDbContext>()
    .As<IMyDbContext>()
    .InstancePerLifetimeScope();

затем...

public AuditService(Owned<IMyDbContext> dbContext)

Обратите внимание на ваш AuditService будет нести ответственность за утилизацию dbContext когда это будет сделано, так что вам придется обрабатывать это вручную (это часть использования Owned<T>). Но если у вас уже есть что-то одноразовое, это не должно иметь большого значения.

Другие вопросы по тегам