Preserve `ViewDataDictionary.ModelType` for `Nullable<T>` properties when `Model` is non-`null`
- #2539 - reuse `ModelMetadata` and occasionally `ModelExplorer` when `ModelType` is `Nullable<T>`
This commit is contained in:
parent
c2347e4d4b
commit
f10a071da3
|
|
@ -186,7 +186,9 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
// This is the core constructor called when Model is known.
|
// This is the core constructor called when Model is known.
|
||||||
var modelType = GetModelType(model);
|
var modelType = GetModelType(model);
|
||||||
if (modelType == source.ModelMetadata.ModelType && model == source.ModelExplorer.Model)
|
var metadataModelType =
|
||||||
|
Nullable.GetUnderlyingType(source.ModelMetadata.ModelType) ?? source.ModelMetadata.ModelType;
|
||||||
|
if (modelType == metadataModelType && model == source.ModelExplorer.Model)
|
||||||
{
|
{
|
||||||
// Preserve any customizations made to source.ModelExplorer.ModelMetadata if the Type
|
// Preserve any customizations made to source.ModelExplorer.ModelMetadata if the Type
|
||||||
// that will be calculated in SetModel() and source.Model match new instance's values.
|
// that will be calculated in SetModel() and source.Model match new instance's values.
|
||||||
|
|
@ -384,7 +386,13 @@ namespace Microsoft.AspNet.Mvc
|
||||||
// null. When called from the Model setter, ModelMetadata will (temporarily) be null. When called from
|
// null. When called from the Model setter, ModelMetadata will (temporarily) be null. When called from
|
||||||
// a constructor, current ModelMetadata may already be set to preserve customizations made in parent scope.
|
// a constructor, current ModelMetadata may already be set to preserve customizations made in parent scope.
|
||||||
var modelType = GetModelType(value);
|
var modelType = GetModelType(value);
|
||||||
if (ModelExplorer?.Metadata.ModelType != modelType)
|
Type metadataModelType = null;
|
||||||
|
if (ModelExplorer != null)
|
||||||
|
{
|
||||||
|
metadataModelType = Nullable.GetUnderlyingType(ModelMetadata.ModelType) ?? ModelMetadata.ModelType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadataModelType != modelType)
|
||||||
{
|
{
|
||||||
ModelExplorer = _metadataProvider.GetModelExplorerForType(modelType, value);
|
ModelExplorer = _metadataProvider.GetModelExplorerForType(modelType, value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var vdd = new ViewDataDictionary(new EmptyModelMetadataProvider());
|
var vdd = new ViewDataDictionary(new EmptyModelMetadataProvider());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
vdd.Model = model;
|
vdd.Model = model;
|
||||||
|
|
||||||
|
|
@ -257,12 +257,15 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
// Instances in this data set must have exactly the same type as the corresponding Type. Otherwise
|
// Instances in this data set must have exactly the same type as the corresponding Type or be null.
|
||||||
// the copy constructor ignores the source ModelMetadata.
|
// Otherwise the copy constructor ignores the source ModelMetadata.
|
||||||
return new TheoryData<Type, object>
|
return new TheoryData<Type, object>
|
||||||
{
|
{
|
||||||
{ typeof(int), 23 },
|
{ typeof(int), 23 },
|
||||||
|
{ typeof(ulong?), 24ul },
|
||||||
|
{ typeof(ushort?), null },
|
||||||
{ typeof(string), "hello" },
|
{ typeof(string), "hello" },
|
||||||
|
{ typeof(string), null },
|
||||||
{ typeof(List<string>), new List<string>() },
|
{ typeof(List<string>), new List<string>() },
|
||||||
{ typeof(string[]), new string[0] },
|
{ typeof(string[]), new string[0] },
|
||||||
{ typeof(Dictionary<string, object>), new Dictionary<string, object>() },
|
{ typeof(Dictionary<string, object>), new Dictionary<string, object>() },
|
||||||
|
|
@ -359,6 +362,33 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
Assert.NotSame(originalExplorer, viewData.ModelExplorer);
|
Assert.NotSame(originalExplorer, viewData.ModelExplorer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ModelSetter_SetNullableNonNull_UpdatesModelExplorer()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
var metadata = metadataProvider.GetMetadataForType(typeof(bool?));
|
||||||
|
var explorer = new ModelExplorer(metadataProvider, metadata, model: null);
|
||||||
|
var viewData = new ViewDataDictionary(metadataProvider)
|
||||||
|
{
|
||||||
|
ModelExplorer = explorer,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
viewData.Model = true;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(viewData.ModelMetadata);
|
||||||
|
Assert.NotNull(viewData.ModelExplorer);
|
||||||
|
Assert.Same(metadata, viewData.ModelMetadata);
|
||||||
|
Assert.Same(metadata.ModelType, explorer.ModelType);
|
||||||
|
Assert.NotSame(explorer, viewData.ModelExplorer);
|
||||||
|
Assert.Equal(viewData.Model, viewData.ModelExplorer.Model);
|
||||||
|
|
||||||
|
var model = Assert.IsType<bool>(viewData.Model);
|
||||||
|
Assert.True(model);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ModelSetter_SameType_BoxedValueTypeUpdatesModelExplorer()
|
public void ModelSetter_SameType_BoxedValueTypeUpdatesModelExplorer()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue