Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"xunit"
],
"dotnet-test-explorer.testProjectPath": "test/**/*Test.csproj",
"dotnet.defaultSolution": "Autofac.WebApi.Owin.sln",
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"*.resx": "$(capture).*.resx, $(capture).designer.cs, $(capture).designer.vb"
Expand Down
2 changes: 2 additions & 0 deletions build/Analyzers.ruleset
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<Rule Id="CA1032" Action="None" />
<!-- Change names to avoid reserved word overlaps (e.g., Delegate, GetType, etc.) - too many of these in the public API, we'd break if we fixed it. -->
<Rule Id="CA1716" Action="None" />
<!-- Do not create tasks without passing a TaskScheduler - we're working with Task, make sure we do it right. -->
<Rule Id="CA2008" Action="Warning" />
<!-- Implement serialization constructors - false positive when building .NET Core -->
<Rule Id="CA2229" Action="None" />
<!-- Mark ISerializable types with SerializableAttribute - false positive when building .NET Core -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
<PackageReference Include="Autofac.Owin" Version="7.1.0" />
<PackageReference Include="Autofac.WebApi2" Version="6.1.1" />
<PackageReference Include="Microsoft.AspNet.WebApi.Owin" Version="5.2.9" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="7.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" Condition="Exists('$(MSBuildThisFileDirectory)../../.git')">
<PrivateAssets>All</PrivateAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public static class AutofacWebApiAppBuilderExtensions
/// <exception cref="ArgumentNullException">
/// Thrown if <paramref name="app" /> or <paramref name="configuration" /> is <see langword="null" />.
/// </exception>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The handler created must exist for the entire application lifetime.")]
public static IAppBuilder UseAutofacWebApi(this IAppBuilder app, HttpConfiguration configuration)
{
if (app == null)
Expand Down
23 changes: 15 additions & 8 deletions src/Autofac.Integration.WebApi.Owin/DependencyScopeHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Autofac Project. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System.Diagnostics.CodeAnalysis;
using System.Security;
using System.Web.Http.Hosting;
using Autofac.Integration.Owin;
Expand Down Expand Up @@ -42,13 +43,19 @@ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage reques
var dependencyScope = new AutofacWebApiDependencyScope(lifetimeScope);
request.Properties[HttpPropertyKeys.DependencyScope] = dependencyScope;

try
{
return base.SendAsync(request, cancellationToken);
}
finally
{
request.Properties.Remove(HttpPropertyKeys.DependencyScope);
}
// Using .ContinueWith instead of try/finally because you can't
// have async/await in [SecurityCritical] or [SecuritySafeCritical]
// marked code.
return base
.SendAsync(request, cancellationToken)
.ContinueWith(
task =>
{
request.Properties.Remove(HttpPropertyKeys.DependencyScope);
return task.Result;
},
cancellationToken,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Current);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="7.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public async void AddsAutofacDependencyScopeToHttpRequestMessage()
}

[Fact]
public async void RemoveAutofacDependencyScopeFromHttpRequestMessageAfterLeaving()
public void RemoveAutofacDependencyScopeAfterTaskExecutes_Task()
{
var request = new HttpRequestMessage();
var context = new OwinContext();
Expand All @@ -56,22 +56,115 @@ public async void RemoveAutofacDependencyScopeFromHttpRequestMessageAfterLeaving
var container = new ContainerBuilder().Build();
context.Set(Constants.OwinLifetimeScopeKey, container);

var fakeHandler = new FakeInnerHandler(_ => new HttpResponseMessage(HttpStatusCode.OK));
var flag = new AutoResetEvent(false);
var fakeHandler = new FakeInnerHandler(_ =>
{
return Task.Factory.StartNew(() =>
{
flag.WaitOne();
Assert.Contains(HttpPropertyKeys.DependencyScope, request.Properties);
return new HttpResponseMessage(HttpStatusCode.OK);
});
});
var handler = new DependencyScopeHandler { InnerHandler = fakeHandler };
var invoker = new HttpMessageInvoker(handler);
await invoker.SendAsync(request, CancellationToken.None);
var task = invoker.SendAsync(request, CancellationToken.None);

Assert.Contains(HttpPropertyKeys.DependencyScope, request.Properties);
flag.Set();
task.GetAwaiter().GetResult();
Assert.DoesNotContain(HttpPropertyKeys.DependencyScope, request.Properties);
}

