在应用程序ASPNET WebApi中,我们使用SimpleInjector作为IOC和EF6 Code First,问题是对对象进行更新,EF没有意识到它是相同的并尝试再次创建它。
从浏览器运行它时运行正常,但是当我们从集成测试中运行它时,需要定义混合上下文,因为我们在IoC中使用的LifeStyle是请求,测试是Scope。
这就是我们定义混合环境的方式。
Container.Options.DefaultScopedLifestyle = Lifestyle.CreateHybrid(
() => Container.GetCurrentExecutionContextScope() != null,
new ExecutionContextScopeLifestyle(),
new WebApiRequestLifestyle());
IocConfig.RegisterIoc(new HttpConfiguration(), Container);
然后我们必须运行[AssemblyInitialize]中定义的测试,该测试负责在数据库中创建初始测试数据。
对于每个单独的测试
[TestMethod, TestCategory("Nightly")][Ignore]
public void SearchLogsOrdersCorrectlyTest()
{
using (GlobalInit.Container.BeginExecutionContextScope())
{
var subscriptionService = GlobalInit.Container.GetInstance<ISubscriptionService>();
var subscriptionBaseModel = new SubscriptionBaseModel
{
AuditInformation = new AuditModel
{
UserName = "User Name",
UserEmail = "username@domain.com",
UserHostname = "manage.domain.com",
UserIpAddress = "11.11.11.11"
},
Company = new Company { Id = 1 },
Customers = new List<Customer>
{
new Customer
{
Email = "email@dom.com",
FullName = "Full Name"
}
},
SubscriptionData = new SubscriptionPlanUpdateModel
{
AvailableLicenses = 1,
ContractId = "1111",
Url = "http://google.com ",
SubscriptionComments = ".",
ExpirationDate = DateTime.Today.AddYears(1)
}
};
var subscriptionCreationModel = mappingEngine.Map<SubscriptionCreationModel>(subscriptionBaseModel);
subscriptionCreationModel.Validate();
Assert.IsTrue(subscriptionCreationModel.IsModelValid);
var newSubscription = SubscriptionFactory.GetSubscription(subscriptionCreationModel);
var savedSubscription = newSubscription.Save();
Assert.IsNotNull(savedSubscription);
}
}
基本上,测试的作用是使用基本数据模型,从数据库中添加数据然后保存。问题是它试图将对象作为新对象插入数据库而不是更新它们。这似乎是因为它理解它们来自不同的背景。
如果我们不使用GlobalInit.Container.BeginExecutionContextScope()
,那么我们会得到一个例外情况,即范围不同,并且无法在网络环境之外启动WebRequestScope。
这里的特点是所有这一切都在以下内容中完成:
using (GlobalInit.Container.BeginExecutionContextScope())
因此,如果你知道我们在这里失败了,那将会很受欢迎。
答案 0 :(得分:1)
您根本不需要定义混合生活方式。这只是为每个应用程序提供自己特定的生活方式。
// In the unit tests:
var container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
IocConfig.RegisterIoc(new HttpConfiguration(), container);
// In web api:
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebApiRequestScope();
IocConfig.RegisterIoc(new HttpConfiguration(), container);
如果可以,请通过处理范围来防止污染每个测试。你应该做的是将范围的开始和停止移动到基类到构造函数和Dispose
方法。这样它将在每次测试之前和之后运行。例如:
public abstract class IntegrationTestBase : IDisposable
{
private Scope scope;
public IntegrationTestBase() {
this.scope = Container.BeginExecutionContextScope();
}
public void Dispose() {
this.scope.Dispose();
}
}
这个btw,也是添加交易处理的好地方:
public abstract class IntegrationTestBase : IDisposable
{
private Scope scope;
private TransactionScope transactionScope;
public IntegrationTestBase() {
this.scope = Container.BeginExecutionContextScope();
this.transactionScope = new TransactionScope();
}
public void Dispose() {
this.transactionScope.Dispose();
this.scope.Dispose();
}
}
这确保了每个测试都是孤立运行的,因为我们不调用Complete
,所以在每次测试结束时回滚事务。您唯一需要做的就是从这个基类继承您的测试类。
关于你为什么有多个范围的问题:我不知道;你没有提供足够的信息来看到这一点。您可能在执行该测试期间创建了第二个范围。