1. ホーム
  2. Web プログラミング
  3. ASP.NET

Net CoreによるAutoFacの利用

2022-01-14 23:54:01

この記事ではIoCやDIの概念は紹介していませんので、Iocの予備知識がない場合は、まず関連情報を検索することをお勧めします

この記事では、AutoFacの基本的な使い方と、asp .net coreでの応用を簡単に紹介します。

オートファックの紹介

コンポーネントを登録する3つの方法

1. リフレクション

2. レディメイド・インスタンス(新規)

3. ラムダ式(オブジェクトのインスタンス化を実行する無名メソッド)。

また、quot;component" や "service" といった用語の意味については、コメントで説明します。

// Create the builder for the registered component
var builder = new ContainerBuilder();

// Register components by type ConsoleLogger Storming Service: ILogger
builder.RegisterType<ConsoleLogger>().As<ILogger>();

//Register the component ConsoleLogger by type, exposing all the services (interfaces) it implements
builder.RegisterType<ConsoleLogger>().AsImplementedInterfaces();

// register the component output according to the instance Storm service: TextWriter
var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();

//expression register component, here we are in the constructor when passing the reference - >"musection" storm service: IConfigReader
builder.Register(c =new ConfigReader("mysection")).As<IConfigReader>();

//expression to register the component, pass the reference when parsing
var service = scope.Resolve<IConfigReader>(
           new NamedParameter("section", "mysection"));     

//reflective registration of components, directly registered ConsoleLogger class (must be a specific class), if the ConsoleLogger has more than one constructor, will take the constructor with the most parameters to instantiate
builder.RegisterType<ConsoleLogger>();

//reflective registration of components, manually specify the constructor, here specify the constructor to call MyComponent (ILogger log, IConfigReader config) to register
builder.RegisterType<MyComponent>()
 .UsingConstructor(typeof(ILogger), typeof(IConfigReader));  

 // Register the static variable "Instance" in the MySingleton class, the ExternallyOwned() function specifies that it controls the life cycle of the instance, rather than automatically released by autofac
 builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();

//One component exposes two services  
builder.RegisterType<CallLogger>().As<ILogger>().As<ICallInterceptor>(); 

//Register the class ending with "Service" in the current assembly
Builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Service")) .AsImplementedInterfaces();
//Register all classes in "MyApp.Repository" assembly
builder.RegisterAssemblyTypes(GetAssembly("MyApp.Repository")).AsImplementedInterfaces();
  
// Build a container to complete the registration
var rootcontainer = builder.Build();

//You can get the IConfigReader implementation class manually in the following way
//This manual parsing method needs to get the component from the lifecycle scope to ensure that the component is eventually released
//Do not parse the component directly from the root container rootcontainer, it will probably lead to a memory leak
BeginLifetimeScope())
{
  var reader = scope.Resolve<IConfigReader>();
}

複数のコンポーネントが同じサービスを公開している場合、オートファックは最後に登録されたコンポーネントをサービスの提供元として使用します。この動作をオーバーライドするには PreserveExistingDefaults() メソッドを使用して

ライフサイクル

using(var scope = rootcontainer.BeginLifetimeScope())

上記のコードは、ライフサイクルスコープを作成します。

ライフサイクルスコープは解放可能で、スコープ内で解決されたコンポーネントは、using 内で使用するか、コンポーネントの Dispose() 関数を最後に手動で呼び出さなければなりません。

ライフサイクルが参照するクラスのライフサイクルよりも大きいクラスを参照することは避けてください。例えば、サービスのライフサイクルがsingletonで、リポジトリのライフサイクルがperrequestの場合、サービスがリポジトリを参照します。サービスはリリースされないので、結局関連するリポジトリもリリースされないでしょう ( キャプティブ依存性 )

ルートコンテナから直接コンポーネントをパースすることはできるだけ避ける必要がありますが、常に例外は存在します。実際、非シングルトン コンポーネントの場合、理論的にはプロジェクト アーキテクチャは手動で解析するのではなく、コンストラクタから注入する必要があります。手動で解析する必要があるのは、いくつかの設定ヘルパークラスなどです。

特定のコンポーネント(クラス)のライフサイクルは、以下のように分類される(この後に続く機能は、autofacに対応するものである)。

  • 依存関係ごとに1インスタンス(デフォルト) ---InstancePerDependency()
  • シングルインスタンス シングルインスタンス ----SingleInstance()
  • ライフタイム・スコープごとに1つのインスタンス ---InstancePerLifetimeScope()
  • 一致するライフサイクルスコープごとに1つのインスタンス (Instance Per Matching Lifetime Scope) ---InstancePerMatchingLifetimeScope()
  • 1 リクエストにつき 1 インスタンス (Instance Per Request) asp.net Web リクエスト ---InstancePerRequest()
  • 所有するごとに1つのインスタンス (Instance Per Owned) ---InstancePerOwned()

