Инъекция собственности в Core Asp.Net

Я пытаюсь перенести приложение asp.net в ядро ​​asp.net. У меня есть инъекция свойств (используя ninject) в моей реализации UnitOfWork, как это.

[Inject]
public IOrderRepository OrderRepository { get; set; }
[Inject]
public ICustomerRepository CustomerRepository { get; set; }

Есть ли способ достичь той же функциональности, используя встроенный DI на .net-ядре? Также возможно использовать привязку на основе соглашения?

Ответ 1

Нет, встроенный контейнер DI/IoC намеренно упрощается как в использовании, так и в функциях, чтобы предложить базу для других контейнеров DI для плагина.

Таким образом, нет встроенной поддержки для: автоматического обнаружения, авторегистрации, декораторов или инжекторов или регистрации на основе конвенций. Также нет планов добавить это во встроенный контейнер, насколько я знаю.

Вам понадобится использовать сторонний контейнер с поддержкой вставки свойств.

Обратите внимание, что в 98% всех сценариев вложение свойств считается плохим, поскольку оно скрывает зависимости, и нет гарантии, что объект будет введен при создании класса.

С вводом конструктора вы можете принудительно выполнить это через конструктор и проверить значение null и не создавать экземпляр класса. При инъекции свойств это невозможно, и во время модульных тестов неясно, какие службы/зависимости требуется классу, когда они не определены в конструкторе, поэтому легко пропустить и получить NullReferenceExceptions.

Единственная действительная причина для Инъекции свойств, которую я когда-либо находил, заключалась в том, чтобы внедрять службы в прокси-классы, созданные сторонней библиотекой, то есть прокси WCF, созданные из интерфейса, где у вас нет контроля над созданием объекта.

Избегайте его везде else.

Ответ 2

Есть ли способ достичь такой же функциональности, используя сборку DI на ядре.net?

Нет, но вот как вы можете создать свои собственные атрибуты [inject] с помощью метода автозаполнения свойств.

Сначала создайте свой собственный InjectAttribute:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class InjectAttribute : Attribute
{
  public InjectAttribute() : base() { }
}

Затем создайте свой собственный InjectPropertySelector который использует отражение, чтобы проверить свойства, помеченные с помощью [inject]:

public class InjectPropertySelector : DefaultPropertySelector
{
  public InjectPropertySelector(bool preserveSetValues) : base(preserveSetValues)
  { }

  public override bool InjectProperty(PropertyInfo propertyInfo, object instance)
  {
    var attr = propertyInfo.GetCustomAttribute<InjectAttribute>(inherit: true);
    return attr != null && propertyInfo.CanWrite
            && (!PreserveSetValues
            || (propertyInfo.CanRead && propertyInfo.GetValue(instance, null) == null));
  }
}

Затем используйте свой селектор в ConfigureServices где вы AutofacServiceProvider:

public class Startup
{
  public IServiceProvider ConfigureServices(IServiceCollection services)
  {
    var builder = new ContainerBuilder();
    builder.Populate(services);

    // use your property selector to discover the properties marked with [inject]
    builder.RegisterType<MyServiceX>().PropertiesAutowired((new InjectablePropertySelector(true)););

    this.ApplicationContainer = builder.Build();
    return new AutofacServiceProvider(this.ApplicationContainer);
  }
}

Наконец, в вашем сервисе вы теперь можете использовать [inject]:

public class MyServiceX 
{
    [Inject]
    public IOrderRepository OrderRepository { get; set; }
    [Inject]
    public ICustomerRepository CustomerRepository { get; set; }
}

Вы, конечно же, можете принять это решение еще больше, например, используя атрибут для определения жизненного цикла службы выше вашего определения класса обслуживания...

[Injectable(LifetimeScope.SingleInstance)]
public class IOrderRepository

... и затем проверяя этот атрибут при настройке ваших сервисов через autofac. Но это выходит за рамки этого ответа.

Ответ 3

Когда доступен HttpContext (например, в контроллере), существует одно быстрое и грязное решение с использованием шаблона HttpContext сервисов:

ILoggingService Logger => HttpContext.RequestServices.GetService<ILoggingService>();

Однако это не рекомендуется, и обычно ссылки на службы должны быть получены с помощью инжектора конструктора или [FromServices] параметра [FromServices].