使用泛型时不注入属性 (PropertiesAutowired)<T>
Properties (PropertiesAutowired) are not injected when using generics<T>
首先我们来看一下代码:
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();
}
}
}
如果我将所有内容都更改为构造函数,它会完美地工作,但是,这里使用注入属性的主要思想是避免构造函数地狱。
在上面的代码片段中,有一些注释我提到了哪些有效,哪些无效。当没有使用泛型时,属性注入可以正常工作。
所以,我想问问你们,我在这里做错了什么以及我的代码缺少哪些功能?
非常感谢!
您在这里遇到的问题主要是关于您指定的位置 PropertiesAutowired
与您正在解决的问题。
我已经用一些额外的评论更新了你的 RegisterServices
方法。
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();
}
从根本上说,我认为您可能需要重新调整一些通用 class 与具体 class 继承。我不认为有一种直接的方法可以像您在这里尝试的那样通过具体的注册来提供通用服务。
首先我们来看一下代码:
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();
}
}
}
如果我将所有内容都更改为构造函数,它会完美地工作,但是,这里使用注入属性的主要思想是避免构造函数地狱。
在上面的代码片段中,有一些注释我提到了哪些有效,哪些无效。当没有使用泛型时,属性注入可以正常工作。
所以,我想问问你们,我在这里做错了什么以及我的代码缺少哪些功能?
非常感谢!
您在这里遇到的问题主要是关于您指定的位置 PropertiesAutowired
与您正在解决的问题。
我已经用一些额外的评论更新了你的 RegisterServices
方法。
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();
}
从根本上说,我认为您可能需要重新调整一些通用 class 与具体 class 继承。我不认为有一种直接的方法可以像您在这里尝试的那样通过具体的注册来提供通用服务。