如何在 StructureMap 中动态创建对象的实例?
How to create an Instance of an Object in StructureMap Dynamically?
我有一个名为 "ImportRunner" 的摘要 Class。
我有一个名为 "ScorecardRunner" 的 class 实现。
如何在运行时获取 class 的实例,例如 ScorecardRunner,当我从 XML 文件中将对象类型作为字符串拉回时,它可以是任何实现ImportRunner?
我现在的代码如下
var importer = container.GetInstance<ImportRunner>();
当我尝试执行如下操作时出现编译错误。
var importer = container.GetInstance<Type.GetType("Importer.ScorecardRunner")>();
Operator '<' cannot be applied to operands of type 'method group' and
'Type'
谢谢,汤姆
与其分散基于运行时值创建实例的逻辑并将其硬塞到 StructureMap 的注册表中,更好的方法是简单地创建一个负责确定正确的运行器实例并将其注入的工厂。
例如:
public class XmlReader
{
public bool IsScoreCard { get; set; }
}
public abstract class ImportRunner
{
}
public class ScorecardRunner : ImportRunner
{
}
public class DefaultRunner : ImportRunner
{
}
public class RunnerFactory
{
private readonly XmlReader _reader;
public RunnerFactory(XmlReader reader)
{
_reader = reader;
}
public ImportRunner Resolve()
{
if (_reader.IsScoreCard)
return new ScorecardRunner();
return new DefaultRunner();
}
}
然后在您的注册表中像这样配置它:
this.For<ImportRunner>().Use(ctx => ctx.GetInstance<RunnerFactory>().Resolve());
对不起,我不同意答案的笼统说法。
如果您使用 IoC,则不需要创建具体工厂...这就是依赖注入的全部要点。
考虑这个小例子...
- UnitOfWork 将由 StructureMap 实例化
- BankAccountApplication 可以并且也应该由 StructureMap 实例化
Process.BankAccounts 库 - 样本 Class:
请注意 UnitOfWork class 是如何被应用程序 classes 属性.
引用的
public class BankAccountApplication : IAccountApplication
{
#region <Fields & Constants>
private string StartingBalanceInvalidFormat = "A Starting Balance of {0} is invalid.";
private string AnnualPercentageRateInvalidFormat = "The Annual Percentage Rate of {0} is invalid.";
#endregion
#region <Properties>
[SetterProperty]
public IDemoDbUnitOfWork UnitOfWork { get; set; }
#endregion
#region <Methods>
public BankAccount CreateNew(AccountType bankAccountType, string ownerFullName, decimal startingBalance, decimal annualPercentageRate, string executedBy)
{
TraceHandler.TraceIn(TraceLevel.Info);
if (string.IsNullOrWhiteSpace(ownerFullName))
throw new ArgumentNullException("Owner Full Name");
if (startingBalance < 0.0M)
throw new ArgumentException(string.Format(StartingBalanceInvalidFormat, startingBalance));
if (annualPercentageRate <= 0.0M)
throw new ArgumentException(string.Format(AnnualPercentageRateInvalidFormat, annualPercentageRate));
var account = new BankAccount();
try
{
BankAccountType accountType = GetAccountType(bankAccountType);
account.AnnualPercentageRate = annualPercentageRate;
account.Balance = startingBalance;
account.BankAccountTypeId = accountType.BankAccountTypeId;
account.OwnerFullName = ownerFullName;
account.ExecutedByName = executedBy;
account.ExecutedDatetime = DateTime.UtcNow;
UnitOfWork.BankAccounts.Add(account);
UnitOfWork.SaveChanges();
}
catch (Exception ex)
{
TraceHandler.TraceError(ex);
}
finally
{
TraceHandler.TraceOut();
}
return account;
}
public IEnumerable<BankAccount> FindOverdrafts(IAccountAlgorithm overdraftAlgorithm)
{
TraceHandler.TraceIn(TraceLevel.Info);
var accounts = new List<BankAccount>();
try
{
var entities = UnitOfWork.BankAccounts.GetAll().ToList();
entities.ForEach(e => {
IAlgorithmResult calculation = overdraftAlgorithm.Calculate(e.Balance);
if (calculation.Result)
accounts.Add(e);
});
}
catch (Exception ex)
{
TraceHandler.TraceError(ex);
}
finally
{
TraceHandler.TraceOut();
}
return accounts.AsEnumerable();
}
private BankAccountType GetAccountType(AccountType bankAccountType)
{
var name = bankAccountType.ToStringValue();
// In this case I am going to assume all accounts are properly mapped -> First()
return UnitOfWork.BankAccountTypes.GetAll().Where(a => a.BankAccountTypeName == name).First();
}
#endregion
}
Process.BankAccounts 库 - ContainerRegistry :
此 ContainerRegistry 扫描程序集和 "sets-up" 您的根接口,然后使用您的命令获取更多 "explicit" 说明。
public class ContainerRegistry : Registry
{
#region <Constructors>
public ContainerRegistry()
{
Scan(
scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.LookForRegistries();
scan.AssembliesFromApplicationBaseDirectory(f => f.FullName.StartsWith("Bushido.Common", true, null));
scan.AssembliesFromApplicationBaseDirectory(f => f.FullName.StartsWith("Bushido.Process", true, null));
scan.AddAllTypesOf(typeof(IAccountApplication));
scan.AddAllTypesOf(typeof(IAccountAlgorithm));
scan.SingleImplementationsOfInterface();
});
ForSingletonOf(typeof(IDbContext)).Use(typeof(DemoDbContext));
For(typeof(IDemoDbUnitOfWork)).Use(typeof(DemoDbUnitOfWork));
For(typeof(IRepository<>)).Use(typeof(GenericRepository<>));
}
#endregion
}
单元测试库 - 生成器:
当然,在单元测试中,您必须模拟第 3 方对象。但是你可以而且应该使用 IoC 来做到这一点。
public partial class Builder
{
#region <Methods>
public T CreateInstance<T>(IDemoDbUnitOfWork unitOfWork, bool byFullName = false)
{
if (unitOfWork == null)
throw new ArgumentNullException("UnitOfWork");
// Here, I am passing-in a MOCK of the UnitOfWork & "shimming" it into "T" via IoC
var container = IoC.Initialize();
container.Inject(typeof(IDemoDbUnitOfWork), unitOfWork);
return container.GetInstance<T>();
}
public Mock<IDemoDbUnitOfWork> CreateMockUnitOfWork()
{
var unitOfWork = new Mock<IDemoDbUnitOfWork>();
// DBO Tables
var bankAccountRepository = BankAccountRepositoryBuilder.CreateMock();
var bankAccountTypeRepository = BankAccountTypeRepositoryBuilder.CreateMock();
unitOfWork.SetupAllProperties();
// DBO Tables
unitOfWork.SetupGet(x => x.BankAccounts).Returns(bankAccountRepository.Object);
unitOfWork.SetupGet(x => x.BankAccountTypes).Returns(bankAccountTypeRepository.Object);
return unitOfWork;
}
#endregion
}
UnitTest 库 - 测试:
现在,通过生成器 class,您可以实例化您的 classes...因此,您可以很好地清理单元测试。
public void BankAccountApplication_CreateNew_Invalid_StartingBalance()
{
// -----
// ARRANGE
var unitOfWork = Builder.CreateMockUnitOfWork();
var application = Builder.CreateInstance<BankAccountApplication>(unitOfWork);
// -----
// ACT
var account = application.CreateNew(AccountType.Checking, "Scrooge McDuck", -100.00M, 3.00M, Builder.ExecutedBy);
// -----
// ASSERT
...put your asserts here
}
我有一个名为 "ImportRunner" 的摘要 Class。
我有一个名为 "ScorecardRunner" 的 class 实现。
如何在运行时获取 class 的实例,例如 ScorecardRunner,当我从 XML 文件中将对象类型作为字符串拉回时,它可以是任何实现ImportRunner?
我现在的代码如下
var importer = container.GetInstance<ImportRunner>();
当我尝试执行如下操作时出现编译错误。
var importer = container.GetInstance<Type.GetType("Importer.ScorecardRunner")>();
Operator '<' cannot be applied to operands of type 'method group' and 'Type'
谢谢,汤姆
与其分散基于运行时值创建实例的逻辑并将其硬塞到 StructureMap 的注册表中,更好的方法是简单地创建一个负责确定正确的运行器实例并将其注入的工厂。
例如:
public class XmlReader
{
public bool IsScoreCard { get; set; }
}
public abstract class ImportRunner
{
}
public class ScorecardRunner : ImportRunner
{
}
public class DefaultRunner : ImportRunner
{
}
public class RunnerFactory
{
private readonly XmlReader _reader;
public RunnerFactory(XmlReader reader)
{
_reader = reader;
}
public ImportRunner Resolve()
{
if (_reader.IsScoreCard)
return new ScorecardRunner();
return new DefaultRunner();
}
}
然后在您的注册表中像这样配置它:
this.For<ImportRunner>().Use(ctx => ctx.GetInstance<RunnerFactory>().Resolve());
对不起,我不同意答案的笼统说法。
如果您使用 IoC,则不需要创建具体工厂...这就是依赖注入的全部要点。
考虑这个小例子...
- UnitOfWork 将由 StructureMap 实例化
- BankAccountApplication 可以并且也应该由 StructureMap 实例化
Process.BankAccounts 库 - 样本 Class:
请注意 UnitOfWork class 是如何被应用程序 classes 属性.
public class BankAccountApplication : IAccountApplication
{
#region <Fields & Constants>
private string StartingBalanceInvalidFormat = "A Starting Balance of {0} is invalid.";
private string AnnualPercentageRateInvalidFormat = "The Annual Percentage Rate of {0} is invalid.";
#endregion
#region <Properties>
[SetterProperty]
public IDemoDbUnitOfWork UnitOfWork { get; set; }
#endregion
#region <Methods>
public BankAccount CreateNew(AccountType bankAccountType, string ownerFullName, decimal startingBalance, decimal annualPercentageRate, string executedBy)
{
TraceHandler.TraceIn(TraceLevel.Info);
if (string.IsNullOrWhiteSpace(ownerFullName))
throw new ArgumentNullException("Owner Full Name");
if (startingBalance < 0.0M)
throw new ArgumentException(string.Format(StartingBalanceInvalidFormat, startingBalance));
if (annualPercentageRate <= 0.0M)
throw new ArgumentException(string.Format(AnnualPercentageRateInvalidFormat, annualPercentageRate));
var account = new BankAccount();
try
{
BankAccountType accountType = GetAccountType(bankAccountType);
account.AnnualPercentageRate = annualPercentageRate;
account.Balance = startingBalance;
account.BankAccountTypeId = accountType.BankAccountTypeId;
account.OwnerFullName = ownerFullName;
account.ExecutedByName = executedBy;
account.ExecutedDatetime = DateTime.UtcNow;
UnitOfWork.BankAccounts.Add(account);
UnitOfWork.SaveChanges();
}
catch (Exception ex)
{
TraceHandler.TraceError(ex);
}
finally
{
TraceHandler.TraceOut();
}
return account;
}
public IEnumerable<BankAccount> FindOverdrafts(IAccountAlgorithm overdraftAlgorithm)
{
TraceHandler.TraceIn(TraceLevel.Info);
var accounts = new List<BankAccount>();
try
{
var entities = UnitOfWork.BankAccounts.GetAll().ToList();
entities.ForEach(e => {
IAlgorithmResult calculation = overdraftAlgorithm.Calculate(e.Balance);
if (calculation.Result)
accounts.Add(e);
});
}
catch (Exception ex)
{
TraceHandler.TraceError(ex);
}
finally
{
TraceHandler.TraceOut();
}
return accounts.AsEnumerable();
}
private BankAccountType GetAccountType(AccountType bankAccountType)
{
var name = bankAccountType.ToStringValue();
// In this case I am going to assume all accounts are properly mapped -> First()
return UnitOfWork.BankAccountTypes.GetAll().Where(a => a.BankAccountTypeName == name).First();
}
#endregion
}
Process.BankAccounts 库 - ContainerRegistry :
此 ContainerRegistry 扫描程序集和 "sets-up" 您的根接口,然后使用您的命令获取更多 "explicit" 说明。
public class ContainerRegistry : Registry
{
#region <Constructors>
public ContainerRegistry()
{
Scan(
scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.LookForRegistries();
scan.AssembliesFromApplicationBaseDirectory(f => f.FullName.StartsWith("Bushido.Common", true, null));
scan.AssembliesFromApplicationBaseDirectory(f => f.FullName.StartsWith("Bushido.Process", true, null));
scan.AddAllTypesOf(typeof(IAccountApplication));
scan.AddAllTypesOf(typeof(IAccountAlgorithm));
scan.SingleImplementationsOfInterface();
});
ForSingletonOf(typeof(IDbContext)).Use(typeof(DemoDbContext));
For(typeof(IDemoDbUnitOfWork)).Use(typeof(DemoDbUnitOfWork));
For(typeof(IRepository<>)).Use(typeof(GenericRepository<>));
}
#endregion
}
单元测试库 - 生成器:
当然,在单元测试中,您必须模拟第 3 方对象。但是你可以而且应该使用 IoC 来做到这一点。
public partial class Builder
{
#region <Methods>
public T CreateInstance<T>(IDemoDbUnitOfWork unitOfWork, bool byFullName = false)
{
if (unitOfWork == null)
throw new ArgumentNullException("UnitOfWork");
// Here, I am passing-in a MOCK of the UnitOfWork & "shimming" it into "T" via IoC
var container = IoC.Initialize();
container.Inject(typeof(IDemoDbUnitOfWork), unitOfWork);
return container.GetInstance<T>();
}
public Mock<IDemoDbUnitOfWork> CreateMockUnitOfWork()
{
var unitOfWork = new Mock<IDemoDbUnitOfWork>();
// DBO Tables
var bankAccountRepository = BankAccountRepositoryBuilder.CreateMock();
var bankAccountTypeRepository = BankAccountTypeRepositoryBuilder.CreateMock();
unitOfWork.SetupAllProperties();
// DBO Tables
unitOfWork.SetupGet(x => x.BankAccounts).Returns(bankAccountRepository.Object);
unitOfWork.SetupGet(x => x.BankAccountTypes).Returns(bankAccountTypeRepository.Object);
return unitOfWork;
}
#endregion
}
UnitTest 库 - 测试:
现在,通过生成器 class,您可以实例化您的 classes...因此,您可以很好地清理单元测试。
public void BankAccountApplication_CreateNew_Invalid_StartingBalance()
{
// -----
// ARRANGE
var unitOfWork = Builder.CreateMockUnitOfWork();
var application = Builder.CreateInstance<BankAccountApplication>(unitOfWork);
// -----
// ACT
var account = application.CreateNew(AccountType.Checking, "Scrooge McDuck", -100.00M, 3.00M, Builder.ExecutedBy);
// -----
// ASSERT
...put your asserts here
}