Skip to content
This repository was archived by the owner on Feb 23, 2021. It is now read-only.

Commit 24a9c54

Browse files
committed
Added design time support for ViewComponent TagHelpers
1 parent f3240cc commit 24a9c54

3 files changed

Lines changed: 107 additions & 47 deletions

File tree

src/Microsoft.AspNetCore.Razor.Design/Internal/AssemblyTagHelperDescriptorResolver.cs

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,26 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Globalization;
7+
using System.Linq;
8+
using System.Reflection;
79
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
810
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
11+
using Microsoft.Extensions.DependencyInjection;
912

1013
namespace 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
}

src/Microsoft.AspNetCore.Razor.Design/project.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"dependencies": {
3939
"Microsoft.AspNetCore.Razor.Runtime": "1.1.0-*",
4040
"Microsoft.Extensions.CommandLineUtils": "1.1.0-*",
41+
"Microsoft.Extensions.DependencyInjection": "1.1.0-*",
4142
"Newtonsoft.Json": "9.0.1"
4243
},
4344
"frameworks": {
@@ -49,6 +50,10 @@
4950
}
5051
}
5152
},
52-
"net451": {}
53+
"net451": {
54+
"frameworkAssemblies": {
55+
"System.Runtime": ""
56+
}
57+
}
5358
}
5459
}

test/Microsoft.AspNetCore.Razor.Design.Test/Internal/AssemblyTagHelperDescriptorResolverTest.cs

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Reflection;
77
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
8+
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
89
using Microsoft.AspNetCore.Razor.TagHelpers;
910
using Microsoft.AspNetCore.Razor.Test.Internal;
1011
using Xunit;
@@ -52,11 +53,11 @@ public void Resolve_ThrowsWhenInvalidProtocol(int protocol)
5253
public void Resolve_ResolvesTagHelperDescriptors()
5354
{
5455
// Arrange
55-
var assemblyNameLookups = new Dictionary<string, IEnumerable<Type>>
56+
var assemblyNameLookups = new Dictionary<string, IEnumerable<TypeInfo>>
5657
{
57-
{ CustomTagHelperAssembly, new[] { typeof(CustomTagHelper) } }
58+
{ CustomTagHelperAssembly, new[] { typeof(CustomTagHelper).GetTypeInfo() } }
5859
};
59-
var descriptorResolver = new TestAssemblyTagHelperDescriptorResolver(assemblyNameLookups);
60+
var descriptorResolver = new AssemblyTagHelperDescriptorResolver(new TestTagHelperTypeResolver(assemblyNameLookups));
6061
var errorSink = new ErrorSink();
6162

6263
// Act
@@ -74,11 +75,11 @@ public void Resolve_ResolvesTagHelperDescriptors()
7475
public void Resolve_ResolvesDesignTimeTagHelperDescriptors()
7576
{
7677
// Arrange
77-
var assemblyNameLookups = new Dictionary<string, IEnumerable<Type>>
78+
var assemblyNameLookups = new Dictionary<string, IEnumerable<TypeInfo>>
7879
{
79-
{ CustomTagHelperAssembly, new[] { typeof(DesignTimeTagHelper) } }
80+
{ CustomTagHelperAssembly, new[] { typeof(DesignTimeTagHelper).GetTypeInfo() } }
8081
};
81-
var descriptorResolver = new TestAssemblyTagHelperDescriptorResolver(assemblyNameLookups);
82+
var descriptorResolver = new AssemblyTagHelperDescriptorResolver(new TestTagHelperTypeResolver(assemblyNameLookups));
8283
var expectedDescriptor = new TagHelperDescriptor
8384
{
8485
Prefix = DefaultPrefix,
@@ -109,11 +110,12 @@ public void Resolve_ResolvesDesignTimeTagHelperDescriptors()
109110
public void Resolve_CreatesErrors()
110111
{
111112
// Arrange
112-
var assemblyNameLookups = new Dictionary<string, IEnumerable<Type>>
113+
var assemblyNameLookups = new Dictionary<string, IEnumerable<TypeInfo>>
113114
{
114-
{ CustomTagHelperAssembly, new[] { typeof(InvalidTagHelper) } }
115+
{ CustomTagHelperAssembly, new[] { typeof(InvalidTagHelper).GetTypeInfo() } }
115116
};
116-
var descriptorResolver = new TestAssemblyTagHelperDescriptorResolver(assemblyNameLookups);
117+
118+
var descriptorResolver = new AssemblyTagHelperDescriptorResolver(new TestTagHelperTypeResolver(assemblyNameLookups));
117119
var errorSink = new ErrorSink();
118120

119121
// Act
@@ -130,35 +132,35 @@ public void Resolve_CreatesErrors()
130132
Assert.Equal(0, error.Length);
131133
}
132134

133-
private class TestAssemblyTagHelperDescriptorResolver : AssemblyTagHelperDescriptorResolver
135+
private class TestTagHelperTypeResolver : TagHelperTypeResolver
134136
{
135-
private readonly IDictionary<string, IEnumerable<Type>> _assemblyTypeLookups;
137+
private readonly IDictionary<string, IEnumerable<TypeInfo>> _assemblyTypeLookups;
136138

137-
public TestAssemblyTagHelperDescriptorResolver(IDictionary<string, IEnumerable<Type>> assemblyTypeLookups)
139+
public TestTagHelperTypeResolver(IDictionary<string, IEnumerable<TypeInfo>> assemblyTypeLookups)
138140
{
139141
_assemblyTypeLookups = assemblyTypeLookups;
140142
}
141143

142-
protected override IEnumerable<Type> GetTagHelperTypes(string assemblyName, ErrorSink errorSink)
144+
protected override IEnumerable<TypeInfo> GetExportedTypes(AssemblyName assemblyName)
143145
{
144-
return _assemblyTypeLookups[assemblyName];
146+
return _assemblyTypeLookups[assemblyName.Name];
145147
}
146148
}
149+
}
147150

148-
private class CustomTagHelper : TagHelper
149-
{
150-
}
151+
public class CustomTagHelper : TagHelper
152+
{
153+
}
151154

152-
[RestrictChildren("br")]
153-
[OutputElementHint("strong")]
154-
[HtmlTargetElement("design-time", TagStructure = TagStructure.NormalOrSelfClosing)]
155-
private class DesignTimeTagHelper : TagHelper
156-
{
157-
}
155+
[RestrictChildren("br")]
156+
[OutputElementHint("strong")]
157+
[HtmlTargetElement("design-time", TagStructure = TagStructure.NormalOrSelfClosing)]
158+
public class DesignTimeTagHelper : TagHelper
159+
{
160+
}
158161

159-
[HtmlTargetElement("inv@lid")]
160-
private class InvalidTagHelper : TagHelper
161-
{
162-
}
162+
[HtmlTargetElement("inv@lid")]
163+
public class InvalidTagHelper : TagHelper
164+
{
163165
}
164166
}

0 commit comments

Comments
 (0)