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.
|
||||
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
|
||||
// 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
|
||||
// a constructor, current ModelMetadata may already be set to preserve customizations made in parent scope.
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
{
|
||||
// Arrange
|
||||
var vdd = new ViewDataDictionary(new EmptyModelMetadataProvider());
|
||||
|
||||
|
||||
// Act
|
||||
vdd.Model = model;
|
||||
|
||||
|
|
@ -257,12 +257,15 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
{
|
||||
get
|
||||
{
|
||||
// Instances in this data set must have exactly the same type as the corresponding Type. Otherwise
|
||||
// the copy constructor ignores the source ModelMetadata.
|
||||
// Instances in this data set must have exactly the same type as the corresponding Type or be null.
|
||||
// Otherwise the copy constructor ignores the source ModelMetadata.
|
||||
return new TheoryData<Type, object>
|
||||
{
|
||||
{ typeof(int), 23 },
|
||||
{ typeof(ulong?), 24ul },
|
||||
{ typeof(ushort?), null },
|
||||
{ typeof(string), "hello" },
|
||||
{ typeof(string), null },
|
||||
{ typeof(List<string>), new List<string>() },
|
||||
{ typeof(string[]), new string[0] },
|
||||
{ typeof(Dictionary<string, object>), new Dictionary<string, object>() },
|
||||
|
|
@ -359,6 +362,33 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
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]
|
||||
public void ModelSetter_SameType_BoxedValueTypeUpdatesModelExplorer()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue