Autofac 的属性注入,IOC的坑
2014-11-08 22:31
375 查看
Autofac 是一款优秀的IOC的开源工具,完美的适配.Net特性,但是有时候我们想通过属性注入的方式来获取我们注入的对象,对不起,有时候你还真是获取不到,这因为什么呢?
1.你对Autofac 不太了解,在这个浮躁的社会,没有人会认真的了解每个开源项目,只要求能用就行
2.没有时间了解,你是一个很忙的人,工作很忙,应酬很忙
3.刚开始使用Autofac 还没来得及深入了解就要做项目。
不管是什么原因,总之我们注入的属性就是无法直接由autofac 自动注入,或者说我们希望由Autofac自动注入的属性为Null,这可是很让我们纠结的事情。下面我们就来通过一系列我们可能的操作来还原事情的真相。
真相1:通过registerType注册类型,希望通过属性获取注入的类型。
我们通过RegisterType注入了两个类型Test和Test2,其中Test2中由一个属性为Test类型的Test变量,我们期望Test会自动注入,我们可以直接使用Test.Show方法,但是现实情况是:
我们期望会被Autofac自动注入的属性为Null,我们的期望落空。既然通过RegisterType无法注入,那么通过Register注入呢,是否可行呢?
我们通过Register注入一个实例,最后我们的期望还是落空了,还有一种方式就是通过Module进行注册,这种方式还不行,那就说明autofac的属性注入式骗人的(心里想的),我们来通过Module来实现。
真相3:通过module进行注册
我们通过Module注册了Test,但是我们通过断点调试可以看到,我们通过属性注入的还是没有得到,还是为Null,这是不是autofac不支持属性注入,我们在心里只骂坑爹啊。
但是我们仔细看一下会发现一个问题,我们的Test2 是通过New得到的,而不是通过autofac得到,我们并没有将Test2注入到autofac中,是不是因为这个愿意呢?
我们来尝试一下,这可是我最后的机会了,因为除了这个原因我实在想不出还有什么别的原因。
我们修改了一下代码,将Test2也注入到Autofac中,然后通过autofac的resolve获取,奇迹出现了,我们看到了我们注册的类型,在属性注入得到了我们想要的实例。
这不得不说是一个令我激动的事情,因为这个属性得到的实在是太难,让我尝试了很多种,经历了很多次绝望。
所以我发现,如果你要想实现autofac的自动属性注入,由三个步骤,第一个通过module注册你要通过属性获取的类型,第二个,在属性所在的class中,也要注册到autofac中,最后一点,获取属性所在的class的实例必须通过autofac获取,也就是绝对不要通过new来获取,因为autofac的存在就是为了让你在一定程度上减少new的使用。
我们使用过autofac的MVC实现,我们发现在controller中可以得到我们的属性值,那是因为controller已经注册到了autofac中,因为肯定有一句builder。registerController的存在。
所在对于想实现autofac自动属性注入的朋友,一定要记得将类型通过module注入到autofac。并且属性所在的类型必须通过autofac获取,因为我们必须让autofac知道类型的存在,才可能会自动注入。
这是一篇说明文,简短的说明,希望没有高深的知识点,但是如果你不了解,会花费很长时间才可能查找到错误的地方。应验了那句话,知道了不难,不知道了难上加难。
我的座右铭:做架构师,要做牛逼的架构师。
1.你对Autofac 不太了解,在这个浮躁的社会,没有人会认真的了解每个开源项目,只要求能用就行
2.没有时间了解,你是一个很忙的人,工作很忙,应酬很忙
3.刚开始使用Autofac 还没来得及深入了解就要做项目。
不管是什么原因,总之我们注入的属性就是无法直接由autofac 自动注入,或者说我们希望由Autofac自动注入的属性为Null,这可是很让我们纠结的事情。下面我们就来通过一系列我们可能的操作来还原事情的真相。
真相1:通过registerType注册类型,希望通过属性获取注入的类型。
class Program { static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); // builder.RegisterModule(new LoggingModule()); builder.RegisterType<Test>(); builder.RegisterType<Test2>(); var container = builder.Build(); Test2 test2 = container.Resolve<Test2>(); test2.Show(); } } public class Test { public void Show() { Console.WriteLine("FileName"); } } public class Test2 { public Test Test { get; set; } public void Show() { if (Test != null) { Test.Show(); } } }
我们通过RegisterType注入了两个类型Test和Test2,其中Test2中由一个属性为Test类型的Test变量,我们期望Test会自动注入,我们可以直接使用Test.Show方法,但是现实情况是:
我们期望会被Autofac自动注入的属性为Null,我们的期望落空。既然通过RegisterType无法注入,那么通过Register注入呢,是否可行呢?
class Program { static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); // builder.RegisterModule(new LoggingModule()); //builder.RegisterType<Test>(); builder.Register(t => new Test()).As<Test>(); builder.RegisterType<Test2>(); var container = builder.Build(); Test2 test2 = container.Resolve<Test2>(); test2.Show(); } } public class Test { public void Show() { Console.WriteLine("FileName"); } } public class Test2 { public Test Test { get; set; } public void Show() { if (Test != null) { Test.Show(); } } }
我们通过Register注入一个实例,最后我们的期望还是落空了,还有一种方式就是通过Module进行注册,这种方式还不行,那就说明autofac的属性注入式骗人的(心里想的),我们来通过Module来实现。
真相3:通过module进行注册
class Program { static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); builder.RegisterModule(new LoggingModule()); //builder.RegisterType<Test>(); //builder.Register(t => new Test()).As<Test>(); //builder.RegisterType<Test2>(); var container = builder.Build(); //Test2 test2 = container.Resolve<Test2>(); Test2 ee = new Test2(); ee.Show(); } } public class Test { public void Show() { Console.WriteLine("FileName"); } } public class Test2 { public Test Test { get; set; } public void Show() { if (Test != null) { Test.Show(); } } } public class LoggingModule : Module { private readonly ConcurrentDictionary<string, Test> _loggerCache; public LoggingModule() { _loggerCache = new ConcurrentDictionary<string, Test>(); } protected override void Load(ContainerBuilder moduleBuilder) { // by default, use Coevery's logger that delegates to Castle's logger factory moduleBuilder.RegisterType<Test>().As<Test>().InstancePerLifetimeScope(); // call CreateLogger in response to the request for an ILogger implementation // moduleBuilder.Register(CreateLogger).As<ILogging.ILogger>().InstancePerDependency(); } protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) { var implementationType = registration.Activator.LimitType; // build an array of actions on this type to assign loggers to member properties var injectors = BuildLoggerInjectors(implementationType).ToArray(); // if there are no logger properties, there's no reason to hook the activated event if (!injectors.Any()) return; // otherwise, whan an instance of this component is activated, inject the loggers on the instance registration.Activated += (s, e) => { foreach (var injector in injectors) injector(e.Context, e.Instance); }; } private IEnumerable<Action<IComponentContext, object>> BuildLoggerInjectors(Type componentType) { // Look for settable properties of type "ILogger" var loggerProperties = componentType .GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance) .Select(p => new { PropertyInfo = p, p.PropertyType, IndexParameters = p.GetIndexParameters(), Accessors = p.GetAccessors(false) }) .Where(x => x.PropertyType == typeof(Test)) // must be a logger .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)); //must have get/set, or only set // Return an array of actions that resolve a logger and assign the property foreach (var entry in loggerProperties) { var propertyInfo = entry.PropertyInfo; yield return (ctx, instance) => { string component = componentType.ToString(); var logger = _loggerCache.GetOrAdd(component, key => ctx.Resolve<Test>(new TypedParameter(typeof(Type), componentType))); propertyInfo.SetValue(instance, logger, null); }; } }
我们通过Module注册了Test,但是我们通过断点调试可以看到,我们通过属性注入的还是没有得到,还是为Null,这是不是autofac不支持属性注入,我们在心里只骂坑爹啊。
但是我们仔细看一下会发现一个问题,我们的Test2 是通过New得到的,而不是通过autofac得到,我们并没有将Test2注入到autofac中,是不是因为这个愿意呢?
我们来尝试一下,这可是我最后的机会了,因为除了这个原因我实在想不出还有什么别的原因。
static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); builder.RegisterModule(new LoggingModule()); //builder.RegisterType<Test>(); //builder.Register(t => new Test()).As<Test>(); builder.RegisterType<Test2>(); var container = builder.Build(); Test2 test2 = container.Resolve<Test2>(); // Test2 ee = new Test2(); test2.Show(); }
我们修改了一下代码,将Test2也注入到Autofac中,然后通过autofac的resolve获取,奇迹出现了,我们看到了我们注册的类型,在属性注入得到了我们想要的实例。
这不得不说是一个令我激动的事情,因为这个属性得到的实在是太难,让我尝试了很多种,经历了很多次绝望。
所以我发现,如果你要想实现autofac的自动属性注入,由三个步骤,第一个通过module注册你要通过属性获取的类型,第二个,在属性所在的class中,也要注册到autofac中,最后一点,获取属性所在的class的实例必须通过autofac获取,也就是绝对不要通过new来获取,因为autofac的存在就是为了让你在一定程度上减少new的使用。
我们使用过autofac的MVC实现,我们发现在controller中可以得到我们的属性值,那是因为controller已经注册到了autofac中,因为肯定有一句builder。registerController的存在。
所在对于想实现autofac自动属性注入的朋友,一定要记得将类型通过module注入到autofac。并且属性所在的类型必须通过autofac获取,因为我们必须让autofac知道类型的存在,才可能会自动注入。
这是一篇说明文,简短的说明,希望没有高深的知识点,但是如果你不了解,会花费很长时间才可能查找到错误的地方。应验了那句话,知道了不难,不知道了难上加难。
我的座右铭:做架构师,要做牛逼的架构师。
相关文章推荐
- WebAPI2使用Autofac实现IOC属性注入完美解决方案
- ASP.NET Core中使用IOC三部曲(二.采用Autofac来替换IOC容器,并实现属性注入)
- ASP.NET Core中使用IOC三部曲(二.采用Autofac来替换IOC容器,并实现属性注入)
- WebAPI2使用Autofac实现IOC属性注入完美解决方案
- .NET领域最为流行的IOC框架之一Autofac WebAPI2使用Autofac实现IOC属性注入完美解决方案 AutoFac容器初步
- WebAPI2使用Autofac实现IOC属性注入完美解决方案
- Spring之ioc操作与属性注入
- Autofac官方文档(五)【注册组件之属性和方法注入】
- IoC组件~Autofac将多实现一次注入,根据别名Resove实例
- 从0开始写一个基于注解的轻量级分布式RPC框架(4)自定义Spring的IOC,自定义属性注入bean的过程
- IOC 构造函数注入vs属性注入
- AutoFac (控制反转IOC 与依赖注入DI)
- IOC依赖注入集合属性
- lesson one:spring ioc,属性注入方式,实现自己的beanfactoy
- Spring--IoC--域属性的注入-使用spring注解
- spring学习总结(四):IOC & DI 配置 Bean 之注入属性细节
- IoC容器Autofac正篇之依赖注入(六)
- Autofac 的属性注入方式
- 通过Setters方式进行普通属性的IOC注入
- autofac文档:属性注入