[Fixes #5166] Support passing instance directly when invoking ViewComponents with single parameter
This commit is contained in:
parent
9ed753288f
commit
a6a4b5369a
|
|
@ -53,12 +53,14 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponents
|
||||||
|
|
||||||
private static ViewComponentDescriptor CreateDescriptor(TypeInfo typeInfo)
|
private static ViewComponentDescriptor CreateDescriptor(TypeInfo typeInfo)
|
||||||
{
|
{
|
||||||
|
var methodInfo = FindMethod(typeInfo.AsType());
|
||||||
var candidate = new ViewComponentDescriptor
|
var candidate = new ViewComponentDescriptor
|
||||||
{
|
{
|
||||||
FullName = ViewComponentConventions.GetComponentFullName(typeInfo),
|
FullName = ViewComponentConventions.GetComponentFullName(typeInfo),
|
||||||
ShortName = ViewComponentConventions.GetComponentName(typeInfo),
|
ShortName = ViewComponentConventions.GetComponentName(typeInfo),
|
||||||
TypeInfo = typeInfo,
|
TypeInfo = typeInfo,
|
||||||
MethodInfo = FindMethod(typeInfo.AsType())
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters()
|
||||||
};
|
};
|
||||||
|
|
||||||
return candidate;
|
return candidate;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Html;
|
using Microsoft.AspNetCore.Html;
|
||||||
|
|
@ -130,19 +132,28 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponents
|
||||||
componentType.FullName));
|
componentType.FullName));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IHtmlContent> InvokeCoreAsync(
|
// Internal for testing
|
||||||
ViewComponentDescriptor descriptor,
|
internal IDictionary<string, object> GetArgumentDictionary(ViewComponentDescriptor descriptor, object arguments)
|
||||||
object arguments)
|
|
||||||
{
|
{
|
||||||
|
if (descriptor.Parameters.Count == 1 && descriptor.Parameters[0].ParameterType.IsAssignableFrom(arguments.GetType()))
|
||||||
|
{
|
||||||
|
return new Dictionary<string, object>(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ descriptor.Parameters[0].Name, arguments }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return PropertyHelper.ObjectToDictionary(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IHtmlContent> InvokeCoreAsync(ViewComponentDescriptor descriptor, object arguments)
|
||||||
|
{
|
||||||
|
var argumentDictionary = GetArgumentDictionary(descriptor, arguments);
|
||||||
|
|
||||||
var viewBuffer = new ViewBuffer(_viewBufferScope, descriptor.FullName, ViewBuffer.ViewComponentPageSize);
|
var viewBuffer = new ViewBuffer(_viewBufferScope, descriptor.FullName, ViewBuffer.ViewComponentPageSize);
|
||||||
using (var writer = new ViewBufferTextWriter(viewBuffer, _viewContext.Writer.Encoding))
|
using (var writer = new ViewBufferTextWriter(viewBuffer, _viewContext.Writer.Encoding))
|
||||||
{
|
{
|
||||||
var context = new ViewComponentContext(
|
var context = new ViewComponentContext(descriptor, argumentDictionary, _htmlEncoder, _viewContext, writer);
|
||||||
descriptor,
|
|
||||||
PropertyHelper.ObjectToDictionary(arguments),
|
|
||||||
_htmlEncoder,
|
|
||||||
_viewContext,
|
|
||||||
writer);
|
|
||||||
|
|
||||||
var invoker = _invokerFactory.CreateInstance(context);
|
var invoker = _invokerFactory.CreateInstance(context);
|
||||||
if (invoker == null)
|
if (invoker == null)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
|
|
@ -124,5 +126,10 @@ namespace Microsoft.AspNetCore.Mvc.ViewComponents
|
||||||
/// Gets or sets the <see cref="System.Reflection.MethodInfo"/> to invoke.
|
/// Gets or sets the <see cref="System.Reflection.MethodInfo"/> to invoke.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MethodInfo MethodInfo { get; set; }
|
public MethodInfo MethodInfo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the parameters associated with the method described by <see cref="MethodInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<ParameterInfo> Parameters { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,12 +55,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public async Task ExecuteAsync_ViewComponentResult_AllowsNullViewDataAndTempData()
|
public async Task ExecuteAsync_ViewComponentResult_AllowsNullViewDataAndTempData()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.Text",
|
FullName = "Full.Name.Text",
|
||||||
ShortName = "Text",
|
ShortName = "Text",
|
||||||
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)),
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var actionContext = CreateActionContext(descriptor);
|
var actionContext = CreateActionContext(descriptor);
|
||||||
|
|
@ -146,12 +148,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public async Task ExecuteResultAsync_ExecutesSyncViewComponent()
|
public async Task ExecuteResultAsync_ExecutesSyncViewComponent()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.Text",
|
FullName = "Full.Name.Text",
|
||||||
ShortName = "Text",
|
ShortName = "Text",
|
||||||
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)),
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var actionContext = CreateActionContext(descriptor);
|
var actionContext = CreateActionContext(descriptor);
|
||||||
|
|
@ -175,12 +179,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public async Task ExecuteResultAsync_UsesDictionaryArguments()
|
public async Task ExecuteResultAsync_UsesDictionaryArguments()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.Text",
|
FullName = "Full.Name.Text",
|
||||||
ShortName = "Text",
|
ShortName = "Text",
|
||||||
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)),
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var actionContext = CreateActionContext(descriptor);
|
var actionContext = CreateActionContext(descriptor);
|
||||||
|
|
@ -204,12 +210,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public async Task ExecuteResultAsync_ExecutesAsyncViewComponent()
|
public async Task ExecuteResultAsync_ExecutesAsyncViewComponent()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(AsyncTextViewComponent).GetMethod(nameof(AsyncTextViewComponent.InvokeAsync));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.AsyncText",
|
FullName = "Full.Name.AsyncText",
|
||||||
ShortName = "AsyncText",
|
ShortName = "AsyncText",
|
||||||
TypeInfo = typeof(AsyncTextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(AsyncTextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(AsyncTextViewComponent).GetMethod(nameof(AsyncTextViewComponent.InvokeAsync)),
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var actionContext = CreateActionContext(descriptor);
|
var actionContext = CreateActionContext(descriptor);
|
||||||
|
|
@ -233,12 +241,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public async Task ExecuteResultAsync_ExecutesViewComponent_AndWritesDiagnosticSource()
|
public async Task ExecuteResultAsync_ExecutesViewComponent_AndWritesDiagnosticSource()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.Text",
|
FullName = "Full.Name.Text",
|
||||||
ShortName = "Text",
|
ShortName = "Text",
|
||||||
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)),
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var adapter = new TestDiagnosticListener();
|
var adapter = new TestDiagnosticListener();
|
||||||
|
|
@ -272,12 +282,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public async Task ExecuteResultAsync_ExecutesViewComponent_ByShortName()
|
public async Task ExecuteResultAsync_ExecutesViewComponent_ByShortName()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.Text",
|
FullName = "Full.Name.Text",
|
||||||
ShortName = "Text",
|
ShortName = "Text",
|
||||||
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)),
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var actionContext = CreateActionContext(descriptor);
|
var actionContext = CreateActionContext(descriptor);
|
||||||
|
|
@ -301,12 +313,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public async Task ExecuteResultAsync_ExecutesViewComponent_ByFullName()
|
public async Task ExecuteResultAsync_ExecutesViewComponent_ByFullName()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.Text",
|
FullName = "Full.Name.Text",
|
||||||
ShortName = "Text",
|
ShortName = "Text",
|
||||||
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)),
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var actionContext = CreateActionContext(descriptor);
|
var actionContext = CreateActionContext(descriptor);
|
||||||
|
|
@ -330,12 +344,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public async Task ExecuteResultAsync_ExecutesViewComponent_ByType()
|
public async Task ExecuteResultAsync_ExecutesViewComponent_ByType()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.Text",
|
FullName = "Full.Name.Text",
|
||||||
ShortName = "Text",
|
ShortName = "Text",
|
||||||
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)),
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var actionContext = CreateActionContext(descriptor);
|
var actionContext = CreateActionContext(descriptor);
|
||||||
|
|
@ -359,12 +375,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public async Task ExecuteResultAsync_SetsStatusCode()
|
public async Task ExecuteResultAsync_SetsStatusCode()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.Text",
|
FullName = "Full.Name.Text",
|
||||||
ShortName = "Text",
|
ShortName = "Text",
|
||||||
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke))
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var actionContext = CreateActionContext(descriptor);
|
var actionContext = CreateActionContext(descriptor);
|
||||||
|
|
@ -417,12 +435,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
string expectedContentType)
|
string expectedContentType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.Text",
|
FullName = "Full.Name.Text",
|
||||||
ShortName = "Text",
|
ShortName = "Text",
|
||||||
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)),
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var actionContext = CreateActionContext(descriptor);
|
var actionContext = CreateActionContext(descriptor);
|
||||||
|
|
@ -455,12 +475,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public async Task ViewComponentResult_SetsContentTypeHeader_OverrideResponseContentType()
|
public async Task ViewComponentResult_SetsContentTypeHeader_OverrideResponseContentType()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.Text",
|
FullName = "Full.Name.Text",
|
||||||
ShortName = "Text",
|
ShortName = "Text",
|
||||||
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)),
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var actionContext = CreateActionContext(descriptor);
|
var actionContext = CreateActionContext(descriptor);
|
||||||
|
|
@ -492,12 +514,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
string expectedContentType)
|
string expectedContentType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke));
|
||||||
var descriptor = new ViewComponentDescriptor()
|
var descriptor = new ViewComponentDescriptor()
|
||||||
{
|
{
|
||||||
FullName = "Full.Name.Text",
|
FullName = "Full.Name.Text",
|
||||||
ShortName = "Text",
|
ShortName = "Text",
|
||||||
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
TypeInfo = typeof(TextViewComponent).GetTypeInfo(),
|
||||||
MethodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)),
|
MethodInfo = methodInfo,
|
||||||
|
Parameters = methodInfo.GetParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var actionContext = CreateActionContext(descriptor);
|
var actionContext = CreateActionContext(descriptor);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||||
|
using Microsoft.Extensions.WebEncoders.Testing;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ViewComponents
|
||||||
|
{
|
||||||
|
public class DefaultViewComponentHelperTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GetArgumentDictionary_SupportsAnonymouslyTypedArguments()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = CreateHelper();
|
||||||
|
var descriptor = CreateDescriptorForType(typeof(ViewComponentSingleParam));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var argumentDictionary = helper.GetArgumentDictionary(descriptor, new { a = 0 });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(argumentDictionary,
|
||||||
|
item =>
|
||||||
|
{
|
||||||
|
Assert.Equal("a", item.Key);
|
||||||
|
Assert.IsType(typeof(int), item.Value);
|
||||||
|
Assert.Equal(0, item.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetArgumentDictionary_SingleParameter_DoesNotNeedAnonymouslyTypedArguments()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = CreateHelper();
|
||||||
|
var descriptor = CreateDescriptorForType(typeof(ViewComponentSingleParam));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var argumentDictionary = helper.GetArgumentDictionary(descriptor, 0);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(argumentDictionary,
|
||||||
|
item =>
|
||||||
|
{
|
||||||
|
Assert.Equal("a", item.Key);
|
||||||
|
Assert.IsType(typeof(int), item.Value);
|
||||||
|
Assert.Equal(0, item.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetArgumentDictionary_MultipleParameters_NeedsAnonymouslyTypedArguments()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = CreateHelper();
|
||||||
|
var descriptor = CreateDescriptorForType(typeof(ViewComponentMultipleParam));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var argumentDictionary = helper.GetArgumentDictionary(descriptor, new { a = 0, b = "foo" });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(argumentDictionary,
|
||||||
|
item1 =>
|
||||||
|
{
|
||||||
|
Assert.Equal("a", item1.Key);
|
||||||
|
Assert.IsType(typeof(int), item1.Value);
|
||||||
|
Assert.Equal(0, item1.Value);
|
||||||
|
},
|
||||||
|
item2 =>
|
||||||
|
{
|
||||||
|
Assert.Equal("b", item2.Key);
|
||||||
|
Assert.IsType(typeof(string), item2.Value);
|
||||||
|
Assert.Equal("foo", item2.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetArgumentDictionary_SingleObjectParameter_DoesNotNeedAnonymouslyTypedArguments()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = CreateHelper();
|
||||||
|
var descriptor = CreateDescriptorForType(typeof(ViewComponentObjectParam));
|
||||||
|
var expectedValue = new object();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var argumentDictionary = helper.GetArgumentDictionary(descriptor, expectedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(argumentDictionary,
|
||||||
|
item =>
|
||||||
|
{
|
||||||
|
Assert.Equal("o", item.Key);
|
||||||
|
Assert.IsType(typeof(object), item.Value);
|
||||||
|
Assert.Same(expectedValue, item.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetArgumentDictionary_SingleParameter_AcceptsDictionaryType()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var helper = CreateHelper();
|
||||||
|
var descriptor = CreateDescriptorForType(typeof(ViewComponentSingleParam));
|
||||||
|
var arguments = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "a", 10 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var argumentDictionary = helper.GetArgumentDictionary(descriptor, arguments);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(argumentDictionary,
|
||||||
|
item =>
|
||||||
|
{
|
||||||
|
Assert.Equal("a", item.Key);
|
||||||
|
Assert.IsType(typeof(int), item.Value);
|
||||||
|
Assert.Equal(10, item.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private DefaultViewComponentHelper CreateHelper()
|
||||||
|
{
|
||||||
|
var descriptorCollectionProvider = Mock.Of<IViewComponentDescriptorCollectionProvider>();
|
||||||
|
var selector = Mock.Of<IViewComponentSelector>();
|
||||||
|
var invokerFactory = Mock.Of<IViewComponentInvokerFactory>();
|
||||||
|
var viewBufferScope = Mock.Of<IViewBufferScope>();
|
||||||
|
|
||||||
|
return new DefaultViewComponentHelper(
|
||||||
|
descriptorCollectionProvider,
|
||||||
|
new HtmlTestEncoder(),
|
||||||
|
selector,
|
||||||
|
invokerFactory,
|
||||||
|
viewBufferScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ViewComponentDescriptor CreateDescriptorForType(Type componentType)
|
||||||
|
{
|
||||||
|
var provider = CreateProvider(componentType);
|
||||||
|
return provider.GetViewComponents().First();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ViewComponentSingleParam
|
||||||
|
{
|
||||||
|
public IViewComponentResult Invoke(int a) => null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ViewComponentMultipleParam
|
||||||
|
{
|
||||||
|
public IViewComponentResult Invoke(int a, string b) => null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ViewComponentObjectParam
|
||||||
|
{
|
||||||
|
public IViewComponentResult Invoke(object o) => null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DefaultViewComponentDescriptorProvider CreateProvider(Type componentType)
|
||||||
|
{
|
||||||
|
return new FilteredViewComponentDescriptorProvider(componentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will only consider types nested inside this class as ViewComponent classes
|
||||||
|
private class FilteredViewComponentDescriptorProvider : DefaultViewComponentDescriptorProvider
|
||||||
|
{
|
||||||
|
public FilteredViewComponentDescriptorProvider(params Type[] allowedTypes)
|
||||||
|
: base(GetApplicationPartManager(allowedTypes.Select(t => t.GetTypeInfo())))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ApplicationPartManager GetApplicationPartManager(IEnumerable<TypeInfo> types)
|
||||||
|
{
|
||||||
|
var manager = new ApplicationPartManager();
|
||||||
|
manager.ApplicationParts.Add(new TestApplicationPart(types));
|
||||||
|
manager.FeatureProviders.Add(new TestFeatureProvider());
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestFeatureProvider : IApplicationFeatureProvider<ViewComponentFeature>
|
||||||
|
{
|
||||||
|
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ViewComponentFeature feature)
|
||||||
|
{
|
||||||
|
foreach (var type in parts.OfType<IApplicationPartTypeProvider>().SelectMany(p => p.Types))
|
||||||
|
{
|
||||||
|
feature.ViewComponents.Add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
@model Person
|
@model Person
|
||||||
<h1>@Model.Name</h1>
|
<h1>@Model.Name</h1>
|
||||||
@await Component.InvokeAsync("InheritingViewComponent", new { address = Model.Address })
|
@await Component.InvokeAsync("InheritingViewComponent", Model.Address)
|
||||||
|
|
@ -12,4 +12,4 @@
|
||||||
}
|
}
|
||||||
ViewWithRelativePath-content
|
ViewWithRelativePath-content
|
||||||
<partial>@await Html.PartialAsync("../Shared/_PartialThatSetsTitle.cshtml")</partial>
|
<partial>@await Html.PartialAsync("../Shared/_PartialThatSetsTitle.cshtml")</partial>
|
||||||
@await Component.InvokeAsync("ComponentWithRelativePath", new { person })
|
@await Component.InvokeAsync("ComponentWithRelativePath", person)
|
||||||
Loading…
Reference in New Issue