従来のASP.NET MVCプロジェクトでautofacを使用したことがある場合、注意すべきいくつかの相違点があります。

<ブロッククオート
  • Net Coreでは InstancePerLifetimeScope 従来の(レガシーな)Asp.netではなく InstancePerRequest を使用して、HTTP リクエストごとに依存関係の一意なインスタンスのみが作成されるようにします。 InstancePerRequest 要求レベルはもはや存在しない
  • .net CoreのWeb ApiはMvcと同じように登録される
  • {コントローラは.NETで作成されます。 net コアによって作成され、コントローラは autofac によって管理されません (コントローラのコンストラクタは例外です)。 {インスタンスペアリクエスト インスタンスペアリクエスト のライフサイクルを使用することができます。 AddControllersAsServices() 関数は、より深く知りたい人のために、チェックアウトしてください。 https://www.strathweb.com/2016/03/the-subtle-perils-of-controller-dependency-injection-in-asp-net-core-mvc

asp .net coreでのAutoFacの使用について

.net coreでautofacを使用することは比較的簡単で、従来のasp.netのWebプロジェクトと比較して多くのステップを節約することができます。

nugetパッケージの紹介です。

  • オートファック
  • Autofac.Extensions.DependencyInjectionについて

起動時のコード

  public static IContainer AutofacContainer;
    // This method gets called by the runtime. Use this method to add services to the container.
    public IServiceProvider ConfigureServices(IServiceCollection services)
    public IServiceProvider ConfigureServices(IServiceCollection services) {
        // Register the services into the IServiceCollection
        services.AddMvc();
        ContainerBuilder builder = new ContainerBuilder();
        //Populate Autofac with the services in services.
        builder.Populate(services);
        //register the new module component
        builder.RegisterModule<DefaultModuleRegister>();
        //create container.
        AutofacContainer = builder.Build();
        //use container to create AutofacServiceProvider 
        return new AutofacServiceProvider(AutofacContainer);
    }

上記のコードでは、ビルダーの RegisterModule 関数に渡す必要があります。 TModule モジュールで、autofacと呼ばれる

このモジュールの機能は、以下のように、関連するすべての登録設定を1つのクラスにまとめ、コードの保守と設定を容易にすることです。 DefaultModuleRegister のコードは

DefaultModuleRegisterです。

public class DefaultModuleRegister : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        //register the classes ending with "Ser" in the current assembly, and leak all the interfaces implemented by the class, the life cycle of PerLifetimeScope
        builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Ser")). AsImplementedInterfaces().InstancePerLifetimeScope();
        builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Repository" ;)).AsImplementedInterfaces().InstancePerLifetimeScope();
        //register all the classes in the "MyApp.
        //builder.RegisterAssemblyTypes(GetAssembly("MyApp.Repository")).AsImplementedInterfaces();
    }

	public static Assembly GetAssembly(string assemblyName)
    {
        AssemblyLoadContext.Default.LoadFromAssemblyPath(AppContext.BaseDirectory + $"{assemblyName}.dll");
        return assembly;
    }
}

Configure機能では、オプションでプログラム停止時のオートファック解除機能を追加することができます。

  public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime appLifetime)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
        //The program stops calling the function
        appLifetime.ApplicationStopped.Register(() => { AutofacContainer.Dispose(); });
    }

Controller内のコード。

  private IUserSer _user;
    private IUserSer _user2;
    public HomeController(IUserSer user, IUserSer user2)
    {
        _user = user;
        _user2 = user2;
    }
    public IActionResult Index()
    {
        using (var scope = Startup.AutofacContainer.BeginLifetimeScope())
        {
            IConfiguration config = scope.Resolve<IConfiguration>();
            IHostingEnvironment env = scope.Resolve<IHostingEnvironment>();
        }
        string name = _user.GetName();
        string name2 = _user2.GetName();
        return View();
    }

ご覧の通り、IServiceCollection のサービスに autofac を投入したので、.net core のデフォルトの注入サービス(IConfiguration、IHostingEnvironment など)を AutoFac でどこからでも解析できるようになりました。

通常のプロジェクトの使い方では AutofacContainer を共通クラスライブラリで呼び出せるようにします。

Net Core AutoFacについては、Scripting Houseの過去記事を検索していただくか、引き続き以下の関連記事をご覧ください。