public void Process_ChainedMemberAccess_OfValueTypes_WithNullValuedNullable_ReturningValueType() { // Arrange var model = new ValueType1 { NullableValueType2 = null }; var expression = GetExpression <ValueType1, DateTime>(m => m.NullableValueType2.Value.Date); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.NotNull(func); var result = func(model); Assert.Null(result); }
public void Process_ComplexChainedMemberAccess_ReturnsNull() { // Arrange var expected = "SomeName"; var model = new TestModel { DifferentModels = new[] { new DifferentModel { Name = expected } } }; var expression = GetTestModelExpression(m => m.DifferentModels[0].Name); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.Null(func); }
public void Process_SimpleMemberAccess_OnValueTypeWithBadEqualityComparer() { // Arrange var model = new BadEqualityValueTypeModel { Id = 7 }; var expression = GetExpression <BadEqualityValueTypeModel, int>(m => m.Id); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.NotNull(func); var result = func(model); Assert.Equal(7, result); }
public void Process_ChainedMemberAccess_ToReferenceType_WithNullIntermediary() { // Arrange var model = new TestModel { DifferentModel = null }; var expression = GetTestModelExpression(m => m.DifferentModel.Name); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.NotNull(func); var result = func(model); Assert.Null(result); }
public void Process_SimpleMemberAccess_ToPrimitive() { // Arrange var model = new TestModel { Age = 12 }; var expression = GetTestModelExpression(m => m.Age); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.NotNull(func); var result = func(model); Assert.Equal(12, result); }
public void Process_SimpleMemberAccess() { // Arrange var model = new TestModel { Name = "Test" }; var expression = GetTestModelExpression(m => m.Name); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.NotNull(func); var result = func(model); Assert.Equal("Test", result); }
public void Process_StaticPropertyAccess_WithNullModel() { // Arrange var expected = "TestValue"; TestModel.StaticProperty = expected; var expression = GetTestModelExpression(m => TestModel.StaticProperty); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.NotNull(func); var result = func(null); Assert.Equal(expected, result); }
public void Process_ChainedMemberAccess_ToValueType() { // Arrange var dateTime = new DateTime(2000, 1, 1); var model = new TestModel { Date = dateTime }; var expression = GetTestModelExpression(m => m.Date.Year); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.NotNull(func); var result = func(model); Assert.Equal(2000, result); }
public void Process_ChainedMemberAccess_LongChain_WithNullValueTypeAccessor() { // Arrange // Chain2 is a value type var model = new Chain0Model { Chain1 = null }; var expression = GetExpression <Chain0Model, string>(m => m.Chain1.ValueTypeModel.TestModel.DifferentModel.Name); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.NotNull(func); var result = func(model); Assert.Null(result); }
public void Process_ChainedMemberAccess_ToReferenceType() { // Arrange var expected = "Test1"; var model = new TestModel { DifferentModel = new DifferentModel { Name = expected } }; var expression = GetTestModelExpression(m => m.DifferentModel.Name); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.NotNull(func); var result = func(model); Assert.Equal(expected, result); }
public void Process_ChainedMemberAccess_OfValueTypes_IncludingNullableType() { // Arrange var expected = "TestName"; var model = new ValueType1 { NullableValueType2 = new ValueType2 { Name = expected }, }; var expression = GetExpression <ValueType1, string>(m => m.NullableValueType2.Value.Name); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.NotNull(func); var result = func(model); Assert.Equal(expected, result); }
public void Process_ChainedMemberAccess_OfValueTypes_ReturningValueType() { // Arrange var expected = new DateTime(2001, 1, 1); var model = new ValueType1 { ValueType2 = new ValueType2 { Date = expected }, }; var expression = GetExpression <ValueType1, DateTime>(m => m.ValueType2.Date); // Act var func = CachedExpressionCompiler.Process(expression); // Assert Assert.NotNull(func); var result = func(model); Assert.Equal(expected, result); }
public void Process_ConstLookup_UsingCachedValue() { // Arrange var model = new TestModel(); var differentModel = new DifferentModel(); var expression1 = GetTestModelExpression(m => differentModel); var expression2 = GetTestModelExpression(m => differentModel); // Act - 1 var func1 = CachedExpressionCompiler.Process(expression1); // Assert - 1 var result1 = func1(null); Assert.Same(differentModel, result1); // Act - 2 var func2 = CachedExpressionCompiler.Process(expression2); // Assert - 2 var result2 = func1(null); Assert.Same(differentModel, result2); }
public void Process_ConstLookup_WhenCapturedLocalChanges() { // Arrange var model = new TestModel(); var differentModel = new DifferentModel(); var expression = GetTestModelExpression(m => differentModel); // Act var func = CachedExpressionCompiler.Process(expression); // Assert - 1 var result1 = func(null); Assert.Same(differentModel, result1); // Act - 2 differentModel = new DifferentModel(); // Assert - 2 var result2 = func(null); Assert.NotSame(differentModel, result1); Assert.Same(differentModel, result2); }
public static ModelExplorer FromLambdaExpression <TModel, TResult>( Expression <Func <TModel, TResult> > expression, ViewDataDictionary <TModel> viewData, IModelMetadataProvider metadataProvider) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (viewData == null) { throw new ArgumentNullException(nameof(viewData)); } 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; if (string.Equals(propertyName, "Model", StringComparison.Ordinal) && memberExpression.Type == typeof(TModel) && memberExpression.Expression.NodeType == ExpressionType.Constant) { // Special case the Model property in RazorPage<TModel>. (m => Model) should behave identically // to (m => m). But do the more complicated thing for (m => m.Model) since that is a slightly // different beast.) return(FromModel(viewData, metadataProvider)); } // memberExpression.Expression can be null when this is a static field or property. // // This can be the case if the expression is like (m => Person.Name) where Name is a static field // or property on the Person type. 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); } object modelAccessor(object container) { var model = (TModel)container; var cachedFunc = CachedExpressionCompiler.Process(expression); if (cachedFunc != null) { return(cachedFunc(model)); } var func = expression.Compile(); try { return(func(model)); } catch (NullReferenceException) { return(null); } } ModelMetadata metadata = null; if (containerType != null && propertyName != null) { // Ex: // m => m.Color (simple property access) // m => m.Color.Red (nested property access) // m => m.Widgets[0].Size (expression ending with property-access) metadata = metadataProvider.GetMetadataForType(containerType).Properties[propertyName]; } if (metadata == null) { // Ex: // m => 5 (arbitrary expression) // m => foo (arbitrary expression) // m => m.Widgets[0] (expression ending with non-property-access) // // This can also happen for any case where we cannot retrieve a model metadata. // This will happen for: // - fields // - statics // - non-visibility (internal/private) metadata = metadataProvider.GetMetadataForType(typeof(TResult)); Debug.Assert(metadata != null); } return(viewData.ModelExplorer.GetExplorerForExpression(metadata, modelAccessor)); }