我想使用Injection构造函数简化类型注册。国际奥委会通常应自行解决类型。有些类型无法自动自行解析,因为它们的构造函数需要一些Named参数。他们需要自定义注册。
例如:
public DatabaseLayer(string connectionString,
string userName,
ILogger loger,
TypeMapper mapper,
...some other dependencies)
类型注册:
container.RegisterType<DatabaseLayer>(
new InjectionConstructor(
new ResolvedParameter<string>("connectionString"),
new ResolvedParameter<string>("userName"),
new ResolvedParameter<ILoger>("file")
typeof(TypeMapper),
typeof(..),
...));
如您所见,如果您在注册中需要任何命名解析,则需要写下数十个未命名参数或应用程序在运行时崩溃。更糟糕的是,每次更改构造函数参数时,都需要更正注册。在Programm在运行时崩溃之前,您什么都不会注意到。
因此,我正在寻找扩展或一种配置Unity的方法,该方法使我可以跳过默认(无名)参数注册,以减少可能的错误并保持引导程序尽可能小。
修改 很好找到我在寻找什么。 https://outlawtrail.wordpress.com/2012/08/02/fun-with-constructor-arguments-part-1-pick-choose/
智能构造函数似乎是解决方案
答案 0 :(得分:1)
一种选择是使用InjectionFactory
而不是InjectionConstructor
。
假设此模型:
public class DataBaseLayer
{
private readonly string _connectionString;
private readonly string _userName;
private readonly ILogger _logger;
private readonly ITypeMapper _mapper;
public DataBaseLayer(
string connectionString,
string userName,
ILogger logger,
ITypeMapper mapper)
{
_connectionString = connectionString;
_userName = userName;
_logger = logger;
_mapper = mapper;
}
}
public interface ITypeMapper
{
}
public class TypeMapper : ITypeMapper
{
}
public interface ILogger
{
}
public class Logger : ILogger
{
}
并假设Username
和ConnectionString
存储在配置文件中(您未指定这些参数的来源),我将创建一个Bootstrapper
类,例如以下:
public static class Bootstrapper
{
// assuming Username is a configuration setting stored in the config file
private static string Username => ConfigurationManager.AppSettings["username"];
// assuming ConnectionString is a connection string stored in the config file
private static string ConnectionString => ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;
public static IUnityContainer Setup()
{
IUnityContainer container = new UnityContainer();
container.RegisterType<ILogger, Logger>();
container.RegisterType<ITypeMapper, TypeMapper>();
container.RegisterType<DataBaseLayer>(new InjectionFactory(CreateDataBaseLayer));
return container;
}
private static DataBaseLayer CreateDataBaseLayer(IUnityContainer container)
{
ILogger logger = container.Resolve<ILogger>();
ITypeMapper mapper = container.Resolve<ITypeMapper>();
return new DataBaseLayer(ConnectionString, Username, logger, mapper);
}
}
使用代码:
IUnityContainer container = Bootstrapper.Setup();
DataBaseLayer dbLayer = container.Resolve<DataBaseLayer>();
答案 1 :(得分:0)
好的,还有更多选项可帮助您改善代码。假设使用以下模型:
public class DataBaseLayer
{
private readonly string _connectionString;
private readonly string _userName;
private readonly ILogger _logger;
private readonly ITypeMapper _mapper;
public DataBaseLayer(
string connectionString,
string userName,
ILogger logger,
ITypeMapper mapper)
{
_connectionString = connectionString;
_userName = userName;
_logger = logger;
_mapper = mapper;
}
}
public interface ITypeMapper
{
}
public class TypeMapper : ITypeMapper
{
}
public interface ILogger
{
}
public class ConsoleLogger : ILogger
{
}
public class FileLogger : ILogger
{
}
第一件事是尝试减少代码中魔术字符串的数量,例如"connectionString"
和"userName"
:
container.RegisterInstance(typeof(string), "userName", "my username");
container.RegisterInstance(typeof(string), "connectionString", "my connection string");
因此,与其在整个代码中重复这样的事情……
new InjectionConstructor(
new ResolvedParameter<string>("connectionString"),
new ResolvedParameter<string>("userName"),
/* .... */
);
您可以创建自ResolveParameter
派生的参数类:
public class UsernameParameter : ResolvedParameter<string>
{
public static UsernameParameter Instance => new UsernameParameter();
public static string ParameterName => "userName";
private UsernameParameter() : base(ParameterName)
{
}
}
public class ConnectionStringParameter : ResolvedParameter<string>
{
public static ConnectionStringParameter Instance => new ConnectionStringParameter();
public static string ParameterName => "connectionString";
private ConnectionStringParameter() : base(ParameterName)
{
}
}
为什么要打扰?这样,您不必重复字符串"connectionString"
和"userName"
,并且如果需要更改它们,您要做的就是编辑相应类中的ParameterName
属性。
使用ConnectionStringParameter
和UsernameParameter
注册字符串:
container.RegisterInstance(typeof(string), UsernameParameter.ParameterName, "my username");
container.RegisterInstance(typeof(string), ConnectionStringParameter.ParameterName, "my connection string");
使用参数:
new InjectionConstructor(
ConnectionStringParameter.Instance,
UsernameParameter.Instance,
/* .... */
);
例如,假设您要注册DataBaseLayer的不同实例。这些实例之间的唯一区别是使用的记录器类型。注册ILogger
的实例:
container.RegisterType<ILogger, FileLogger>("file");
container.RegisterType<ILogger, ConsoleLogger>("console");
使用扩展方法注册DataBaseLayer
的实例:
container.RegisterDataBaseLayer(instanceName: "fileDatabaseLayer", loggerType: "file");
container.RegisterDataBaseLayer(instanceName: "consoleDatabaseLayer", loggerType: "console");
扩展RegisterDataBaseLayer
的方法:
public static class UnityContainerExtensions
{
public static IUnityContainer RegisterDataBaseLayer(
this IUnityContainer container,
string instanceName,
string loggerType)
{
container.RegisterType<DataBaseLayer>(
instanceName,
new InjectionConstructor(
ConnectionStringParameter.Instance,
UsernameParameter.Instance,
new ResolvedParameter<ILogger>(loggerType),
new ResolvedParameter<ITypeMapper>()
)
);
return container;
}
}
代替....
public class FooService
{
public FooService(string userName, string password)
{
// ...
}
}
要做:
public class FooService
{
public FooService(Credentials credentials)
{
// ...
}
}
public class Credentials
{
public string Username { get; set; }
public string Password { get; set; }
}
使用IoC容器注册类型时,这将使您的生活更轻松,并减少Bootstrapper代码。
这非常重要,可以为您省去一些麻烦。如果您认为代码易碎,并且在/更改类时(例如,更改或添加新的构造函数)可能会崩溃,则应为注册的每种类型和/或命名实例添加一些测试。
您至少应该检查容器是否能够解析特定实例而不会引发异常。