Fix #298 - Added ModelMetadata.Container property and unit tests.

This commit is contained in:
Kirthi Krishnamraju 2014-11-18 17:03:20 -08:00
parent 8a668eb9d1
commit a0629f4d23
6 changed files with 121 additions and 7 deletions

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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; }

View File

@ -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; }
}
}
}

View File

@ -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

View File

@ -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);