// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.ModelBinding; using ModelBindingWebSite.Models; namespace ModelBindingWebSite.Controllers { public class TryUpdateModelController : Controller { public async Task GetPerson() { // Person has a property of type Person. Only Top level should be updated. var person = new Person(); await TryUpdateModelAsync( person, prefix: string.Empty, includeExpressions: m => m.Parent); return person; } public async Task GetUserAsync_IncludeAllByDefault(int id) { var user = GetUser(id); await TryUpdateModelAsync(user, prefix: string.Empty); return user; } public async Task GetUserAsync_ExcludeSpecificProperties(int id) { var user = GetUser(id); await TryUpdateModelAsync( user, prefix: string.Empty, predicate: (context, modelName) => !string.Equals(modelName, nameof(Models.User.Id), StringComparison.Ordinal) && !string.Equals(modelName, nameof(Models.User.Key), StringComparison.Ordinal)); return user; } public async Task CreateAndUpdateUser() { // don't update the id. var user = new User(); return await TryUpdateModelAsync(user, prefix: string.Empty, includeExpressions: model => model.RegisterationMonth); } public async Task GetUserAsync_IncludeSpecificProperties(int id) { var user = GetUser(id); await TryUpdateModelAsync(user, prefix: string.Empty, includeExpressions: model => model.RegisterationMonth); return user; } public async Task TryUpdateModelFails(int id) { var user = GetUser(id); return await TryUpdateModelAsync(user, prefix: string.Empty, valueProvider: new CustomValueProvider()); } public async Task GetUserAsync_IncludeAndExcludeListNull(int id) { var user = GetUser(id); await TryUpdateModelAsync(user); return user; } public async Task GetUserAsync_IncludesAllSubProperties(int id) { var user = GetUser(id); await TryUpdateModelAsync(user, prefix: string.Empty, includeExpressions: model => model.Address); return user; } public async Task GetUserAsync_WithChainedProperties(int id) { var user = GetUser(id); // Since this is a chained expression this would throw await TryUpdateModelAsync(user, prefix: string.Empty, includeExpressions: model => model.Address.Country); return user; } public async Task GetEmployeeAsync_BindToBaseDeclaredType() { var backingStore = new ReadableStringCollection( new Dictionary { { "Parent.Name", new[] { "fatherName"} }, { "Parent.Parent.Name", new[] {"grandFatherName" } }, { "Department", new[] {"Sales" } } }); Person employee = new Employee(); await TryUpdateModelAsync( employee, employee.GetType(), prefix: string.Empty, valueProvider: new ReadableStringCollectionValueProvider( BindingSource.Query, backingStore, CultureInfo.CurrentCulture), predicate: (content, propertyName) => true); return (Employee)employee; } public async Task GetUserAsync_ModelType_IncludeAll(int id) { var backingStore = new ReadableStringCollection( new Dictionary { { "Key", new[] { "123"} }, { "RegisterationMonth", new[] {"March" } }, { "UserName", new[] {"SomeName" } } }); var user = GetUser(id); await TryUpdateModelAsync(user, typeof(User), prefix: string.Empty, valueProvider: new ReadableStringCollectionValueProvider( BindingSource.Query, backingStore, CultureInfo.CurrentCulture), predicate: (content, propertyName) => true); return user; } public async Task GetUserAsync_ModelType_IncludeAllByDefault(int id) { var user = GetUser(id); await TryUpdateModelAsync(user, user.GetType(), prefix: string.Empty); return user; } public async Task TryUpdateModel_ClearsModelStateEntries() { var result = new ObjectResult(null); // Invalid model. var model = new MyModel { Id = 1, Price = -1 }; // Validate model first and subsequent TryUpdateModel should remove //modelstate entries for model and re-validate. TryValidateModel(model); // Update Name to a valid value and call TryUpdateModel model.Price = 1; await TryUpdateModelAsync(model); if (ModelState.IsValid) { result.StatusCode = StatusCodes.Status204NoContent; } else { result.StatusCode = StatusCodes.Status500InternalServerError; } return result; } private User GetUser(int id) { return new User { UserName = "User_" + id, Id = id, Key = id + 20, }; } private class MyModel { public int Id { get; set; } [Range(0,10)] public double Price { get; set; } } public class CustomValueProvider : IValueProvider { public Task ContainsPrefixAsync(string prefix) { return Task.FromResult(false); } public Task GetValueAsync(string key) { return Task.FromResult(ValueProviderResult.None); } } } }