代码之家  ›  专栏  ›  技术社区  ›  AFract

从类内部解析autofac依赖项,不使用参数化构造函数

  •  0
  • AFract  · 技术社区  · 6 年前

    我面临一个与依赖注入相关的小问题。我有一个程序,它使用了一个asp.net mvc项目的例子中使用的依赖注入的最新规则,以及一些控制台程序。

    但它也包含某种“服务定位器”反模式。

    我将尝试用一个非常简单的控制台项目来说明它:

    using System;
    using Autofac;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace AutofacIssue
    {
        class Program
        {
            static void Main(string[] args)
            {
                var builder = new ContainerBuilder();
                builder.RegisterModule<MyModule>();
                var container = builder.Build();
    
                using (var scope = container.BeginLifetimeScope())
                {
                    var service = scope.Resolve<UserService>();
    
                    var users = service.CreateSomeUsers();
    
                    var info = users.First().GetSomeInfo;
                    Console.WriteLine(info.Something);
                }
            }
        }
    
        internal class MyModule : Autofac.Module
        {
            protected override void Load(ContainerBuilder builder)
            {
                base.Load(builder);
    
                builder.RegisterType<UserService>();
                builder.RegisterType<UserRelatedInfoService>();
            }
        }
    
        internal class UserRelatedInfoService
        {
            public UserInfo GetForUser(int id)
            {
                return new UserInfo("Various infos for " + id);
            }
        }
    
        internal class UserService
        {
            public IEnumerable<User> CreateSomeUsers()
            {
                return Enumerable.Range(1, 10).Select(r => new User(r)); // Remark "new User()" is called from many various spaces !
            }
        }
    
        internal class UserInfo
        {
            // Some things
            public string Something;
    
            public UserInfo(string someThings)
            {
                this.Something = someThings;
            }
        }
    
        /// <summary>
        /// "Service locator" antipattern
        /// </summary>
        class DependencyLocator
        {
            public static IContainer Container { get; }
    
            static DependencyLocator()
            {
                var builder = new ContainerBuilder();
                builder.RegisterModule<MyModule>();
                Container = builder.Build();
            }
        }
    
        internal class User
        {
            public User(int id)
            {
                this.Id = id;
            }
    
            public int Id { get; private set; }
    
            /// <summary>
            /// Here's my problematic property using the DependencyLocator
            /// </summary>
            public UserInfo GetSomeInfo
            {
                get
                {
                    UserRelatedInfoService userRelatedInfoService = DependencyLocator.Container.Resolve<UserRelatedInfoService>();
                    return userRelatedInfoService.GetForUser(Id);
                }
            }
        }
    }
    

    反模式允许编写非常小的代码,工作得非常好,但违反了di的一些原则(由于“服务定位器”+重复的容器,每个容器都有自己的生命周期)。

    此实现还具有实例化 UserRelatedInfoService 只有在实际需要时,如果实际调用了用户的相关属性(请记住,现实世界中的示例要复杂得多,与此相关的一些操作可能会有成本)

    在现实世界的例子中,我在许多程序集中都遇到了这种情况,每个程序集都需要能够以相同的方式解析依赖关系。

    我的问题是:不需要修改 User 构造函数,以及实例化某些 用户 ,有没有干净的方法来避免这种情况?

    例如通过某种依赖关系的“动态解析”?

    请注意 用户 与我的程序集不在同一程序集中 Program 类,所以我无法访问原始的 Container 作为公共财产。

    我想的一个解决办法是让全班 DependencyLocator 但要删除它的内容,只需分配它的 集装箱 属性中创建的属性。

    编辑:仅供参考,到目前为止,我只是按照自己的建议和修改的dependencLocator来避免它重新构建自己的容器,并在其上设置在应用程序入口点构建的最终容器。这是一个简单的改变,它避免了原来问题中指出的大多数问题。 至少,代码将始终使用相同的容器。

    谢谢你的阅读!

    1 回复  |  直到 6 年前
        1
  •  0
  •   Jeff    6 年前

    对于这种需要按类型进行运行时解析的边缘情况,可以注册iserviceprovider或func(或将object[]作为输入参数的func)

     builder.Register(ctx => ctx as IServiceProvider ??           
       ctx.Resolve<ILifetimeScope>() as IServiceProvider)
       .InstancePerLifetimeScope().AsSelf();
    

     builder.Register(c =>
            {
                var scope = c.Resolve<ILifetimeScope>();
                return (Func<Type, object>)(t => scope.Resolve(t));
            }).As<Func<Type, object>>();