簡體   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