コンストラクター引数を注入するのと同じ方法で依存関係をプロパティとして単純に注入するように DryIoc コンテナーを構成する方法の簡単な例を広く検索しました。
次の作業例を考えると...
コンテナ登録:
public static void Register(HttpConfiguration config)
{
var c = new Container().WithWebApi(config);
c.Register<IWidgetService, WidgetService>(Reuse.Singleton);
c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton);
}
ウィジェット サービス:
public class WidgetService : IWidgetService
{
private readonly IWidgetRepository _widgetRepository;
public WidgetService(IWidgetRepository widgetRepository)
{
_widgetRepository = widgetRepository;
}
public IList<Widget> GetWidgets()
{
return _widgetRepository.GetWidgets().ToList();
}
}
ウィジェット リポジトリ:
public class WidgetRepository : IWidgetRepository
{
private readonly IList<Widget> _widgets;
public WidgetRepository()
{
_widgets = new List<Widget>
{
new Widget {Name = "Widget 1", Cost = new decimal(1.99), Description = "The first widget"},
new Widget {Name = "Widget 2", Cost = new decimal(2.99), Description = "The second widget"}
};
}
public IEnumerable<Widget> GetWidgets()
{
return _widgets.AsEnumerable();
}
}
DryIoc が WidgetRepository をプロパティとして挿入する場合、このような WidgetService をサポートするには、構成をどのように変更する必要がありますか?
希望するウィジェット サービス:
public class WidgetService : IWidgetService
{
public IWidgetRepository WidgetRepository { get; set; }
public IList<Widget> GetWidgets()
{
return WidgetRepository.GetWidgets().ToList();
}
}
失敗した試み
これらの設定変更を試してみましたが、WidgetService でプロパティ インジェクションを有効にしても効果がないようです。
試行 1:
public static void Register(HttpConfiguration config)
{
var c = new Container().WithWebApi(config);
// Seems logical - no luck
c.InjectPropertiesAndFields(PropertiesAndFields.Auto);
c.Register<IWidgetService, WidgetService>(Reuse.Singleton);
c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton);
}
試行 2:
public static void Register(HttpConfiguration config)
{
var c = new Container(Rules.Default.With(propertiesAndFields: PropertiesAndFields.Auto))
.WithWebApi(config);
c.Register<IWidgetService, WidgetService>(Reuse.Singleton);
c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton);
}
私も上記をPropertiesAndFields.All
で試しましたが、運もありませんでした。
注: プロパティ インジェクションは推奨されるアプローチではなく、多くの理由でコンストラクタ インジェクションが推奨されることを理解しています。ただし、両方を正しく行う方法を知りたいです。
アップデート
@dadhi のアドバイスに従って、試み #2 を次のようにコンテナを初期化するように変更しました。
var c = new Container(Rules.Default.With(propertiesAndFields: PropertiesAndFields.All(withNonPublic: false,
withPrimitive: false,
withFields: false,
ifUnresolved: IfUnresolved.Throw)))
.WithWebApi(config);
しかし、私はこのエラーを受け取りました:
{
"Message" : "An error has occurred.",
"ExceptionMessage" : "An error occurred when trying to create a controller of type 'WidgetController'. Make sure that the controller has a parameterless public constructor.",
"ExceptionType" : "System.InvalidOperationException",
"StackTrace" : " at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()",
"InnerException" : {
"Message" : "An error has occurred.",
"ExceptionMessage" : "Type 'IOCContainerTest.DryIOC.Controllers.WidgetController' does not have a default constructor",
"ExceptionType" : "System.ArgumentException",
"StackTrace" : " at System.Linq.Expressions.Expression.New(Type type)\r\n at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"
}
}
DryIoc は、私が持っていなかった引数なしのコンストラクターを使用して、WidgetController を初期化しようとしているようです。ルールが に変更されて以来 PropertiesAndFields.All(...)
、DryIoc はすべての登録済みアイテムに対してプロパティ インジェクションを使用しようとしていると思います。
public class WidgetController : ApiController
{
private readonly IWidgetService _widgetService;
public WidgetController(IWidgetService widgetService)
{
_widgetService = widgetService;
}
// GET api/<controller>
public List<WidgetSummary> Get()
{
return _widgetService.GetWidgetSummaries();
}
}
プロパティ注入を使用して WidgetService (上記) を初期化しようとしましたが、コンストラクター注入を使用して WidgetController を初期化しようとしました。PropertiesAndFields.Auto
両方はできないかもしれませんが、ルール上は両方できると思っていました。WidgetController もプロパティ インジェクション用に設定するように変更しました。その後、DryIoc から例外は発生しませんが、WidgetController で WidgetService が null になります。これが更新された WidgetController です。
public class WidgetController : ApiController
{
public IWidgetService WidgetService { get; set; }
// GET api/<controller>
public List<WidgetSummary> Get()
{
return WidgetService.GetWidgetSummaries();
}
}
自動プロパティ インジェクションはまだ難しいようです。
更新 2
多くの試行錯誤 (および @dadhi からの提案) の後、WidgetController でコンストラクター注入を使用し、他のサービスを登録するときにプロパティ注入を指定することにしました。これにより、コントローラーを除いて、現在プロパティ注入を利用しているコードをコンストラクター注入に移行することができます。更新されたコンテナ登録は次のとおりです。
public static void Register(HttpConfiguration config)
{
var c = new Container(Rules.Default).WithWebApi(config);
var autoInjectProps = Made.Of(propertiesAndFields: PropertiesAndFields.Auto);
c.Register<IWidgetService, WidgetService>(Reuse.Singleton, autoInjectProps);
c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton, autoInjectProps);
}
コントローラーと他のサービスの両方で機能するゴールデン設定を最終的に見つけたいと思っていますが、動作が異なるようです。しかし、これは今のところ十分に実行可能な解決策です。
アップデート 3
プロパティ注入としてすべて (WidgetController を含む) を配線する別の試みで、@dadhi の提案に従ってコンテナー構成を次のように更新しました。
public static void Register(HttpConfiguration config)
{
var c = new Container(Rules.Default.With(propertiesAndFields: PropertiesAndFields.All(withNonPublic: false, withPrimitive: false, withFields: false, ifUnresolved: IfUnresolved.Throw)))
.WithWebApi(config, throwIfUnresolved: type => type.IsController());
c.Register<IWidgetService, WidgetService>(Reuse.Singleton);
c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton);
}
これは少なくとも、ある程度意味のある例外を生成するようであり、使用するコンテナーをセットアップするときにコントローラーが異なる方法で処理される理由を説明する可能性がありますPropertiesAndFields.All(..)
。
{
"Message" : "An error has occurred.",
"ExceptionMessage" : "An error occurred when trying to create a controller of type 'WidgetController'. Make sure that the controller has a parameterless public constructor.",
"ExceptionType" : "System.InvalidOperationException",
"StackTrace" : " at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()",
"InnerException" : {
"Message" : "An error has occurred.",
"ExceptionMessage" : "Unable to resolve HttpConfiguration as property \"Configuration\"\r\n in IOCContainerTest.DryIOC.Controllers.WidgetController.\r\nWhere no service registrations found\r\n and number of Rules.FallbackContainers: 0\r\n and number of Rules.UnknownServiceResolvers: 0",
"ExceptionType" : "DryIoc.ContainerException",
"StackTrace" : " at DryIoc.Throw.It(Int32 error, Object arg0, Object arg1, Object arg2, Object arg3)\r\n at DryIoc.Container.ThrowUnableToResolve(Request request)\r\n at DryIoc.Container.DryIoc.IContainer.ResolveFactory(Request request)\r\n at DryIoc.ReflectionFactory.InitPropertiesAndFields(NewExpression newServiceExpr, Request request)\r\n at DryIoc.ReflectionFactory.CreateExpressionOrDefault(Request request)\r\n at DryIoc.Factory.GetExpressionOrDefault(Request request)\r\n at DryIoc.Factory.GetDelegateOrDefault(Request request)\r\n at DryIoc.Container.ResolveAndCacheDefaultDelegate(Type serviceType, Boolean ifUnresolvedReturnDefault, IScope scope)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"
}
}