Fix #298 - Added ModelMetadata.Container property and unit tests.
This commit is contained in:
parent
8a668eb9d1
commit
a0629f4d23
|
|
@ -2,7 +2,6 @@
|
||||||
// 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.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
@ -73,6 +72,7 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
||||||
modelAccessor,
|
modelAccessor,
|
||||||
typeof(TValue),
|
typeof(TValue),
|
||||||
propertyName,
|
propertyName,
|
||||||
|
container,
|
||||||
containerType,
|
containerType,
|
||||||
metadataProvider);
|
metadataProvider);
|
||||||
}
|
}
|
||||||
|
|
@ -92,12 +92,14 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
||||||
Type modelType = null;
|
Type modelType = null;
|
||||||
Func<object> modelAccessor = null;
|
Func<object> modelAccessor = null;
|
||||||
string propertyName = null;
|
string propertyName = null;
|
||||||
|
object container = null;
|
||||||
|
|
||||||
if (viewDataInfo != null)
|
if (viewDataInfo != null)
|
||||||
{
|
{
|
||||||
if (viewDataInfo.Container != null)
|
if (viewDataInfo.Container != null)
|
||||||
{
|
{
|
||||||
containerType = viewDataInfo.Container.GetType();
|
containerType = viewDataInfo.Container.GetType();
|
||||||
|
container = viewDataInfo.Container;
|
||||||
}
|
}
|
||||||
|
|
||||||
modelAccessor = () => viewDataInfo.Value;
|
modelAccessor = () => viewDataInfo.Value;
|
||||||
|
|
@ -124,8 +126,12 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetMetadataFromProvider(modelAccessor, modelType ?? typeof(string), propertyName, containerType,
|
return GetMetadataFromProvider(modelAccessor,
|
||||||
metadataProvider);
|
modelType ?? typeof(string),
|
||||||
|
propertyName,
|
||||||
|
container,
|
||||||
|
containerType,
|
||||||
|
metadataProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ModelMetadata FromModel([NotNull] ViewDataDictionary viewData,
|
private static ModelMetadata FromModel([NotNull] ViewDataDictionary viewData,
|
||||||
|
|
@ -138,6 +144,7 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
||||||
modelAccessor: null,
|
modelAccessor: null,
|
||||||
modelType: typeof(string),
|
modelType: typeof(string),
|
||||||
propertyName: null,
|
propertyName: null,
|
||||||
|
container: null,
|
||||||
containerType: null,
|
containerType: null,
|
||||||
metadataProvider: metadataProvider);
|
metadataProvider: metadataProvider);
|
||||||
}
|
}
|
||||||
|
|
@ -149,12 +156,23 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
||||||
|
|
||||||
// An IModelMetadataProvider is not required unless this method is called. Therefore other methods in this
|
// An IModelMetadataProvider is not required unless this method is called. Therefore other methods in this
|
||||||
// class lack [NotNull] attributes for their corresponding parameter.
|
// class lack [NotNull] attributes for their corresponding parameter.
|
||||||
private static ModelMetadata GetMetadataFromProvider(Func<object> modelAccessor, Type modelType,
|
private static ModelMetadata GetMetadataFromProvider(Func<object> modelAccessor,
|
||||||
string propertyName, Type containerType, [NotNull] IModelMetadataProvider metadataProvider)
|
Type modelType,
|
||||||
|
string propertyName,
|
||||||
|
object container,
|
||||||
|
Type containerType,
|
||||||
|
[NotNull] IModelMetadataProvider metadataProvider)
|
||||||
{
|
{
|
||||||
if (containerType != null && !string.IsNullOrEmpty(propertyName))
|
if (containerType != null && !string.IsNullOrEmpty(propertyName))
|
||||||
{
|
{
|
||||||
return metadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
|
var metadata =
|
||||||
|
metadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
|
||||||
|
if (metadata != null)
|
||||||
|
{
|
||||||
|
metadata.Container = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
return metadataProvider.GetMetadataForType(modelAccessor, modelType);
|
return metadataProvider.GetMetadataForType(modelAccessor, modelType);
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
modelAccessor = () => propertyInfo.PropertyHelper.GetValue(container);
|
modelAccessor = () => propertyInfo.PropertyHelper.GetValue(container);
|
||||||
}
|
}
|
||||||
yield return CreatePropertyMetadata(modelAccessor, propertyInfo);
|
var propertyMetadata = CreatePropertyMetadata(modelAccessor, propertyInfo);
|
||||||
|
if (propertyMetadata != null)
|
||||||
|
{
|
||||||
|
propertyMetadata.Container = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return propertyMetadata;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IBinderMetadata BinderMetadata { get; set; }
|
public IBinderMetadata BinderMetadata { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A reference to the model's container <see cref="object"/>.
|
||||||
|
/// Will be non-<c>null</c> if the model represents a property.
|
||||||
|
/// </summary>
|
||||||
|
public object Container { get; set; }
|
||||||
|
|
||||||
public Type ContainerType
|
public Type ContainerType
|
||||||
{
|
{
|
||||||
get { return _containerType; }
|
get { return _containerType; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
||||||
|
{
|
||||||
|
public class ExpressionMetadataProviderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void FromLambaExpression_SetsContainerAsExpected()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var myModel = new TestModel { SelectedCategory = new Category() };
|
||||||
|
var provider = new EmptyModelMetadataProvider();
|
||||||
|
var viewData = new ViewDataDictionary<TestModel>(provider);
|
||||||
|
viewData.Model = myModel;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var metadata = ExpressionMetadataProvider.FromLambdaExpression<TestModel,Category>(
|
||||||
|
model => model.SelectedCategory,
|
||||||
|
viewData,
|
||||||
|
provider);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(myModel, metadata.Container);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromStringExpression_SetsContainerAsExpected()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var myModel = new TestModel { SelectedCategory = new Category() };
|
||||||
|
var provider = new EmptyModelMetadataProvider();
|
||||||
|
var viewData = new ViewDataDictionary<TestModel>(provider);
|
||||||
|
viewData["Object"] = myModel;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var metadata = ExpressionMetadataProvider.FromStringExpression("Object.SelectedCategory",
|
||||||
|
viewData,
|
||||||
|
provider);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(myModel, metadata.Container);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestModel
|
||||||
|
{
|
||||||
|
public Category SelectedCategory { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Category
|
||||||
|
{
|
||||||
|
public int CategoryId { get; set; }
|
||||||
|
public string CategoryName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -199,6 +199,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetMetadataForProperties_SetsContainerAsExpected()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new PropertyModel { LocalAttributes = 42, MetadataAttributes = "hello", MixedAttributes = 21.12 };
|
||||||
|
var provider = new EmptyModelMetadataProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var metadata = provider.GetMetadataForProperties(model, typeof(PropertyModel)).ToList();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(3, metadata.Count);
|
||||||
|
Assert.Same(model, metadata[0].Container);
|
||||||
|
Assert.Same(model, metadata[1].Container);
|
||||||
|
Assert.Same(model, metadata[2].Container);
|
||||||
|
}
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
// TODO: This type used System.ComponentModel.MetadataType to separate attribute declaration from property
|
// TODO: This type used System.ComponentModel.MetadataType to separate attribute declaration from property
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
var emptycontainerModel = new DummyModelContainer();
|
||||||
|
var contactModel = new DummyContactModel { FirstName = "test" };
|
||||||
|
var nonEmptycontainerModel = new DummyModelContainer { Model = contactModel };
|
||||||
|
|
||||||
return new TheoryData<Action<ModelMetadata>, Func<ModelMetadata, object>, object>
|
return new TheoryData<Action<ModelMetadata>, Func<ModelMetadata, object>, object>
|
||||||
{
|
{
|
||||||
{ m => m.ConvertEmptyStringToNull = false, m => m.ConvertEmptyStringToNull, false },
|
{ m => m.ConvertEmptyStringToNull = false, m => m.ConvertEmptyStringToNull, false },
|
||||||
|
|
@ -38,6 +42,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{ m => m.TemplateHint = "New template hint", m => m.TemplateHint, "New template hint" },
|
{ m => m.TemplateHint = "New template hint", m => m.TemplateHint, "New template hint" },
|
||||||
|
|
||||||
{ m => m.Order = 23, m => m.Order, 23 },
|
{ m => m.Order = 23, m => m.Order, 23 },
|
||||||
|
{ m => m.Container = null, m => m.Container, null },
|
||||||
|
{ m => m.Container = emptycontainerModel, m => m.Container, emptycontainerModel },
|
||||||
|
{ m => m.Container = nonEmptycontainerModel, m => m.Container, nonEmptycontainerModel },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,6 +63,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(typeof(Exception), metadata.ContainerType);
|
Assert.Equal(typeof(Exception), metadata.ContainerType);
|
||||||
|
Assert.Null(metadata.Container);
|
||||||
|
|
||||||
Assert.True(metadata.ConvertEmptyStringToNull);
|
Assert.True(metadata.ConvertEmptyStringToNull);
|
||||||
Assert.False(metadata.HasNonDefaultEditFormat);
|
Assert.False(metadata.HasNonDefaultEditFormat);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue