【问题标题】:Resolving Autofac dependencies from inside of classes, without parameterized constructors从类内部解决 Autofac 依赖项,无需参数化构造函数
【发布时间】:2018-09-06 16:29:46
【问题描述】:

我面临一个与依赖注入相关的小问题。我有一个程序,使用了最先进的依赖注入规则,从 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);
            }
        }
    }
}

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

这个实现还有一个优点是,仅在实际需要时实例化UserRelatedInfoService,如果实际调用了 User 的相关属性(请记住,现实世界的示例要复杂得多,并且一些与此相关的操作可能要收费)

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

我的问题是:不修改User 构造函数,以及实例化一些User 的所有对象的构造函数,有没有一种干净的方法来避免这种情况?

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

请注意User 与我的Program 类不在同一个程序集中,因此我无法将原始Container 作为公共属性访问。

我认为的一个解决方案是保留 DependencyLocator 类,但删除其内容,并将其 Container 属性分配给 main 中创建的属性。

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

感谢阅读!

【问题讨论】:

  • 为什么不能修改User的构造函数?否则你能修改User类的代码吗?是否只是为了避免重写使用相同对象的其他代码,也许没有依赖注入?
  • @ColinYoung :因为我可能有不止一个类“用户”有这种问题,而且因为“用户”是一个业务类,而不是服务。所以它可以有几个具有不同签名的构造函数,主要接受像原始类型这样的“数据”参数。
  • 看看 Autofac 中的delegate factories。我认为这可能会让你想出一个更优雅的解决方案。

标签: c# .net dependency-injection autofac


【解决方案1】:

对于像这种需要按类型进行运行时解析的边缘情况,您可以注册 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>>();

【讨论】:

  • 您好杰夫,感谢您的回答。对不起,我不明白,我如何在现实世界的例子中使用它,例如从我的 User 类?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-12
相关资源
最近更新 更多