Fill out Rendering.Expressions II
Copy from: - some `static` `ModelMetadata` methods -> `ExpressionMetadataProvider` - `TryGetValueDelegate` -> `TryGetValueDelegate` - `TypeHelpers.CreateTryGetValueDelegate()`, related bits -> `TryGetValueProvider` - `ViewDataDictionary.ViewDataEvaluator` inner class -> `ViewDataEvaluator` - `ViewDataInfo` -> `ViewDataInfo` - `ViewDataDictionary.Eval()`, related bits -> add to `ViewDataDictionary` Change to fit in new world: - usual stuff: `var`, `[NotNull]`, String -> string, namespaces, etc. - PropertyDescriptor -> PropertyInfo - update Reflection use - no `IModelMetadata.Container` property - improve a couple of variable and parameter names - make `ViewDataInfo` immutable - make `ViewDataDictionary.FormatValueInternal` `public` and -> `FormatValue` - remove `[SuppressMessage]` attributes
This commit is contained in:
parent
87c2041a52
commit
a1a180d4d0
|
|
@ -0,0 +1,142 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
||||||
|
{
|
||||||
|
public static class ExpressionMetadataProvider
|
||||||
|
{
|
||||||
|
public static ModelMetadata FromLambdaExpression<TParameter, TValue>(
|
||||||
|
[NotNull] Expression<Func<TParameter, TValue>> expression,
|
||||||
|
[NotNull] ViewDataDictionary<TParameter> viewData,
|
||||||
|
IModelMetadataProvider metadataProvider)
|
||||||
|
{
|
||||||
|
string propertyName = null;
|
||||||
|
Type containerType = null;
|
||||||
|
var legalExpression = false;
|
||||||
|
|
||||||
|
// Need to verify the expression is valid; it needs to at least end in something
|
||||||
|
// that we can convert to a meaningful string for model binding purposes
|
||||||
|
|
||||||
|
switch (expression.Body.NodeType)
|
||||||
|
{
|
||||||
|
case ExpressionType.ArrayIndex:
|
||||||
|
// ArrayIndex always means a single-dimensional indexer;
|
||||||
|
// multi-dimensional indexer is a method call to Get().
|
||||||
|
legalExpression = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExpressionType.Call:
|
||||||
|
// Only legal method call is a single argument indexer/DefaultMember call
|
||||||
|
legalExpression = ExpressionHelper.IsSingleArgumentIndexer(expression.Body);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExpressionType.MemberAccess:
|
||||||
|
// Property/field access is always legal
|
||||||
|
var memberExpression = (MemberExpression)expression.Body;
|
||||||
|
propertyName = memberExpression.Member is PropertyInfo ? memberExpression.Member.Name : null;
|
||||||
|
containerType = memberExpression.Expression.Type;
|
||||||
|
legalExpression = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExpressionType.Parameter:
|
||||||
|
// Parameter expression means "model => model", so we delegate to FromModel
|
||||||
|
return FromModel(viewData, metadataProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!legalExpression)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(Resources.TemplateHelpers_TemplateLimitations);
|
||||||
|
}
|
||||||
|
|
||||||
|
var container = viewData.Model;
|
||||||
|
Func<object> modelAccessor = () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return CachedExpressionCompiler.Process(expression)(container);
|
||||||
|
}
|
||||||
|
catch (NullReferenceException)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return GetMetadataFromProvider(modelAccessor, typeof(TValue), propertyName, containerType, metadataProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ModelMetadata FromStringExpression([NotNull] string expression,
|
||||||
|
[NotNull] ViewDataDictionary viewData,
|
||||||
|
IModelMetadataProvider metadataProvider)
|
||||||
|
{
|
||||||
|
if (expression.Length == 0)
|
||||||
|
{
|
||||||
|
// Empty string really means "model metadata for the current model"
|
||||||
|
return FromModel(viewData, metadataProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewDataInfo = ViewDataEvaluator.Eval(viewData, expression);
|
||||||
|
Type containerType = null;
|
||||||
|
Type modelType = null;
|
||||||
|
Func<object> modelAccessor = null;
|
||||||
|
string propertyName = null;
|
||||||
|
|
||||||
|
if (viewDataInfo != null)
|
||||||
|
{
|
||||||
|
if (viewDataInfo.Container != null)
|
||||||
|
{
|
||||||
|
containerType = viewDataInfo.Container.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
modelAccessor = () => viewDataInfo.Value;
|
||||||
|
|
||||||
|
if (viewDataInfo.PropertyInfo != null)
|
||||||
|
{
|
||||||
|
propertyName = viewDataInfo.PropertyInfo.Name;
|
||||||
|
modelType = viewDataInfo.PropertyInfo.PropertyType;
|
||||||
|
}
|
||||||
|
else if (viewDataInfo.Value != null)
|
||||||
|
{
|
||||||
|
// We only need to delay accessing properties (for LINQ to SQL)
|
||||||
|
modelType = viewDataInfo.Value.GetType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (viewData.ModelMetadata != null)
|
||||||
|
{
|
||||||
|
// Try getting a property from ModelMetadata if we couldn't find an answer in ViewData
|
||||||
|
var propertyMetadata =
|
||||||
|
viewData.ModelMetadata.Properties.Where(p => p.PropertyName == expression).FirstOrDefault();
|
||||||
|
if (propertyMetadata != null)
|
||||||
|
{
|
||||||
|
return propertyMetadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetMetadataFromProvider(modelAccessor, modelType ?? typeof(string), propertyName, containerType,
|
||||||
|
metadataProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModelMetadata FromModel([NotNull] ViewDataDictionary viewData,
|
||||||
|
IModelMetadataProvider metadataProvider)
|
||||||
|
{
|
||||||
|
return viewData.ModelMetadata ?? GetMetadataFromProvider(null, typeof(string), propertyName: null,
|
||||||
|
containerType: null, metadataProvider: metadataProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
if (containerType != null && !string.IsNullOrEmpty(propertyName))
|
||||||
|
{
|
||||||
|
return metadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadataProvider.GetMetadataForType(modelAccessor, modelType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
||||||
|
{
|
||||||
|
public delegate bool TryGetValueDelegate(object dictionary, string key, out object value);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
||||||
|
{
|
||||||
|
public static class TryGetValueProvider
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<Type, TryGetValueDelegate> _tryGetValueDelegateCache =
|
||||||
|
new Dictionary<Type, TryGetValueDelegate>();
|
||||||
|
private static readonly ReaderWriterLockSlim _tryGetValueDelegateCacheLock = new ReaderWriterLockSlim();
|
||||||
|
|
||||||
|
// Information about private static method declared below.
|
||||||
|
private static readonly MethodInfo _strongTryGetValueImplInfo =
|
||||||
|
typeof(TryGetValueProvider).GetTypeInfo().GetDeclaredMethod("StrongTryGetValueImpl");
|
||||||
|
|
||||||
|
public static TryGetValueDelegate CreateInstance([NotNull] Type targetType)
|
||||||
|
{
|
||||||
|
TryGetValueDelegate result;
|
||||||
|
|
||||||
|
// Cache delegates since properties of model types are re-evaluated numerous times.
|
||||||
|
_tryGetValueDelegateCacheLock.EnterReadLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_tryGetValueDelegateCache.TryGetValue(targetType, out result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_tryGetValueDelegateCacheLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
var dictionaryType = targetType.ExtractGenericInterface(typeof(IDictionary<,>));
|
||||||
|
|
||||||
|
// Just wrap a call to the underlying IDictionary<TKey, TValue>.TryGetValue() where string can be cast to TKey.
|
||||||
|
if (dictionaryType != null)
|
||||||
|
{
|
||||||
|
var typeArguments = dictionaryType.GetGenericArguments();
|
||||||
|
var keyType = typeArguments[0];
|
||||||
|
var returnType = typeArguments[1];
|
||||||
|
|
||||||
|
if (keyType.IsAssignableFrom(typeof(string)))
|
||||||
|
{
|
||||||
|
var implementationMethod = _strongTryGetValueImplInfo.MakeGenericMethod(keyType, returnType);
|
||||||
|
result = (TryGetValueDelegate)implementationMethod.CreateDelegate(typeof(TryGetValueDelegate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap a call to the underlying IDictionary.Item().
|
||||||
|
if (result == null && typeof(IDictionary).IsAssignableFrom(targetType))
|
||||||
|
{
|
||||||
|
result = TryGetValueFromNonGenericDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
_tryGetValueDelegateCacheLock.EnterWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_tryGetValueDelegateCache[targetType] = result;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_tryGetValueDelegateCacheLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool StrongTryGetValueImpl<TKey, TValue>(object dictionary, string key, out object value)
|
||||||
|
{
|
||||||
|
var strongDict = (IDictionary<TKey, TValue>)dictionary;
|
||||||
|
|
||||||
|
TValue strongValue;
|
||||||
|
var success = strongDict.TryGetValue((TKey)(object)key, out strongValue);
|
||||||
|
value = strongValue;
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetValueFromNonGenericDictionary(object dictionary, string key, out object value)
|
||||||
|
{
|
||||||
|
var weakDict = (IDictionary)dictionary;
|
||||||
|
|
||||||
|
var success = weakDict.Contains(key);
|
||||||
|
value = success ? weakDict[key] : null;
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
||||||
|
{
|
||||||
|
public static class ViewDataEvaluator
|
||||||
|
{
|
||||||
|
public static ViewDataInfo Eval([NotNull] ViewDataDictionary viewData, [NotNull] string expression)
|
||||||
|
{
|
||||||
|
// Given an expression "one.two.three.four" we look up the following (pseudocode):
|
||||||
|
// this["one.two.three.four"]
|
||||||
|
// this["one.two.three"]["four"]
|
||||||
|
// this["one.two"]["three.four]
|
||||||
|
// this["one.two"]["three"]["four"]
|
||||||
|
// this["one"]["two.three.four"]
|
||||||
|
// this["one"]["two.three"]["four"]
|
||||||
|
// this["one"]["two"]["three.four"]
|
||||||
|
// this["one"]["two"]["three"]["four"]
|
||||||
|
|
||||||
|
return EvalComplexExpression(viewData, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ViewDataInfo EvalComplexExpression(object indexableObject, string expression)
|
||||||
|
{
|
||||||
|
foreach (var expressionPair in GetRightToLeftExpressions(expression))
|
||||||
|
{
|
||||||
|
var subExpression = expressionPair.Left;
|
||||||
|
var postExpression = expressionPair.Right;
|
||||||
|
|
||||||
|
var subTargetInfo = GetPropertyValue(indexableObject, subExpression);
|
||||||
|
if (subTargetInfo != null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(postExpression))
|
||||||
|
{
|
||||||
|
return subTargetInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subTargetInfo.Value != null)
|
||||||
|
{
|
||||||
|
var potential = EvalComplexExpression(subTargetInfo.Value, postExpression);
|
||||||
|
if (potential != null)
|
||||||
|
{
|
||||||
|
return potential;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<ExpressionPair> GetRightToLeftExpressions(string expression)
|
||||||
|
{
|
||||||
|
// Produces an enumeration of all the combinations of complex property names
|
||||||
|
// given a complex expression. See the list above for an example of the result
|
||||||
|
// of the enumeration.
|
||||||
|
|
||||||
|
yield return new ExpressionPair(expression, string.Empty);
|
||||||
|
|
||||||
|
var lastDot = expression.LastIndexOf('.');
|
||||||
|
|
||||||
|
var subExpression = expression;
|
||||||
|
var postExpression = string.Empty;
|
||||||
|
|
||||||
|
while (lastDot > -1)
|
||||||
|
{
|
||||||
|
subExpression = expression.Substring(0, lastDot);
|
||||||
|
postExpression = expression.Substring(lastDot + 1);
|
||||||
|
yield return new ExpressionPair(subExpression, postExpression);
|
||||||
|
|
||||||
|
lastDot = subExpression.LastIndexOf('.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ViewDataInfo GetIndexedPropertyValue(object indexableObject, string key)
|
||||||
|
{
|
||||||
|
var dict = indexableObject as IDictionary<string, object>;
|
||||||
|
object value = null;
|
||||||
|
var success = false;
|
||||||
|
|
||||||
|
if (dict != null)
|
||||||
|
{
|
||||||
|
success = dict.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fall back to TryGetValue() calls for other Dictionary types.
|
||||||
|
var tryDelegate = TryGetValueProvider.CreateInstance(indexableObject.GetType());
|
||||||
|
if (tryDelegate != null)
|
||||||
|
{
|
||||||
|
success = tryDelegate(indexableObject, key, out value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
return new ViewDataInfo(indexableObject, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ViewDataInfo GetPropertyValue(object container, string propertyName)
|
||||||
|
{
|
||||||
|
// This method handles one "segment" of a complex property expression
|
||||||
|
|
||||||
|
// First, we try to evaluate the property based on its indexer
|
||||||
|
var value = GetIndexedPropertyValue(container, propertyName);
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the indexer didn't return anything useful, continue...
|
||||||
|
|
||||||
|
// If the container is a ViewDataDictionary then treat its Model property
|
||||||
|
// as the container instead of the ViewDataDictionary itself.
|
||||||
|
var viewData = container as ViewDataDictionary;
|
||||||
|
if (viewData != null)
|
||||||
|
{
|
||||||
|
container = viewData.Model;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the container is null, we're out of options
|
||||||
|
if (container == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally try to use PropertyInfo and treat the expression as a property name
|
||||||
|
var propertyInfo = container.GetType().GetRuntimeProperty(propertyName);
|
||||||
|
if (propertyInfo == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ViewDataInfo(container, propertyInfo, () => propertyInfo.GetValue(container));
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct ExpressionPair
|
||||||
|
{
|
||||||
|
public readonly string Left;
|
||||||
|
public readonly string Right;
|
||||||
|
|
||||||
|
public ExpressionPair(string left, string right)
|
||||||
|
{
|
||||||
|
Left = left;
|
||||||
|
Right = right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Rendering.Expressions
|
||||||
|
{
|
||||||
|
public class ViewDataInfo
|
||||||
|
{
|
||||||
|
private object _value;
|
||||||
|
private Func<object> _valueAccessor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ViewDataInfo"/> class with info about a
|
||||||
|
/// <see cref="ViewDataDictionary"/> lookup which has already been evaluated.
|
||||||
|
/// </summary>
|
||||||
|
public ViewDataInfo(object container, object value)
|
||||||
|
{
|
||||||
|
Container = container;
|
||||||
|
_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ViewDataInfo"/> class with info about a
|
||||||
|
/// <see cref="ViewDataDictionary"/> lookup which is evaluated when <see cref="Value"/> is read.
|
||||||
|
/// </summary>
|
||||||
|
public ViewDataInfo(object container, PropertyInfo propertyInfo, Func<object> valueAccessor)
|
||||||
|
{
|
||||||
|
Container = container;
|
||||||
|
PropertyInfo = propertyInfo;
|
||||||
|
_valueAccessor = valueAccessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Container { get; private set; }
|
||||||
|
|
||||||
|
public PropertyInfo PropertyInfo { get; private set; }
|
||||||
|
|
||||||
|
public object Value
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_valueAccessor != null)
|
||||||
|
{
|
||||||
|
_value = _valueAccessor();
|
||||||
|
_valueAccessor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
_valueAccessor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -138,6 +138,22 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
return GetString("HtmlHelper_NotContextualized");
|
return GetString("HtmlHelper_NotContextualized");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
|
||||||
|
/// </summary>
|
||||||
|
internal static string TemplateHelpers_TemplateLimitations
|
||||||
|
{
|
||||||
|
get { return GetString("TemplateHelpers_TemplateLimitations"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatTemplateHelpers_TemplateLimitations()
|
||||||
|
{
|
||||||
|
return GetString("TemplateHelpers_TemplateLimitations");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The model item passed is null, but this ViewDataDictionary instance requires a non-null model item of type '{0}'.
|
/// The model item passed is null, but this ViewDataDictionary instance requires a non-null model item of type '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,9 @@
|
||||||
<data name="HtmlHelper_NotContextualized" xml:space="preserve">
|
<data name="HtmlHelper_NotContextualized" xml:space="preserve">
|
||||||
<value>Must call 'Contextualize' method before using this HtmlHelper instance.</value>
|
<value>Must call 'Contextualize' method before using this HtmlHelper instance.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TemplateHelpers_TemplateLimitations" xml:space="preserve">
|
||||||
|
<value>Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.</value>
|
||||||
|
</data>
|
||||||
<data name="ViewData_ModelCannotBeNull" xml:space="preserve">
|
<data name="ViewData_ModelCannotBeNull" xml:space="preserve">
|
||||||
<value>The model item passed is null, but this ViewDataDictionary instance requires a non-null model item of type '{0}'.</value>
|
<value>The model item passed is null, but this ViewDataDictionary instance requires a non-null model item of type '{0}'.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Mvc.Rendering.Expressions;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Rendering
|
namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
{
|
{
|
||||||
|
|
@ -124,6 +126,45 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public object Eval(string expression)
|
||||||
|
{
|
||||||
|
var info = GetViewDataInfo(expression);
|
||||||
|
return (info != null) ? info.Value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Eval(string expression, string format)
|
||||||
|
{
|
||||||
|
var value = Eval(expression);
|
||||||
|
return FormatValue(value, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatValue(object value, string format)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(format))
|
||||||
|
{
|
||||||
|
return Convert.ToString(value, CultureInfo.CurrentCulture);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, format, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ViewDataInfo GetViewDataInfo(string expression)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(expression))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(Resources.ArgumentNullOrEmpty, "expression");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ViewDataEvaluator.Eval(this, expression);
|
||||||
|
}
|
||||||
|
|
||||||
// This method will execute before the derived type's instance constructor executes. Derived types must
|
// This method will execute before the derived type's instance constructor executes. Derived types must
|
||||||
// be aware of this and should plan accordingly. For example, the logic in SetModel() should be simple
|
// be aware of this and should plan accordingly. For example, the logic in SetModel() should be simple
|
||||||
// enough so as not to depend on the "this" pointer referencing a fully constructed object.
|
// enough so as not to depend on the "this" pointer referencing a fully constructed object.
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
"System.Runtime": "4.0.20.0",
|
"System.Runtime": "4.0.20.0",
|
||||||
"System.Runtime.Extensions": "4.0.10.0",
|
"System.Runtime.Extensions": "4.0.10.0",
|
||||||
"System.Runtime.InteropServices": "4.0.20.0",
|
"System.Runtime.InteropServices": "4.0.20.0",
|
||||||
|
"System.Threading": "4.0.0.0",
|
||||||
"System.Threading.Tasks": "4.0.10.0"
|
"System.Threading.Tasks": "4.0.10.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue