diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CompositeValueProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CompositeValueProvider.cs
index 88a66204fa..e5eb302fe6 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CompositeValueProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CompositeValueProvider.cs
@@ -52,7 +52,26 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
}
var factories = controllerContext.ValueProviderFactories;
- var valueProviderFactoryContext = new ValueProviderFactoryContext(controllerContext);
+
+ return await CreateAsync(controllerContext, factories);
+ }
+
+ ///
+ /// Asynchronously creates a using the provided
+ /// .
+ ///
+ /// The associated with the current request.
+ /// The to be applied to the context.
+ ///
+ /// A which, when completed, asynchronously returns a
+ /// .
+ ///
+ public static async Task CreateAsync(
+ ActionContext actionContext,
+ IList factories)
+ {
+ var valueProviderFactoryContext = new ValueProviderFactoryContext(actionContext);
+
for (var i = 0; i < factories.Count; i++)
{
var factory = factories[i];
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs
index 3ad708a7b1..5b388a516b 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs
@@ -4,14 +4,19 @@
using System;
using System.Diagnostics;
using System.IO;
+using System.Linq.Expressions;
using System.Security.Claims;
using System.Text;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.AspNetCore.Mvc.ModelBinding.Internal;
+using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Mvc.Rendering;
+using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers;
@@ -23,6 +28,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
public abstract class Page : RazorPageBase, IRazorPage
{
private PageArgumentBinder _binder;
+ private IObjectModelValidator _objectValidator;
+ private IModelMetadataProvider _metadataProvider;
+ private IModelBinderFactory _modelBinderFactory;
///
/// The .
@@ -80,11 +88,61 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
}
}
+ ///
+ /// Gets the for the executing action.
+ ///
+ public RouteData RouteData
+ {
+ get
+ {
+ return PageContext.RouteData;
+ }
+ }
+
///
/// Gets the .
///
public ModelStateDictionary ModelState => PageContext?.ModelState;
+ private IObjectModelValidator ObjectValidator
+ {
+ get
+ {
+ if (_objectValidator == null)
+ {
+ _objectValidator = HttpContext?.RequestServices?.GetRequiredService();
+ }
+
+ return _objectValidator;
+ }
+ }
+
+ private IModelMetadataProvider MetadataProvider
+ {
+ get
+ {
+ if (_metadataProvider == null)
+ {
+ _metadataProvider = HttpContext?.RequestServices?.GetRequiredService();
+ }
+
+ return _metadataProvider;
+ }
+ }
+
+ private IModelBinderFactory ModelBinderFactory
+ {
+ get
+ {
+ if (_modelBinderFactory == null)
+ {
+ _modelBinderFactory = HttpContext?.RequestServices?.GetRequiredService();
+ }
+
+ return _modelBinderFactory;
+ }
+ }
+
///
public override void EnsureRenderedBodyOrSections()
{
@@ -1130,5 +1188,384 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
return new PageViewResult(this);
}
#endregion Factory methods
+
+ ///
+ /// Updates the specified instance using values from the 's current
+ /// .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// A that on completion returns true if the update is successful.
+ public virtual Task TryUpdateModelAsync(
+ TModel model)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ return TryUpdateModelAsync(model, prefix: string.Empty);
+ }
+
+ ///
+ /// Updates the specified instance using values from the 's current
+ /// and a .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// The prefix to use when looking up values in the current .
+ ///
+ /// A that on completion returns true if the update is successful.
+ public virtual async Task TryUpdateModelAsync(
+ TModel model,
+ string prefix)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (prefix == null)
+ {
+ throw new ArgumentNullException(nameof(prefix));
+ }
+ var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
+ return await TryUpdateModelAsync(model, prefix, valueProvider);
+ }
+
+ ///
+ /// Updates the specified instance using the and a
+ /// .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// The prefix to use when looking up values in the .
+ ///
+ /// The used for looking up values.
+ /// A that on completion returns true if the update is successful.
+ public virtual Task TryUpdateModelAsync(
+ TModel model,
+ string prefix,
+ IValueProvider valueProvider)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (prefix == null)
+ {
+ throw new ArgumentNullException(nameof(prefix));
+ }
+
+ if (valueProvider == null)
+ {
+ throw new ArgumentNullException(nameof(valueProvider));
+ }
+
+ return ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ prefix,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator);
+ }
+
+ ///
+ /// Updates the specified instance using values from the 's current
+ /// and a .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// The prefix to use when looking up values in the current .
+ ///
+ /// (s) which represent top-level properties
+ /// which need to be included for the current model.
+ /// A that on completion returns true if the update is successful.
+ public async Task TryUpdateModelAsync(
+ TModel model,
+ string prefix,
+ params Expression>[] includeExpressions)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (includeExpressions == null)
+ {
+ throw new ArgumentNullException(nameof(includeExpressions));
+ }
+
+ var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
+ return await ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ prefix,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator,
+ includeExpressions);
+ }
+
+ ///
+ /// Updates the specified instance using values from the 's current
+ /// and a .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// The prefix to use when looking up values in the current .
+ ///
+ /// A predicate which can be used to filter properties at runtime.
+ /// A that on completion returns true if the update is successful.
+ public async Task TryUpdateModelAsync(
+ TModel model,
+ string prefix,
+ Func propertyFilter)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (propertyFilter == null)
+ {
+ throw new ArgumentNullException(nameof(propertyFilter));
+ }
+
+ var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
+ return await ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ prefix,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator,
+ propertyFilter);
+ }
+
+ ///
+ /// Updates the specified instance using the and a
+ /// .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// The prefix to use when looking up values in the .
+ ///
+ /// The used for looking up values.
+ /// (s) which represent top-level properties
+ /// which need to be included for the current model.
+ /// A that on completion returns true if the update is successful.
+ public Task TryUpdateModelAsync(
+ TModel model,
+ string prefix,
+ IValueProvider valueProvider,
+ params Expression>[] includeExpressions)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (valueProvider == null)
+ {
+ throw new ArgumentNullException(nameof(valueProvider));
+ }
+
+ if (includeExpressions == null)
+ {
+ throw new ArgumentNullException(nameof(includeExpressions));
+ }
+
+ return ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ prefix,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator,
+ includeExpressions);
+ }
+
+ ///
+ /// Updates the specified instance using the and a
+ /// .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// The prefix to use when looking up values in the .
+ ///
+ /// The used for looking up values.
+ /// A predicate which can be used to filter properties at runtime.
+ /// A that on completion returns true if the update is successful.
+ public Task TryUpdateModelAsync(
+ TModel model,
+ string prefix,
+ IValueProvider valueProvider,
+ Func propertyFilter)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (valueProvider == null)
+ {
+ throw new ArgumentNullException(nameof(valueProvider));
+ }
+
+ if (propertyFilter == null)
+ {
+ throw new ArgumentNullException(nameof(propertyFilter));
+ }
+
+ return ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ prefix,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator,
+ propertyFilter);
+ }
+
+ ///
+ /// Updates the specified instance using values from the 's current
+ /// and a .
+ ///
+ /// The model instance to update.
+ /// The type of model instance to update.
+ /// The prefix to use when looking up values in the current .
+ ///
+ /// A that on completion returns true if the update is successful.
+ public virtual async Task TryUpdateModelAsync(
+ object model,
+ Type modelType,
+ string prefix)
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (modelType == null)
+ {
+ throw new ArgumentNullException(nameof(modelType));
+ }
+
+ var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
+ return await ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ modelType,
+ prefix,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator);
+ }
+
+ ///
+ /// Updates the specified instance using the and a
+ /// .
+ ///
+ /// The model instance to update.
+ /// The type of model instance to update.
+ /// The prefix to use when looking up values in the .
+ ///
+ /// The used for looking up values.
+ /// A predicate which can be used to filter properties at runtime.
+ /// A that on completion returns true if the update is successful.
+ public Task TryUpdateModelAsync(
+ object model,
+ Type modelType,
+ string prefix,
+ IValueProvider valueProvider,
+ Func propertyFilter)
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (modelType == null)
+ {
+ throw new ArgumentNullException(nameof(modelType));
+ }
+
+ if (valueProvider == null)
+ {
+ throw new ArgumentNullException(nameof(valueProvider));
+ }
+
+ if (propertyFilter == null)
+ {
+ throw new ArgumentNullException(nameof(propertyFilter));
+ }
+
+ return ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ modelType,
+ prefix,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator,
+ propertyFilter);
+ }
+
+ ///
+ /// Validates the specified instance.
+ ///
+ /// The model to validate.
+ /// true if the is valid; false otherwise.
+ public virtual bool TryValidateModel(
+ object model)
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ return TryValidateModel(model, prefix: null);
+ }
+
+ ///
+ /// Validates the specified instance.
+ ///
+ /// The model to validate.
+ /// The key to use when looking up information in .
+ ///
+ /// true if the is valid;false otherwise.
+ public virtual bool TryValidateModel(
+ object model,
+ string prefix)
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ ObjectValidator.Validate(
+ PageContext,
+ validationState: null,
+ prefix: prefix ?? string.Empty,
+ model: model);
+ return ModelState.IsValid;
+ }
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs
index dc8ab8b044..ffc414ca24 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs
@@ -3,16 +3,21 @@
using System;
using System.IO;
+using System.Linq.Expressions;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.AspNetCore.Mvc.ModelBinding.Internal;
+using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
+using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
+using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers;
@@ -21,6 +26,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
public abstract class PageModel
{
private PageArgumentBinder _binder;
+ private IObjectModelValidator _objectValidator;
+ private IModelMetadataProvider _metadataProvider;
+ private IModelBinderFactory _modelBinderFactory;
private IUrlHelper _urlHelper;
///
@@ -106,68 +114,405 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
///
public HttpResponse Response => HttpContext?.Response;
+ ///
+ /// Gets the for the executing action.
+ ///
+ public RouteData RouteData => PageContext.RouteData;
+
///
/// Gets the .
///
public ModelStateDictionary ModelState => PageContext.ModelState;
+ ///
+ /// Gets the for user associated with the executing action.
+ ///
+ public ClaimsPrincipal User => HttpContext?.User;
+
///
/// Gets the from the .
///
/// Returns null if is null.
public ITempDataDictionary TempData => PageContext?.TempData;
+ private IObjectModelValidator ObjectValidator
+ {
+ get
+ {
+ if (_objectValidator == null)
+ {
+ _objectValidator = HttpContext?.RequestServices?.GetRequiredService();
+ }
+
+ return _objectValidator;
+ }
+ }
+
+ private IModelMetadataProvider MetadataProvider
+ {
+ get
+ {
+ if (_metadataProvider == null)
+ {
+ _metadataProvider = HttpContext?.RequestServices?.GetRequiredService();
+ }
+
+ return _metadataProvider;
+ }
+ }
+
+ private IModelBinderFactory ModelBinderFactory
+ {
+ get
+ {
+ if (_modelBinderFactory == null)
+ {
+ _modelBinderFactory = HttpContext?.RequestServices?.GetRequiredService();
+ }
+
+ return _modelBinderFactory;
+ }
+ }
+
///
/// Gets the .
///
public ViewDataDictionary ViewData => PageContext?.ViewData;
///
- /// Binds the model with the specified .
- ///
- /// The model type.
- /// The model name.
- /// A that on completion returns the bound model.
- protected internal Task BindAsync(string name)
- {
- return Binder.BindModelAsync(PageContext, name);
- }
-
- ///
- /// Binds the model with the specified .
- ///
- /// The model type.
- /// The model name.
- /// The default model value.
- /// A that on completion returns the bound model.
- protected internal Task BindAsync(TModel @default, string name)
- {
- return Binder.BindModelAsync(PageContext, @default, name);
- }
-
- ///
- /// Updates the specified instance using values from the pageModel's current
+ /// Updates the specified instance using values from the 's current
/// .
///
/// The type of the model object.
/// The model instance to update.
/// A that on completion returns true if the update is successful.
protected internal Task TryUpdateModelAsync(TModel model)
+ where TModel : class
{
- return Binder.TryUpdateModelAsync(PageContext, model);
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ return TryUpdateModelAsync(model, name: string.Empty);
}
///
- /// Updates the specified instance using values from the pageModel's current
+ /// Updates the specified instance using values from the 's current
/// .
///
/// The type of the model object.
/// The model instance to update.
/// The model name.
/// A that on completion returns true if the update is successful.
- protected internal Task TryUpdateModelAsync(TModel model, string name)
+ protected internal async Task TryUpdateModelAsync(TModel model, string name)
+ where TModel : class
{
- return Binder.TryUpdateModelAsync(PageContext, model, name);
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (name == null)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
+ return await TryUpdateModelAsync(model, name, valueProvider);
+ }
+
+ ///
+ /// Updates the specified instance using the and a
+ /// .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// The name to use when looking up values in the .
+ ///
+ /// The used for looking up values.
+ /// A that on completion returns true if the update is successful.
+ protected internal Task TryUpdateModelAsync(
+ TModel model,
+ string name,
+ IValueProvider valueProvider)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (name == null)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ if (valueProvider == null)
+ {
+ throw new ArgumentNullException(nameof(valueProvider));
+ }
+
+ return ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ name,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator);
+ }
+
+ ///
+ /// Updates the specified instance using values from the 's current
+ /// and a .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// The name to use when looking up values in the current .
+ ///
+ /// (s) which represent top-level properties
+ /// which need to be included for the current model.
+ /// A that on completion returns true if the update is successful.
+ protected internal async Task TryUpdateModelAsync(
+ TModel model,
+ string name,
+ params Expression>[] includeExpressions)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (includeExpressions == null)
+ {
+ throw new ArgumentNullException(nameof(includeExpressions));
+ }
+
+ var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
+ return await ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ name,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator,
+ includeExpressions);
+ }
+
+ ///
+ /// Updates the specified instance using values from the 's current
+ /// and a .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// The name to use when looking up values in the current .
+ ///
+ /// A predicate which can be used to filter properties at runtime.
+ /// A that on completion returns true if the update is successful.
+ protected internal async Task TryUpdateModelAsync(
+ TModel model,
+ string name,
+ Func propertyFilter)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (propertyFilter == null)
+ {
+ throw new ArgumentNullException(nameof(propertyFilter));
+ }
+
+ var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
+ return await ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ name,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator,
+ propertyFilter);
+ }
+
+ ///
+ /// Updates the specified instance using the and a
+ /// .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// The name to use when looking up values in the .
+ ///
+ /// The used for looking up values.
+ /// (s) which represent top-level properties
+ /// which need to be included for the current model.
+ /// A that on completion returns true if the update is successful.
+ protected internal Task TryUpdateModelAsync(
+ TModel model,
+ string name,
+ IValueProvider valueProvider,
+ params Expression>[] includeExpressions)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (valueProvider == null)
+ {
+ throw new ArgumentNullException(nameof(valueProvider));
+ }
+
+ if (includeExpressions == null)
+ {
+ throw new ArgumentNullException(nameof(includeExpressions));
+ }
+
+ return ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ name,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator,
+ includeExpressions);
+ }
+
+ ///
+ /// Updates the specified instance using the and a
+ /// .
+ ///
+ /// The type of the model object.
+ /// The model instance to update.
+ /// The name to use when looking up values in the .
+ ///
+ /// The used for looking up values.
+ /// A predicate which can be used to filter properties at runtime.
+ /// A that on completion returns true if the update is successful.
+ protected internal Task TryUpdateModelAsync(
+ TModel model,
+ string name,
+ IValueProvider valueProvider,
+ Func propertyFilter)
+ where TModel : class
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (valueProvider == null)
+ {
+ throw new ArgumentNullException(nameof(valueProvider));
+ }
+
+ if (propertyFilter == null)
+ {
+ throw new ArgumentNullException(nameof(propertyFilter));
+ }
+
+ return ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ name,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator,
+ propertyFilter);
+ }
+
+ ///
+ /// Updates the specified instance using values from the 's current
+ /// and a .
+ ///
+ /// The model instance to update.
+ /// The type of model instance to update.
+ /// The name to use when looking up values in the current .
+ ///
+ /// A that on completion returns true if the update is successful.
+ protected internal async Task TryUpdateModelAsync(
+ object model,
+ Type modelType,
+ string name)
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (modelType == null)
+ {
+ throw new ArgumentNullException(nameof(modelType));
+ }
+
+ var valueProvider = await CompositeValueProvider.CreateAsync(PageContext, PageContext.ValueProviderFactories);
+ return await ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ modelType,
+ name,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator);
+ }
+
+ ///
+ /// Updates the specified instance using the and a
+ /// .
+ ///
+ /// The model instance to update.
+ /// The type of model instance to update.
+ /// The name to use when looking up values in the .
+ ///
+ /// The used for looking up values.
+ /// A predicate which can be used to filter properties at runtime.
+ /// A that on completion returns true if the update is successful.
+ protected internal Task TryUpdateModelAsync(
+ object model,
+ Type modelType,
+ string name,
+ IValueProvider valueProvider,
+ Func propertyFilter)
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ if (modelType == null)
+ {
+ throw new ArgumentNullException(nameof(modelType));
+ }
+
+ if (valueProvider == null)
+ {
+ throw new ArgumentNullException(nameof(valueProvider));
+ }
+
+ if (propertyFilter == null)
+ {
+ throw new ArgumentNullException(nameof(propertyFilter));
+ }
+
+ return ModelBindingHelper.TryUpdateModelAsync(
+ model,
+ modelType,
+ name,
+ PageContext,
+ MetadataProvider,
+ ModelBinderFactory,
+ valueProvider,
+ ObjectValidator,
+ propertyFilter);
}
#region Factory methods
@@ -1176,5 +1521,45 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
return new PageViewResult(Page);
}
#endregion Factory methods
+
+ ///
+ /// Validates the specified instance.
+ ///
+ /// The model to validate.
+ /// true if the is valid; false otherwise.
+ public virtual bool TryValidateModel(
+ object model)
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ return TryValidateModel(model, name: null);
+ }
+
+ ///
+ /// Validates the specified instance.
+ ///
+ /// The model to validate.
+ /// The key to use when looking up information in .
+ ///
+ /// true if the is valid;false otherwise.
+ public virtual bool TryValidateModel(
+ object model,
+ string name)
+ {
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ ObjectValidator.Validate(
+ PageContext,
+ validationState: null,
+ prefix: name ?? string.Empty,
+ model: model);
+ return ModelState.IsValid;
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPageModelTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPageModelTest.cs
new file mode 100644
index 0000000000..fa9ae59b8d
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPageModelTest.cs
@@ -0,0 +1,185 @@
+// 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.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.FunctionalTests
+{
+ public class RazorPageModelTest : IClassFixture>
+ {
+ public RazorPageModelTest(MvcTestFixture fixture)
+ {
+ Client = fixture.Client;
+ }
+
+ public HttpClient Client { get; }
+
+ [Fact]
+ public async Task Page_TryUpdateModelAsync_Success()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryUpdateModel/10")
+ {
+ Content = new FormUrlEncodedContent(new KeyValuePair[]
+ {
+ new KeyValuePair("Name", "Overriden"),
+ new KeyValuePair("Age", "25"),
+ })
+ };
+
+ await AddAntiforgeryHeaders(request);
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ var content = await response.Content.ReadAsStringAsync();
+ Assert.Contains("Updated: True", content);
+ Assert.Contains("Name = Overriden", content);
+ }
+
+ [Fact]
+ public async Task Page_TryValidateModel_Success()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryValidateModel/10")
+ {
+ Content = new FormUrlEncodedContent(new KeyValuePair[]
+ {
+ new KeyValuePair("Name", "Foo"),
+ new KeyValuePair("Age", "25"),
+ })
+ };
+
+ await AddAntiforgeryHeaders(request);
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ var content = await response.Content.ReadAsStringAsync();
+
+ Assert.Contains("Validation: success", content.Trim());
+ }
+
+ [Fact]
+ public async Task Page_TryValidateModel_TooLong()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryValidateModel/10")
+ {
+ Content = new FormUrlEncodedContent(new KeyValuePair[]
+ {
+ new KeyValuePair("Name", "Foo"),
+ new KeyValuePair("Age", "200"),
+ })
+ };
+
+ await AddAntiforgeryHeaders(request);
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ var content = await response.Content.ReadAsStringAsync();
+
+ Assert.Contains("Validation: fail", content);
+ Assert.Contains("The field Age must be between 0 and 99.", content);
+ }
+
+ [Fact]
+ public async Task PageModel_TryUpdateModelAsync_Success()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryUpdateModelPageModel/10")
+ {
+ Content = new FormUrlEncodedContent(new KeyValuePair[]
+ {
+ new KeyValuePair("Name", "Overriden"),
+ new KeyValuePair("Age", "25"),
+ })
+ };
+
+ await AddAntiforgeryHeaders(request);
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ var content = await response.Content.ReadAsStringAsync();
+ Assert.Contains("Updated: True", content);
+ Assert.Contains("Name = Overriden", content);
+ }
+
+ [Fact]
+ public async Task PageModel_TryValidateModel_Success()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryValidateModelPageModel/10")
+ {
+ Content = new FormUrlEncodedContent(new KeyValuePair[]
+ {
+ new KeyValuePair("Name", "Foo"),
+ new KeyValuePair("Age", "25"),
+ })
+ };
+
+ await AddAntiforgeryHeaders(request);
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ var content = await response.Content.ReadAsStringAsync();
+
+ Assert.Contains("Validation: success", content.Trim());
+ }
+
+ [Fact]
+ public async Task PageModel_TryValidateModel_TooLong()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Post, "Pages/TryValidateModelPageModel/10")
+ {
+ Content = new FormUrlEncodedContent(new KeyValuePair[]
+ {
+ new KeyValuePair("Name", "Foo"),
+ new KeyValuePair("Age", "200"),
+ })
+ };
+
+ await AddAntiforgeryHeaders(request);
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ var content = await response.Content.ReadAsStringAsync();
+
+ Assert.Contains("Validation: fail", content);
+ Assert.Contains("The field Age must be between 0 and 99.", content);
+ }
+
+ private async Task AddAntiforgeryHeaders(HttpRequestMessage request)
+ {
+ var getResponse = await Client.GetAsync(request.RequestUri);
+ Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode);
+ var getResponseBody = await getResponse.Content.ReadAsStringAsync();
+ var formToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(getResponseBody, "");
+ var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse);
+
+ request.Headers.Add("Cookie", cookie.Key + "=" + cookie.Value);
+ request.Headers.Add("RequestVerificationToken", formToken);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageModelTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageModelTest.cs
index e5019878b8..1560095f6c 100644
--- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageModelTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/PageModelTest.cs
@@ -1463,31 +1463,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
Assert.Same(urlHelper, pageModel.Url);
}
- [Fact]
- public async Task BindModel_InvokesBindOnPageArgumentBinder()
- {
- // Arrange
- var httpContext = new DefaultHttpContext();
- var binder = new TestPageArgumentBinder();
- httpContext.RequestServices = new ServiceCollection()
- .AddSingleton(binder)
- .BuildServiceProvider();
- var pageContext = new PageContext
- {
- HttpContext = httpContext,
- };
- var pageModel = new TestPageModel
- {
- PageContext = pageContext,
- };
-
- // Act
- var result = await pageModel.BindAsync("test-name");
-
- // Assert
- Assert.NotNull(result);
- }
-
[Fact]
public void Redirect_ReturnsARedirectResult()
{
diff --git a/test/WebSites/RazorPagesWebSite/Pages/TryUpdateModel.cshtml b/test/WebSites/RazorPagesWebSite/Pages/TryUpdateModel.cshtml
new file mode 100644
index 0000000000..35938102fd
--- /dev/null
+++ b/test/WebSites/RazorPagesWebSite/Pages/TryUpdateModel.cshtml
@@ -0,0 +1,27 @@
+@page "{id:int}"
+@using RazorPagesWebSite
+
+@functions
+{
+ public UserModel UserModel { get; set; }
+
+ [FromRoute]
+ public int Id { get; set; }
+
+ public async Task OnPost()
+ {
+ var user = new UserModel();
+
+ Updated = await TryUpdateModelAsync(user);
+ UserModel = user;
+ }
+
+ public bool Updated { get; set; }
+}
+
+@Html.ValidationSummary()
+
+Updated: @Updated
+Name = @UserModel?.Name
diff --git a/test/WebSites/RazorPagesWebSite/Pages/TryUpdateModelPageModel.cs b/test/WebSites/RazorPagesWebSite/Pages/TryUpdateModelPageModel.cs
new file mode 100644
index 0000000000..05f7785c58
--- /dev/null
+++ b/test/WebSites/RazorPagesWebSite/Pages/TryUpdateModelPageModel.cs
@@ -0,0 +1,22 @@
+// 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 System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace RazorPagesWebSite.Pages
+{
+ public class TryUpdateModelPageModel : PageModel
+ {
+ public UserModel UserModel { get; set; }
+
+ public bool Updated { get; set; }
+
+ public async Task OnPost()
+ {
+ var user = new UserModel();
+ Updated = await TryUpdateModelAsync(user);
+ UserModel = user;
+ }
+ }
+}
diff --git a/test/WebSites/RazorPagesWebSite/Pages/TryUpdateModelPageModel.cshtml b/test/WebSites/RazorPagesWebSite/Pages/TryUpdateModelPageModel.cshtml
new file mode 100644
index 0000000000..c4183b5a5d
--- /dev/null
+++ b/test/WebSites/RazorPagesWebSite/Pages/TryUpdateModelPageModel.cshtml
@@ -0,0 +1,10 @@
+@page "{id:int}"
+@using RazorPagesWebSite.Pages
+@model TryUpdateModelPageModel
+
+@Html.ValidationSummary()
+
+Updated: @Model.Updated
+Name = @Model.UserModel?.Name
diff --git a/test/WebSites/RazorPagesWebSite/Pages/TryValidateModel.cshtml b/test/WebSites/RazorPagesWebSite/Pages/TryValidateModel.cshtml
new file mode 100644
index 0000000000..1aaa0b637c
--- /dev/null
+++ b/test/WebSites/RazorPagesWebSite/Pages/TryValidateModel.cshtml
@@ -0,0 +1,26 @@
+@page "{id:int}"
+@using RazorPagesWebSite
+
+@functions
+{
+ [ModelBinder]
+ public UserModel UserModel { get; set; }
+
+ [FromRoute]
+ public int Id { get; set; }
+
+ public void OnPost(UserModel user)
+ {
+ Valid = TryValidateModel(user);
+
+ UserModel = user;
+ }
+
+ public bool Valid { get; set; }
+}
+
+@Html.ValidationSummary()
+
+Validation: @(Valid ? "success" : "fail!" )
diff --git a/test/WebSites/RazorPagesWebSite/Pages/TryValidateModelPageModel.cs b/test/WebSites/RazorPagesWebSite/Pages/TryValidateModelPageModel.cs
new file mode 100644
index 0000000000..bde677cb98
--- /dev/null
+++ b/test/WebSites/RazorPagesWebSite/Pages/TryValidateModelPageModel.cs
@@ -0,0 +1,21 @@
+// 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.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace RazorPagesWebSite.Pages
+{
+ public class TryValidateModelPageModel : PageModel
+ {
+ [ModelBinder]
+ public UserModel UserModel { get; set; }
+
+ public bool Validate { get; set; }
+
+ public void OnPost(UserModel user)
+ {
+ Validate = TryValidateModel(user);
+ }
+ }
+}
diff --git a/test/WebSites/RazorPagesWebSite/Pages/TryValidateModelPageModel.cshtml b/test/WebSites/RazorPagesWebSite/Pages/TryValidateModelPageModel.cshtml
new file mode 100644
index 0000000000..08d31d73d8
--- /dev/null
+++ b/test/WebSites/RazorPagesWebSite/Pages/TryValidateModelPageModel.cshtml
@@ -0,0 +1,9 @@
+@page "{id:int}"
+@using RazorPagesWebSite.Pages
+@model TryValidateModelPageModel
+
+@Html.ValidationSummary()
+
+Validation: @(Model.Validate ? "success" : "fail!" )