This repository has been archived by the owner on Dec 14, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
DefaultViewComponentDescriptorProvider.cs
122 lines (110 loc) · 4.86 KB
/
DefaultViewComponentDescriptorProvider.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace Microsoft.AspNetCore.Mvc.ViewComponents
{
/// <summary>
/// Default implementation of <see cref="IViewComponentDescriptorProvider"/>.
/// </summary>
public class DefaultViewComponentDescriptorProvider : IViewComponentDescriptorProvider
{
private const string AsyncMethodName = "InvokeAsync";
private const string SyncMethodName = "Invoke";
private readonly ApplicationPartManager _partManager;
/// <summary>
/// Creates a new <see cref="DefaultViewComponentDescriptorProvider"/>.
/// </summary>
/// <param name="partManager">The <see cref="ApplicationPartManager"/>.</param>
public DefaultViewComponentDescriptorProvider(ApplicationPartManager partManager)
{
if (partManager == null)
{
throw new ArgumentNullException(nameof(partManager));
}
_partManager = partManager;
}
/// <inheritdoc />
public virtual IEnumerable<ViewComponentDescriptor> GetViewComponents()
{
return GetCandidateTypes().Select(CreateDescriptor);
}
/// <summary>
/// Gets the candidate <see cref="TypeInfo"/> instances provided by the <see cref="ApplicationPartManager"/>.
/// </summary>
/// <returns>A list of <see cref="TypeInfo"/> instances.</returns>
protected virtual IEnumerable<TypeInfo> GetCandidateTypes()
{
var feature = new ViewComponentFeature();
_partManager.PopulateFeature(feature);
return feature.ViewComponents;
}
private static ViewComponentDescriptor CreateDescriptor(TypeInfo typeInfo)
{
var methodInfo = FindMethod(typeInfo.AsType());
var candidate = new ViewComponentDescriptor
{
FullName = ViewComponentConventions.GetComponentFullName(typeInfo),
ShortName = ViewComponentConventions.GetComponentName(typeInfo),
TypeInfo = typeInfo,
MethodInfo = methodInfo,
Parameters = methodInfo.GetParameters()
};
return candidate;
}
private static MethodInfo FindMethod(Type componentType)
{
var componentName = componentType.FullName;
var methods = componentType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(method =>
string.Equals(method.Name, AsyncMethodName, StringComparison.Ordinal) ||
string.Equals(method.Name, SyncMethodName, StringComparison.Ordinal))
.ToArray();
if (methods.Length == 0)
{
throw new InvalidOperationException(
Resources.FormatViewComponent_CannotFindMethod(SyncMethodName, AsyncMethodName, componentName));
}
else if (methods.Length > 1)
{
throw new InvalidOperationException(
Resources.FormatViewComponent_AmbiguousMethods(componentName, AsyncMethodName, SyncMethodName));
}
var selectedMethod = methods[0];
if (string.Equals(selectedMethod.Name, AsyncMethodName, StringComparison.Ordinal))
{
if (!selectedMethod.ReturnType.GetTypeInfo().IsGenericType ||
selectedMethod.ReturnType.GetGenericTypeDefinition() != typeof(Task<>))
{
throw new InvalidOperationException(Resources.FormatViewComponent_AsyncMethod_ShouldReturnTask(
AsyncMethodName,
componentName,
nameof(Task)));
}
}
else
{
// Will invoke synchronously. Method must not return void, Task or Task<T>.
if (selectedMethod.ReturnType == typeof(void))
{
throw new InvalidOperationException(Resources.FormatViewComponent_SyncMethod_ShouldReturnValue(
SyncMethodName,
componentName));
}
else if (typeof(Task).IsAssignableFrom(selectedMethod.ReturnType))
{
throw new InvalidOperationException(Resources.FormatViewComponent_SyncMethod_CannotReturnTask(
SyncMethodName,
componentName,
nameof(Task)));
}
}
return selectedMethod;
}
}
}