[Fact]
public async Task RemoveAutofacDependencyScopeAfterTaskExecutes_AsyncAwait()
{
var request = new HttpRequestMessage();
var context = new OwinContext();
request.Properties.Add("MS_OwinContext", context);

var container = new ContainerBuilder().Build();
context.Set(Constants.OwinLifetimeScopeKey, container);

var flag = new AutoResetEvent(false);
var fakeHandler = new FakeInnerHandler(async _ => await Task.Factory.StartNew(() =>
{
flag.WaitOne();
Assert.Contains(HttpPropertyKeys.DependencyScope, request.Properties);
return new HttpResponseMessage(HttpStatusCode.OK);
}));

var handler = new DependencyScopeHandler { InnerHandler = fakeHandler };
var invoker = new HttpMessageInvoker(handler);
var task = invoker.SendAsync(request, CancellationToken.None);

Assert.Contains(HttpPropertyKeys.DependencyScope, request.Properties);
flag.Set();
await task;
Assert.DoesNotContain(HttpPropertyKeys.DependencyScope, request.Properties);
}

[Fact]
public void RemoveAutofacDependencyScopeEvenIfTaskThrows_Task()
{
var request = new HttpRequestMessage();
var context = new OwinContext();
request.Properties.Add("MS_OwinContext", context);

var container = new ContainerBuilder().Build();
context.Set(Constants.OwinLifetimeScopeKey, container);

var fakeHandler = new FakeInnerHandler(_ =>
{
return Task.Factory.StartNew<HttpResponseMessage>(() =>
{
throw new DivideByZeroException();
});
});
var handler = new DependencyScopeHandler { InnerHandler = fakeHandler };
var invoker = new HttpMessageInvoker(handler);
var task = invoker.SendAsync(request, CancellationToken.None);

Assert.Contains(HttpPropertyKeys.DependencyScope, request.Properties);
Assert.Throws<AggregateException>(() => task.GetAwaiter().GetResult());
Assert.DoesNotContain(HttpPropertyKeys.DependencyScope, request.Properties);
}

[Fact]
public async Task RemoveAutofacDependencyScopeEvenIfTaskThrows_AsyncAwait()
{
var request = new HttpRequestMessage();
var context = new OwinContext();
request.Properties.Add("MS_OwinContext", context);

var container = new ContainerBuilder().Build();
context.Set(Constants.OwinLifetimeScopeKey, container);

var fakeHandler = new FakeInnerHandler(async _ => await Task.Factory.StartNew<HttpResponseMessage>(() =>
{
throw new DivideByZeroException();
}));
var handler = new DependencyScopeHandler { InnerHandler = fakeHandler };
var invoker = new HttpMessageInvoker(handler);
var task = invoker.SendAsync(request, CancellationToken.None);

Assert.Contains(HttpPropertyKeys.DependencyScope, request.Properties);
await Assert.ThrowsAsync<AggregateException>(() => task);
Assert.DoesNotContain(HttpPropertyKeys.DependencyScope, request.Properties);
}

private class FakeInnerHandler : HttpMessageHandler
{
private readonly Func<HttpRequestMessage, HttpResponseMessage> _delegate;
private readonly Func<HttpRequestMessage, Task<HttpResponseMessage>> _delegate;

public FakeInnerHandler(Func<HttpRequestMessage, HttpResponseMessage> @delegate)
=> _delegate = @delegate;
: this(request => Task.FromResult(@delegate(request)))
{
}

public FakeInnerHandler(Func<HttpRequestMessage, Task<HttpResponseMessage>> @delegate) => _delegate = @delegate;

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
=> Task.FromResult(_delegate(request));
=> _delegate(request);
}
}