Skip to content
This repository was archived by the owner on Nov 2, 2018. It is now read-only.

Commit 0b98f06

Browse files
committed
Validate scope into singleton sevice injection
1 parent 0b13fed commit 0b98f06

File tree

16 files changed

+199
-10
lines changed

16 files changed

+199
-10
lines changed

src/Microsoft.Extensions.DependencyInjection/Properties/Resources.Designer.cs

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.Extensions.DependencyInjection/Resources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,7 @@
141141
<value>A suitable constructor for type '{0}' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.</value>
142142
<comment>{0} = service type</comment>
143143
</data>
144+
<data name="ScopedInSingletonException" xml:space="preserve">
145+
<value>Cannot consume scoped service '{0}' from singleton '{1}'.</value>
146+
</data>
144147
</root>

src/Microsoft.Extensions.DependencyInjection/ServiceCollectionContainerBuilderExtensions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ public static class ServiceCollectionContainerBuilderExtensions
99
{
1010
public static IServiceProvider BuildServiceProvider(this IServiceCollection services)
1111
{
12-
return new ServiceProvider(services);
12+
return BuildServiceProvider(services, false);
13+
}
14+
15+
public static IServiceProvider BuildServiceProvider(this IServiceCollection services, bool validateScopes)
16+
{
17+
return new ServiceProvider(services, validateScopes);
1318
}
1419
}
1520
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
7+
{
8+
internal class CallSiteValidator: CallSiteVisitor<CallSiteValidatorState, object>
9+
{
10+
public void Validate(IServiceCallSite callSite)
11+
{
12+
VisitCallSite(callSite, default(CallSiteValidatorState));
13+
}
14+
15+
protected override object VisitTransient(TransientCallSite transientCallSite, CallSiteValidatorState state)
16+
{
17+
VisitCallSite(transientCallSite.Service, state);
18+
return null;
19+
}
20+
21+
protected override object VisitConstructor(ConstructorCallSite constructorCallSite, CallSiteValidatorState state)
22+
{
23+
foreach (var parameterCallSite in constructorCallSite.ParameterCallSites)
24+
{
25+
VisitCallSite(parameterCallSite, state);
26+
}
27+
return null;
28+
}
29+
30+
protected override object VisitSingleton(SingletonCallSite singletonCallSite, CallSiteValidatorState state)
31+
{
32+
state.Singleton = singletonCallSite;
33+
VisitCallSite(singletonCallSite.ServiceCallSite, state);
34+
return null;
35+
}
36+
37+
protected override object VisitScoped(ScopedCallSite scopedCallSite, CallSiteValidatorState state)
38+
{
39+
// We are fine with having ServiceScopeService requested by singletons
40+
if (scopedCallSite.ServiceCallSite is ServiceScopeService)
41+
{
42+
return null;
43+
}
44+
if (state.Singleton != null)
45+
{
46+
throw new InvalidOperationException(Resources.FormatScopedInSingletonException(scopedCallSite.Key.Type, state.Singleton.Key.Type));
47+
}
48+
VisitCallSite(scopedCallSite.ServiceCallSite, state);
49+
return null;
50+
}
51+
52+
protected override object VisitConstant(ConstantCallSite constantCallSite, CallSiteValidatorState state) => null;
53+
54+
protected override object VisitCreateInstance(CreateInstanceCallSite createInstanceCallSite, CallSiteValidatorState state) => null;
55+
56+
protected override object VisitInstanceService(InstanceService instanceCallSite, CallSiteValidatorState state) => null;
57+
58+
protected override object VisitServiceProviderService(ServiceProviderService serviceProviderService, CallSiteValidatorState state) => null;
59+
60+
protected override object VisitEmptyIEnumerable(EmptyIEnumerableCallSite emptyIEnumerableCallSite, CallSiteValidatorState state) => null;
61+
62+
protected override object VisitServiceScopeService(ServiceScopeService serviceScopeService, CallSiteValidatorState state) => null;
63+
64+
protected override object VisitClosedIEnumerable(ClosedIEnumerableCallSite closedIEnumerableCallSite, CallSiteValidatorState state) => null;
65+
66+
protected override object VisitFactoryService(FactoryService factoryService, CallSiteValidatorState state) => null;
67+
}
68+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
5+
{
6+
internal struct CallSiteValidatorState
7+
{
8+
public SingletonCallSite Singleton;
9+
}
10+
}

src/Microsoft.Extensions.DependencyInjection/ServiceLookup/ClosedIEnumerableService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public ServiceLifetime Lifetime
2525
get { return ServiceLifetime.Transient; }
2626
}
2727

28+
public Type Type => _itemType;
29+
2830
public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
2931
{
3032
var list = new List<IServiceCallSite>();
@@ -36,6 +38,5 @@ public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> call
3638
}
3739
return new ClosedIEnumerableCallSite(_itemType, list.ToArray());
3840
}
39-
4041
}
4142
}

src/Microsoft.Extensions.DependencyInjection/ServiceLookup/FactoryService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public ServiceLifetime Lifetime
2222
get { return Descriptor.Lifetime; }
2323
}
2424

25+
public Type Type => Descriptor.ServiceType;
26+
2527
public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
2628
{
2729
return this;

src/Microsoft.Extensions.DependencyInjection/ServiceLookup/IService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ internal interface IService
1313
ServiceLifetime Lifetime { get; }
1414

1515
IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain);
16+
17+
Type Type { get; }
1618
}
1719
}

src/Microsoft.Extensions.DependencyInjection/ServiceLookup/InstanceService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public ServiceLifetime Lifetime
2525
get { return Descriptor.Lifetime; }
2626
}
2727

28+
public Type Type => Descriptor.ServiceType;
29+
2830
public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
2931
{
3032
return this;

src/Microsoft.Extensions.DependencyInjection/ServiceLookup/Service.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> call
117117
}
118118
}
119119

120+
public Type Type => _descriptor.ServiceType;
121+
120122
private bool IsSuperset(IEnumerable<Type> left, IEnumerable<Type> right)
121123
{
122124
return new HashSet<Type>(left).IsSupersetOf(right);

0 commit comments

Comments
 (0)