やったこと
ASP.NET MVCでロジックや共通部品をサービスとしてDIするときに、基本的にはサービスと実装が同名で1対1対応するので、それらをいちいちRegisterしなくていいようにした。ドキュメントに載ってはいるが一応メモ。
バージョン
.NET Framework 4.8
Microsoft.AspNet.Mvc 5.2.7
NuGet Gallery | SimpleInjector.Integration.Web.Mvc 5.0.0 とその依存関係
コード
Global.asax
using DiAndAop.Services;
using SimpleInjector;
using SimpleInjector.Integration.Web;
using SimpleInjector.Integration.Web.Mvc;
using System;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace DiAndAop
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
RegisterDiContainer();
// ...
}
/// <summary>
/// DIコンテナを設定する。
/// </summary>
private static void RegisterDiContainer()
{
// Create the container as usual.
Container container = new Container();
// デフォルトの生存期間を設定
WebRequestLifestyle webRequestLifestyle = new WebRequestLifestyle();
container.Options.DefaultScopedLifestyle = webRequestLifestyle;
// 一括登録しない実装を指定
Type[] batchRegExcludeType = { typeof(Hello) };
BatchReg(container, batchRegExcludeType, webRequestLifestyle);
// 一括登録以外のサービスを登録
container.Register<IHello, Hello>(Lifestyle.Scoped);
// This is an extension method from the integration package.
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
/// <summary>
/// DIコンテナにサービスを一括登録する。実装とサービスが(頭の"I"を除いて)同名のクラスを対象とする。
/// サービスと同名のもの以外に実装が存在する場合も、同名の実装が対象となる。
/// </summary>
/// <param name="container">DIコンテナ</param>
/// <param name="batchRegExcludeType">除外する型</param>
/// <param name="lifestyle">生成・破棄のオプション</param>
private static void BatchReg(Container container, Type[] batchRegExcludeType, Lifestyle lifestyle)
{
Lifestyle _lifestyle = lifestyle is null ? Lifestyle.Transient : lifestyle;
// MvcApplication の部分は、サービスとクライアントがいるアセンブリを指定できればなんでもよい
Assembly assembly = typeof(MvcApplication).Assembly;
// 一括登録対象を抽出
var registrations = assembly.GetExportedTypes()
.AsParallel()
.Where(type => type.Namespace.StartsWith("DiAndAop.Services"))
.Where(impl => !batchRegExcludeType.Contains(impl))
.Select(impl => impl.GetInterfaces()
.Where(service => service.Name == "I" + impl.Name)
.Select(service => (impl, service))
.SingleOrDefault())
.Where(pair => !pair.Equals(default));
// 一括登録実施
foreach (var reg in registrations)
{
container.Register(reg.service, reg.impl, _lifestyle);
}
}
}
}
- 例では
DiAndAop.Services
名前空間にサービスがある想定となっている(Where
の中)ので、実態に合わせて書き換えること。 - 気分で
AsParallel
を使っており、対象が少ないとかえって遅いかもしれない。 - サービスと同名だが他の実装を使いたい場合や、他とは異なる生存期間を設定したい場合など、一括登録の対象から除外したい場合は
batchRegExcludeType
に型を羅列すればいい。
リンク
-
ASP.NET MVC Integration Guide — Simple Injector 5 documentation
MVC用のおまじないが必要のようでこの説明に従った。 -
Advanced Scenarios — Simple Injector 5 documentation
一括登録の説明。難しいAPI用意するよりLINQでやったほうが読み書きしやすいし柔軟じゃん、とのこと。たしかに。