[Fixes #5686] Extend `ModelMetadata` to reduce ugliness

This commit is contained in:
Kiran Challa 2017-01-31 10:03:54 -08:00
parent 4da7356348
commit c95c2a5a6d
7 changed files with 148 additions and 1 deletions

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// A metadata representation of a model type, property or parameter.
/// </summary>
[DebuggerDisplay("{DebuggerToString(),nq}")]
public abstract class ModelMetadata : IEquatable<ModelMetadata>
public abstract class ModelMetadata : IEquatable<ModelMetadata>, IModelMetadataProvider
{
/// <summary>
/// The default value of <see cref="ModelMetadata.Order"/>.
@ -42,6 +42,17 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// </summary>
public Type ContainerType { get { return Identity.ContainerType; } }
/// <summary>
/// Gets the metadata of the container type that the current instance is part of.
/// </summary>
public virtual ModelMetadata ContainerMetadata
{
get
{
throw new NotImplementedException();
}
}
/// <summary>
/// Gets a value indicating the kind of metadata element represented by the current instance.
/// </summary>
@ -474,5 +485,17 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
return $"ModelMetadata (Property: '{ContainerType.Name}.{PropertyName}' Type: '{ModelType.Name}')";
}
}
/// <inheritdoc />
public virtual ModelMetadata GetMetadataForType(Type modelType)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public virtual IEnumerable<ModelMetadata> GetMetadataForProperties(Type modelType)
{
throw new NotImplementedException();
}
}
}

View File

@ -68,5 +68,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
/// Gets or sets the <see cref="Metadata.ValidationMetadata"/>
/// </summary>
public ValidationMetadata ValidationMetadata { get; set; }
/// <summary>
/// Gets or sets the <see cref="ModelMetadata"/> of the container type.
/// </summary>
public ModelMetadata ContainerMetadata { get; set; }
}
}

View File

@ -96,6 +96,15 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
}
}
/// <inheritdoc />
public override ModelMetadata ContainerMetadata
{
get
{
return _details.ContainerMetadata;
}
}
/// <summary>
/// Gets the <see cref="Metadata.BindingMetadata"/> for the current instance.
/// </summary>
@ -597,5 +606,17 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
return _details.PropertySetter;
}
}
/// <inheritdoc />
public override ModelMetadata GetMetadataForType(Type modelType)
{
return _provider.GetMetadataForType(modelType);
}
/// <inheritdoc />
public override IEnumerable<ModelMetadata> GetMetadataForProperties(Type modelType)
{
return _provider.GetMetadataForProperties(modelType);
}
}
}

View File

@ -87,6 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
var properties = new ModelMetadata[propertyDetails.Length];
for (var i = 0; i < properties.Length; i++)
{
propertyDetails[i].ContainerMetadata = cacheEntry.Metadata;
properties[i] = CreateModelMetadata(propertyDetails[i]);
}

View File

@ -302,6 +302,36 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
Assert.Equal("String", result);
}
[Fact]
public void GetContainerMetadata_ThrowsNotImplementedException_ByDefault()
{
// Arrange
var metadata = new TestModelMetadata(typeof(DerivedList));
// Act & Assert
Assert.Throws<NotImplementedException>(() => metadata.ContainerMetadata);
}
[Fact]
public void GetMetadataForType_ByDefaultThrows_NotImplementedException()
{
// Arrange
var metadata = new TestModelMetadata(typeof(string));
// Act & Assert
var result = Assert.Throws<NotImplementedException>(() => metadata.GetMetadataForType(typeof(string)));
}
[Fact]
public void GetMetadataForProperties_ByDefaultThrows_NotImplementedException()
{
// Arrange
var metadata = new TestModelMetadata(typeof(string));
// Act & Assert
var result = Assert.Throws<NotImplementedException>(() => metadata.GetMetadataForProperties(typeof(string)));
}
private class TestModelMetadata : ModelMetadata
{
private string _displayName;

View File

@ -57,6 +57,33 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
Assert.Same(metadata1, metadata2);
}
[Fact]
public void GetMetadataForProperties_IncludesContainerMetadataForAllProperties()
{
// Arrange
var provider = CreateProvider();
var modelType = typeof(ModelType);
// Act
var metadata = provider.GetMetadataForProperties(modelType).ToArray();
// Assert
Assert.Collection(
metadata,
(propertyMetadata) =>
{
Assert.Equal("Property1", propertyMetadata.PropertyName);
Assert.NotNull(propertyMetadata.ContainerMetadata);
Assert.Equal(modelType, propertyMetadata.ContainerMetadata.ModelType);
},
(propertyMetadata) =>
{
Assert.Equal("Property2", propertyMetadata.PropertyName);
Assert.NotNull(propertyMetadata.ContainerMetadata);
Assert.Equal(modelType, propertyMetadata.ContainerMetadata.ModelType);
});
}
[Fact]
public void GetMetadataForProperties_IncludesAllProperties()
{

View File

@ -744,6 +744,46 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
Assert.False(validateChildren);
}
[Fact]
public void GetMetadataForType_CallsProvider()
{
// Arrange
var detailsProvider = new Mock<ICompositeMetadataDetailsProvider>();
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider
.Setup(mp => mp.GetMetadataForType(typeof(string)))
.Verifiable();
var metadata1 = new DefaultModelMetadata(metadataProvider.Object, detailsProvider.Object, cache);
// Act
var metadata2 = metadata1.GetMetadataForType(typeof(string));
// Assert
metadataProvider.VerifyAll();
}
[Fact]
public void GetMetadataForProperties_CallsProvider()
{
// Arrange
var detailsProvider = new Mock<ICompositeMetadataDetailsProvider>();
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider
.Setup(mp => mp.GetMetadataForProperties(typeof(Exception)))
.Verifiable();
var metadata1 = new DefaultModelMetadata(metadataProvider.Object, detailsProvider.Object, cache);
// Act
var metadata2 = metadata1.GetMetadataForProperties(typeof(Exception));
// Assert
metadataProvider.VerifyAll();
}
private void ActionMethod(string input)
{
}