繁体   English   中英

从类内部解决Autofac依赖关系,而无需参数化构造函数

[英]Resolving Autofac dependencies from inside of classes, without parameterized constructors

我面临一个与依赖注入有关的小问题。 我有一个程序,该程序使用了有关依赖项注入的最新技术规则(从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的某些原理(由于“服务定位器” +重复的Container,每个都有自己的生命周期)。

如果实际上调用了User的相关属性,则该实现还具有仅在实际需要时才实例化UserRelatedInfoService的优点(请注意,真实世界中的示例要复杂得多,与此相关的某些操作可能会产生成本)

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

我的问题是:无需修改User构造函数,而实例化某个User的所有对象的构造函数,是否有避免这种情况的干净方法?

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

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

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

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

谢谢阅读!

对于需要按类型进行运行时解析的此类极端情况,可以注册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>>();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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