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);
        }
예제 #15
0
        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));
        }