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.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
|
@ -73,6 +72,7 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
|||
modelAccessor,
|
||||
typeof(TValue),
|
||||
propertyName,
|
||||
container,
|
||||
containerType,
|
||||
metadataProvider);
|
||||
}
|
||||
|
|
@ -92,12 +92,14 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
|||
Type modelType = null;
|
||||
Func<object> modelAccessor = null;
|
||||
string propertyName = null;
|
||||
object container = null;
|
||||
|
||||
if (viewDataInfo != null)
|
||||
{
|
||||
if (viewDataInfo.Container != null)
|
||||
{
|
||||
containerType = viewDataInfo.Container.GetType();
|
||||
container = viewDataInfo.Container;
|
||||
}
|
||||
|
||||
modelAccessor = () => viewDataInfo.Value;
|
||||
|
|
@ -124,8 +126,12 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
|||
}
|
||||
}
|
||||
|
||||
return GetMetadataFromProvider(modelAccessor, modelType ?? typeof(string), propertyName, containerType,
|
||||
metadataProvider);
|
||||
return GetMetadataFromProvider(modelAccessor,
|
||||
modelType ?? typeof(string),
|
||||
propertyName,
|
||||
container,
|
||||
containerType,
|
||||
metadataProvider);
|
||||
}
|
||||
|
||||
private static ModelMetadata FromModel([NotNull] ViewDataDictionary viewData,
|
||||
|
|
@ -138,6 +144,7 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
|||
modelAccessor: null,
|
||||
modelType: typeof(string),
|
||||
propertyName: null,
|
||||
container: null,
|
||||
containerType: null,
|
||||
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
|
||||
// class lack [NotNull] attributes for their corresponding parameter.
|
||||
private static ModelMetadata GetMetadataFromProvider(Func<object> modelAccessor, Type modelType,
|
||||
string propertyName, Type containerType, [NotNull] IModelMetadataProvider metadataProvider)
|
||||
private static ModelMetadata GetMetadataFromProvider(Func<object> modelAccessor,
|
||||
Type modelType,
|
||||
string propertyName,
|
||||
object container,
|
||||
Type containerType,
|
||||
[NotNull] IModelMetadataProvider metadataProvider)
|
||||
{
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -97,7 +97,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
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>
|
||||
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
|
||||
{
|
||||
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
|
||||
|
||||
[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
|
||||
|
||||
// TODO: This type used System.ComponentModel.MetadataType to separate attribute declaration from property
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
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>
|
||||
{
|
||||
{ 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.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.Equal(typeof(Exception), metadata.ContainerType);
|
||||
Assert.Null(metadata.Container);
|
||||
|
||||
Assert.True(metadata.ConvertEmptyStringToNull);
|
||||
Assert.False(metadata.HasNonDefaultEditFormat);
|
||||
|
|
|
|||
Loading…
Reference in New Issue