简体   繁体   中英

Properties (PropertiesAutowired) are not injected when using generics<T>

First, let's take a look in the code:

using System;
using Autofac;

namespace PropertyInjectionAutofacPoC
{
    public interface IInterfaceA { }
    public interface IInterfaceB
    {
        IInterfaceA ClassA { get; set; }
    }

    public class ClassA : IInterfaceA { }
    public class ClassB : IInterfaceB
    {
        public IInterfaceA ClassA { get; set; } // this is injected properly //
    }

    public class Z { }

    public interface IInterfaceC<T> { }
    public interface IInterfaceD<T>
    {
        IInterfaceA ClassA { get; set; }
        IInterfaceC<T> ClassC { get; set; }
    }

    public interface IInterfaceCZ : IInterfaceC<Z> { }

    public abstract class ClassD<T> : IInterfaceD<T>
    {
        public IInterfaceA ClassA { get; set; } // this is not injected, it's always null //
        public IInterfaceC<T> ClassC { get; set; } // this is not injected, it's always null //
    }

    public abstract class ClassC<T> : IInterfaceC<T> { }
    public sealed class ClassCZ : ClassC<Z>, IInterfaceCZ { }

    public interface IRepositoryZ : IInterfaceD<Z> { }
    public sealed class RepositoryZ : ClassD<Z>, IRepositoryZ { }

    internal class Program
    {
        private static IContainer _container;

        private static void Main()
        {
            try
            {
                RegisterServices();

                // it works //
                 var a = _container.Resolve<IInterfaceB>();

                 // it doesn't work //
                 var b = _container.Resolve<IRepositoryZ>(); // ClassC property is null
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            finally
            {
                DisposeServices();
            }
        }

        private static void RegisterServices()
        {
            var builder = new ContainerBuilder();

            builder.RegisterType<ClassA>().As<IInterfaceA>();
            builder.RegisterType<ClassB>().As<IInterfaceB>().PropertiesAutowired(); // works like a charm //

            builder.RegisterGeneric(typeof(ClassC<>)).As(typeof(IInterfaceC<>)).PropertiesAutowired();
            builder.RegisterGeneric(typeof(ClassD<>)).As(typeof(IInterfaceD<>)).PropertiesAutowired(); // it doesn't work //

            builder.RegisterType<ClassCZ>().As<IInterfaceCZ>();
            builder.RegisterType<RepositoryZ>().As<IRepositoryZ>();

            _container = builder.Build();
        }

        private static void DisposeServices()
        {
            if (_container != null &&
                _container is IDisposable disposable)
                disposable.Dispose();
        }
    }


}

If I change everything to constructors, it works perfectly like a charm but, the main idea for using injected properties here is to avoid the constructor hell.

In the code snippet above, there's some comments where I mentioned what works and what doesn't. The properties injection works properly when there's no generics in use.

So, I'm asking you guys, what am I doing wrong here and what my code is missing to work?

Thanks so much!

The problem you have here is mostly about where you've specified PropertiesAutowired vs what you are resolving.

I've updated your RegisterServices method with some additional comments.

private static void RegisterServices()
{
    var builder = new ContainerBuilder();

    builder.RegisterType<ClassA>().As<IInterfaceA>();
    builder.RegisterType<ClassB>().As<IInterfaceB>().PropertiesAutowired();

    // These registrations aren't really valid. You would never be able to
    // resolve IInterfaceC<> or IInterfaceD<>, because they are abstract classes, so cannot be constructed.
    // You'll always get a NoConstructorsFoundException.
    builder.RegisterGeneric(typeof(ClassC<>)).As(typeof(IInterfaceC<>)).PropertiesAutowired();
    builder.RegisterGeneric(typeof(ClassD<>)).As(typeof(IInterfaceD<>)).PropertiesAutowired();

    builder.RegisterType<ClassCZ>().As<IInterfaceCZ>();

    // When I resolve IRepositoryZ, this is the registration that gets provided. So this is where you need PropertiesAutowired.
    // Just because RepositoryZ derives from ClassD<Z> does not mean it inherits any of its component registration information,
    // which I think is what you may have been expecting.
    //
    // However, the resolve of IRepositoryZ will now throw a NoConstructorFoundException, because when it goes to inject IInterfaceC<Z>
    // onto the property, it hits the invalid registration problem above.
    builder.RegisterType<RepositoryZ>().As<IRepositoryZ>().PropertiesAutowired();

    _container = builder.Build();
}

Fundamentally, I think you may need to rejig some of your generic class vs concrete class inheritance. I don't think there's a straightforward way to make a generic service be supplied by a concrete registration as you have tried to do here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM