44using System ;
55using System . Collections . Generic ;
66using System . Globalization ;
7+ using System . Linq ;
8+ using System . Reflection ;
79using Microsoft . AspNetCore . Razor . Compilation . TagHelpers ;
810using Microsoft . AspNetCore . Razor . Runtime . TagHelpers ;
11+ using Microsoft . Extensions . DependencyInjection ;
912
1013namespace Microsoft . AspNetCore . Razor . Design . Internal
1114{
1215 public class AssemblyTagHelperDescriptorResolver
1316 {
14- private readonly TagHelperDescriptorFactory _descriptorFactory = new TagHelperDescriptorFactory ( designTime : true ) ;
17+ private const string TypeFullName = "Microsoft.AspNetCore.Mvc.DesignTimeMvcServiceCollectionProvider" ;
18+ private const string MvcAssemblyName = "Microsoft.AspNetCore.Mvc" ;
19+ private const string MethodName = "PopulateServiceCollection" ;
20+
21+ private readonly TagHelperDescriptorFactory _tagHelperDescriptorFactory ;
1522 private readonly TagHelperTypeResolver _tagHelperTypeResolver ;
1623
24+ private bool _isInitialized ;
25+ private Action < IServiceCollection , string > _populateMethodDelegate ;
26+
1727 public AssemblyTagHelperDescriptorResolver ( )
1828 : this ( new TagHelperTypeResolver ( ) )
1929 {
@@ -22,6 +32,7 @@ public AssemblyTagHelperDescriptorResolver()
2232 public AssemblyTagHelperDescriptorResolver ( TagHelperTypeResolver tagHelperTypeResolver )
2333 {
2434 _tagHelperTypeResolver = tagHelperTypeResolver ;
35+ _tagHelperDescriptorFactory = new TagHelperDescriptorFactory ( designTime : true ) ;
2536 }
2637
2738 public static int DefaultProtocolVersion { get ; } = 1 ;
@@ -35,19 +46,7 @@ public IEnumerable<TagHelperDescriptor> Resolve(string assemblyName, ErrorSink e
3546 throw new ArgumentNullException ( nameof ( assemblyName ) ) ;
3647 }
3748
38- if ( ProtocolVersion == 1 )
39- {
40- var tagHelperTypes = GetTagHelperTypes ( assemblyName , errorSink ) ;
41- var tagHelperDescriptors = new List < TagHelperDescriptor > ( ) ;
42- foreach ( var tagHelperType in tagHelperTypes )
43- {
44- var descriptors = _descriptorFactory . CreateDescriptors ( assemblyName , tagHelperType , errorSink ) ;
45- tagHelperDescriptors . AddRange ( descriptors ) ;
46- }
47-
48- return tagHelperDescriptors ;
49- }
50- else
49+ if ( ProtocolVersion != 1 )
5150 {
5251 // Unknown protocol
5352 throw new InvalidOperationException (
@@ -56,14 +55,68 @@ public IEnumerable<TagHelperDescriptor> Resolve(string assemblyName, ErrorSink e
5655 DesignResources . InvalidProtocolValue ,
5756 typeof ( TagHelperDescriptor ) . FullName , ProtocolVersion ) ) ;
5857 }
58+
59+ EnsureMvcInitialized ( ) ;
60+
61+ ITagHelperDescriptorResolver resolver ;
62+ if ( _populateMethodDelegate != null )
63+ {
64+ var serviceCollection = new ServiceCollection ( ) ;
65+ serviceCollection . AddSingleton < ITagHelperTypeResolver > ( _tagHelperTypeResolver ) ;
66+ serviceCollection . AddSingleton < ITagHelperDescriptorFactory > ( _tagHelperDescriptorFactory ) ;
67+
68+ // Populate the service collection
69+ _populateMethodDelegate ( serviceCollection , assemblyName ) ;
70+
71+ var services = serviceCollection . BuildServiceProvider ( ) ;
72+ resolver = services . GetRequiredService < ITagHelperDescriptorResolver > ( ) ;
73+ }
74+ else
75+ {
76+ // MVC assembly does not exist. Manually create the resolver.
77+ resolver = new TagHelperDescriptorResolver ( _tagHelperTypeResolver , _tagHelperDescriptorFactory ) ;
78+ }
79+
80+ var directiveDescriptors = new [ ]
81+ {
82+ new TagHelperDirectiveDescriptor
83+ {
84+ DirectiveText = $ "*, { assemblyName } "
85+ }
86+ } ;
87+ var context = new TagHelperDescriptorResolutionContext ( directiveDescriptors , errorSink ) ;
88+ var descriptors = resolver . Resolve ( context ) ;
89+
90+ return descriptors ;
5991 }
6092
61- /// <summary>
62- /// Protected virtual for testing.
63- /// </summary>
64- protected virtual IEnumerable < Type > GetTagHelperTypes ( string assemblyName , ErrorSink errorSink )
93+ private void EnsureMvcInitialized ( )
6594 {
66- return _tagHelperTypeResolver . Resolve ( assemblyName , SourceLocation . Zero , errorSink ) ;
95+ if ( _isInitialized )
96+ {
97+ return ;
98+ }
99+
100+ _isInitialized = true ;
101+
102+ var providerClass = Type . GetType ( $ "{ TypeFullName } , { MvcAssemblyName } ") ;
103+
104+ // Get the method from the type
105+ var populateMethod = providerClass ? . GetMethods ( BindingFlags . Public | BindingFlags . Static )
106+ . FirstOrDefault ( methodInfo =>
107+ {
108+ if ( string . Equals ( MethodName , methodInfo . Name , StringComparison . Ordinal ) )
109+ {
110+ var methodParams = methodInfo . GetParameters ( ) ;
111+ return methodParams . Length == 2
112+ && methodParams [ 0 ] . ParameterType . Equals ( typeof ( IServiceCollection ) )
113+ && methodParams [ 1 ] . ParameterType . Equals ( typeof ( string ) ) ;
114+ }
115+ return false ;
116+ } ) ;
117+
118+ _populateMethodDelegate = ( Action < IServiceCollection , string > ) populateMethod
119+ ? . CreateDelegate ( typeof ( Action < IServiceCollection , string > ) ) ;
67120 }
68121 }
69122}
0 commit comments