diff --git a/src/Microsoft.AspNet.Mvc.Core/AreaReference.cs b/src/Microsoft.AspNet.Mvc.Core/AreaReference.cs
deleted file mode 100644
index c4fccb132f..0000000000
--- a/src/Microsoft.AspNet.Mvc.Core/AreaReference.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
-
-namespace System.Web.Mvc
-{
- ///
- /// Controls interpretation of a controller name when constructing a .
- ///
- public enum AreaReference
- {
- ///
- /// Find the controller in the current area.
- ///
- UseCurrent = 0,
-
- ///
- /// Find the controller in the root area.
- ///
- UseRoot = 1,
- }
-}
diff --git a/src/Microsoft.AspNet.Mvc.Core/Internal/ModelClientValidationRemoteRule.cs b/src/Microsoft.AspNet.Mvc.Core/Internal/ModelClientValidationRemoteRule.cs
index 374da299a4..272ba952d6 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Internal/ModelClientValidationRemoteRule.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Internal/ModelClientValidationRemoteRule.cs
@@ -1,26 +1,47 @@
-// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+// 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.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
+using System;
+using Microsoft.AspNet.Mvc.ModelBinding;
-namespace System.Web.Mvc
+namespace Microsoft.AspNet.Mvc.Internal
{
- [TypeForwardedFrom("System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
+ ///
+ /// containing information for HTML attribute generation in fields a
+ /// targets.
+ ///
public class ModelClientValidationRemoteRule : ModelClientValidationRule
{
- [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "The value is a not a regular URL since it may contain ~/ ASP.NET-specific characters")]
- public ModelClientValidationRemoteRule(string errorMessage, string url, string httpMethod, string additionalFields)
- {
- ErrorMessage = errorMessage;
- ValidationType = "remote";
- ValidationParameters["url"] = url;
+ private const string RemoteValidationType = "remote";
+ private const string AdditionalFieldsValidationParameter = "additionalfields";
+ private const string TypeValidationParameter = "type";
+ private const string UrlValidationParameter = "url";
- if (!String.IsNullOrEmpty(httpMethod))
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Error message client should display when validation fails.
+ /// URL where client should send a validation request.
+ ///
+ /// HTTP method ("GET" or "POST") client should use when sending a validation request.
+ ///
+ ///
+ /// Comma-separated names of fields the client should include in a validation request.
+ ///
+ public ModelClientValidationRemoteRule(
+ string errorMessage,
+ string url,
+ string httpMethod,
+ string additionalFields)
+ : base(validationType: RemoteValidationType, errorMessage: errorMessage)
+ {
+ ValidationParameters[UrlValidationParameter] = url;
+ if (!string.IsNullOrEmpty(httpMethod))
{
- ValidationParameters["type"] = httpMethod;
+ ValidationParameters[TypeValidationParameter] = httpMethod;
}
- ValidationParameters["additionalfields"] = additionalFields;
+ ValidationParameters[AdditionalFieldsValidationParameter] = additionalFields;
}
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
index 08263a8077..2ecefcaaee 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
@@ -1706,6 +1706,38 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("ApiExplorer_UnsupportedAction"), p0);
}
+ ///
+ /// No URL for remote validation could be found.
+ ///
+ internal static string RemoteAttribute_NoUrlFound
+ {
+ get { return GetString("RemoteAttribute_NoUrlFound"); }
+ }
+
+ ///
+ /// No URL for remote validation could be found.
+ ///
+ internal static string FormatRemoteAttribute_NoUrlFound()
+ {
+ return GetString("RemoteAttribute_NoUrlFound");
+ }
+
+ ///
+ /// '{0}' is invalid.
+ ///
+ internal static string RemoteAttribute_RemoteValidationFailed
+ {
+ get { return GetString("RemoteAttribute_RemoteValidationFailed"); }
+ }
+
+ ///
+ /// '{0}' is invalid.
+ ///
+ internal static string FormatRemoteAttribute_RemoteValidationFailed(object p0)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("RemoteAttribute_RemoteValidationFailed"), p0);
+ }
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.AspNet.Mvc.Core/RemoteAttribute.cs b/src/Microsoft.AspNet.Mvc.Core/RemoteAttribute.cs
index 21868ee211..e8daaf395e 100644
--- a/src/Microsoft.AspNet.Mvc.Core/RemoteAttribute.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/RemoteAttribute.cs
@@ -1,158 +1,251 @@
-// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+// 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;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
-using System.Diagnostics.CodeAnalysis;
using System.Globalization;
-using System.Web.Mvc.Properties;
-using System.Web.Routing;
+using System.Linq;
+using Microsoft.AspNet.Mvc.Core;
+using Microsoft.AspNet.Mvc.Internal;
+using Microsoft.AspNet.Mvc.ModelBinding;
+using Microsoft.AspNet.Routing;
+using Microsoft.Framework.DependencyInjection;
-namespace System.Web.Mvc
+namespace Microsoft.AspNet.Mvc
{
- [AttributeUsage(AttributeTargets.Property)]
- [SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification = "The constructor parameters are used to feed RouteData, which is public.")]
- [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "This attribute is designed to be a base class for other attributes.")]
- public class RemoteAttribute : ValidationAttribute, IClientValidatable
+ ///
+ /// A which configures Unobtrusive validation to send an Ajax request to the
+ /// web site. The invoked action should return JSON indicating whether the value is valid.
+ ///
+ /// Does no server-side validation of the final form submission.
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
+ public class RemoteAttribute : ValidationAttribute, IClientModelValidator
{
- private string _additionalFields;
- private string[] _additonalFieldsSplit = new string[0];
+ private string _additionalFields = string.Empty;
+ private string[] _additionalFieldsSplit = new string[0];
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Intended for subclasses that support URL generation with no route, action, or controller names.
+ ///
protected RemoteAttribute()
- : base(MvcResources.RemoteAttribute_RemoteValidationFailed)
+ : base(Resources.RemoteAttribute_RemoteValidationFailed)
{
RouteData = new RouteValueDictionary();
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The route name used when generating the URL where client should send a validation request.
+ ///
+ ///
+ /// Finds the in any area of the application.
+ ///
public RemoteAttribute(string routeName)
: this()
{
- if (String.IsNullOrWhiteSpace(routeName))
- {
- throw new ArgumentException(MvcResources.Common_NullOrEmpty, "routeName");
- }
-
RouteName = routeName;
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The action name used when generating the URL where client should send a validation request.
+ ///
+ ///
+ /// The controller name used when generating the URL where client should send a validation request.
+ ///
+ ///
+ ///
+ /// If either or is null, uses the corresponding
+ /// ambient value.
+ ///
+ /// Finds the in the current area.
+ ///
public RemoteAttribute(string action, string controller)
- :
- this(action, controller, null /* areaName */)
- {
- }
-
- public RemoteAttribute(string action, string controller, string areaName)
: this()
{
- if (String.IsNullOrWhiteSpace(action))
+ if (action != null)
{
- throw new ArgumentException(MvcResources.Common_NullOrEmpty, "action");
- }
- if (String.IsNullOrWhiteSpace(controller))
- {
- throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controller");
+ RouteData["action"] = action;
}
- RouteData["controller"] = controller;
- RouteData["action"] = action;
-
- if (!String.IsNullOrWhiteSpace(areaName))
+ if (controller != null)
{
- RouteData["area"] = areaName;
+ RouteData["controller"] = controller;
}
}
///
/// Initializes a new instance of the class.
///
- /// The route name.
- /// The name of the controller.
- ///
- /// Find the controller in the root if . Otherwise look in the current area.
+ ///
+ /// The action name used when generating the URL where client should send a validation request.
///
- public RemoteAttribute(string action, string controller, AreaReference areaReference)
+ ///
+ /// The controller name used when generating the URL where client should send a validation request.
+ ///
+ /// The name of the area containing the .
+ ///
+ ///
+ /// If either or is null, uses the corresponding
+ /// ambient value.
+ ///
+ /// If is null, finds the in the root area.
+ /// Use the overload find the in
+ /// the current area. Or explicitly pass the current area's name as the argument to
+ /// this overload.
+ ///
+ public RemoteAttribute(string action, string controller, string areaName)
: this(action, controller)
{
- if (areaReference == AreaReference.UseRoot)
- {
- RouteData["area"] = null;
- }
+ RouteData["area"] = areaName;
}
+ ///
+ /// Gets or sets the HTTP method ("Get" or "Post") client should use when sending a validation
+ /// request.
+ ///
public string HttpMethod { get; set; }
+ ///
+ /// Gets or sets the comma-separated names of fields the client should include in a validation request.
+ ///
public string AdditionalFields
{
- get { return _additionalFields ?? String.Empty; }
+ get { return _additionalFields; }
set
{
- _additionalFields = value;
- _additonalFieldsSplit = AuthorizeAttribute.SplitString(value);
+ _additionalFields = value ?? string.Empty;
+ _additionalFieldsSplit = SplitAndTrimPropertyNames(value)
+ .Select(field => FormatPropertyForClientValidation(field))
+ .ToArray();
}
}
- protected RouteValueDictionary RouteData { get; private set; }
+ ///
+ /// Gets the used when generating the URL where client should send a
+ /// validation request.
+ ///
+ protected RouteValueDictionary RouteData { get; }
+ ///
+ /// Gets or sets the route name used when generating the URL where client should send a validation request.
+ ///
protected string RouteName { get; set; }
- protected virtual RouteCollection Routes
- {
- get { return RouteTable.Routes; }
- }
-
+ ///
+ /// Formats and for use in generated HTML.
+ ///
+ ///
+ /// Name of the property associated with this instance.
+ ///
+ /// Comma-separated names of fields the client should include in a validation request.
+ ///
+ /// Excludes any whitespace from in the return value.
+ /// Prefixes each field name in the return value with "*.".
+ ///
public string FormatAdditionalFieldsForClientValidation(string property)
{
- if (String.IsNullOrEmpty(property))
+ if (string.IsNullOrEmpty(property))
{
- throw new ArgumentException(MvcResources.Common_NullOrEmpty, "property");
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "property");
}
- string delimitedAdditionalFields = FormatPropertyForClientValidation(property);
-
- foreach (string field in _additonalFieldsSplit)
+ var delimitedAdditionalFields = string.Join(",", _additionalFieldsSplit);
+ if (!string.IsNullOrEmpty(delimitedAdditionalFields))
{
- delimitedAdditionalFields += "," + FormatPropertyForClientValidation(field);
+ delimitedAdditionalFields = "," + delimitedAdditionalFields;
}
- return delimitedAdditionalFields;
+ var formattedString = FormatPropertyForClientValidation(property) + delimitedAdditionalFields;
+
+ return formattedString;
}
+ ///
+ /// Formats for use in generated HTML.
+ ///
+ /// One field name the client should include in a validation request.
+ /// Name of a field the client should include in a validation request.
+ /// Returns with a "*." prefix.
public static string FormatPropertyForClientValidation(string property)
{
- if (String.IsNullOrEmpty(property))
+ if (string.IsNullOrEmpty(property))
{
- throw new ArgumentException(MvcResources.Common_NullOrEmpty, "property");
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "property");
}
+
return "*." + property;
}
- [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "The value is a not a regular URL since it may contain ~/ ASP.NET-specific characters")]
- protected virtual string GetUrl(ControllerContext controllerContext)
+ ///
+ /// Returns the URL where the client should send a validation request.
+ ///
+ /// The used to generate the URL.
+ /// The URL where the client should send a validation request.
+ protected virtual string GetUrl([NotNull] ClientModelValidationContext context)
{
- var pathData = Routes.GetVirtualPathForArea(controllerContext.RequestContext,
- RouteName,
- RouteData);
-
- if (pathData == null)
+ var urlHelper = context.RequestServices.GetRequiredService();
+ var url = urlHelper.RouteUrl(RouteName, values: RouteData, protocol: null, host: null, fragment: null);
+ if (url == null)
{
- throw new InvalidOperationException(MvcResources.RemoteAttribute_NoUrlFound);
+ throw new InvalidOperationException(Resources.RemoteAttribute_NoUrlFound);
}
- return pathData.VirtualPath;
+ return url;
}
+ ///
public override string FormatErrorMessage(string name)
{
- return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name);
+ return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name);
}
+ ///
+ ///
+ /// Always returns true since this does no validation itself.
+ /// Related validations occur only when the client sends a validation request.
+ ///
public override bool IsValid(object value)
{
return true;
}
- public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
+ ///
+ ///
+ /// Thrown if unable to generate a target URL for a validation request.
+ ///
+ public virtual IEnumerable GetClientValidationRules(
+ [NotNull] ClientModelValidationContext context)
{
- yield return new ModelClientValidationRemoteRule(FormatErrorMessage(metadata.GetDisplayName()), GetUrl(context), HttpMethod, FormatAdditionalFieldsForClientValidation(metadata.PropertyName));
+ var metadata = context.ModelMetadata;
+ var rule = new ModelClientValidationRemoteRule(
+ FormatErrorMessage(metadata.GetDisplayName()),
+ GetUrl(context),
+ HttpMethod,
+ FormatAdditionalFieldsForClientValidation(metadata.PropertyName));
+
+ return new[] { rule };
+ }
+
+ private static IEnumerable SplitAndTrimPropertyNames(string original)
+ {
+ if (string.IsNullOrEmpty(original))
+ {
+ return new string[0];
+ }
+
+ var split = original.Split(',')
+ .Select(piece => piece.Trim())
+ .Where(trimmed => !string.IsNullOrEmpty(trimmed));
+ return split;
}
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/DefaultHtmlGenerator.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/DefaultHtmlGenerator.cs
index 82b577eb9e..51c4d30eeb 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/DefaultHtmlGenerator.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/DefaultHtmlGenerator.cs
@@ -724,16 +724,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
string name)
{
var validatorProvider = _bindingContextAccessor.Value.ValidatorProvider;
-
metadata = metadata ??
ExpressionMetadataProvider.FromStringExpression(name, viewContext.ViewData, _metadataProvider);
+ var validationContext =
+ new ClientModelValidationContext(metadata, _metadataProvider, viewContext.HttpContext.RequestServices);
- return
- validatorProvider
+ return validatorProvider
.GetValidators(metadata)
.OfType()
- .SelectMany(v => v.GetClientValidationRules(
- new ClientModelValidationContext(metadata, _metadataProvider)));
+ .SelectMany(v => v.GetClientValidationRules(validationContext));
}
internal static string EvalString(ViewContext viewContext, string key, string format)
diff --git a/src/Microsoft.AspNet.Mvc.Core/Resources.resx b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
index 51c569c1c4..2b17cde37e 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Resources.resx
+++ b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
@@ -445,4 +445,10 @@
The action '{0}' has ApiExplorer enabled, but is using conventional routing. Only actions which use attribute routing support ApiExplorer.
+
+ No URL for remote validation could be found.
+
+
+ '{0}' is invalid.
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedDataAnnotationsModelMetadata.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedDataAnnotationsModelMetadata.cs
index 8aa42e6555..3448715089 100644
--- a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedDataAnnotationsModelMetadata.cs
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedDataAnnotationsModelMetadata.cs
@@ -351,19 +351,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
- private static IEnumerable SplitString(string original)
- {
- if (string.IsNullOrEmpty(original))
- {
- return new string[0];
- }
-
- var split = original.Split(',')
- .Select(piece => piece.Trim())
- .Where(trimmed => !string.IsNullOrEmpty(trimmed));
- return split;
- }
-
private class CompositePredicateProvider : IPropertyBindingPredicateProvider
{
private readonly IPropertyBindingPredicateProvider[] _providers;
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ClientModelValidationContext.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ClientModelValidationContext.cs
index 05e47f2249..9e282ef464 100644
--- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ClientModelValidationContext.cs
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/ClientModelValidationContext.cs
@@ -1,19 +1,25 @@
// 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;
+
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class ClientModelValidationContext
{
public ClientModelValidationContext([NotNull] ModelMetadata metadata,
- [NotNull] IModelMetadataProvider metadataProvider)
+ [NotNull] IModelMetadataProvider metadataProvider,
+ [NotNull] IServiceProvider requestServices)
{
ModelMetadata = metadata;
MetadataProvider = metadataProvider;
+ RequestServices = requestServices;
}
- public ModelMetadata ModelMetadata { get; private set; }
+ public ModelMetadata ModelMetadata { get; }
- public IModelMetadataProvider MetadataProvider { get; private set; }
+ public IModelMetadataProvider MetadataProvider { get; }
+
+ public IServiceProvider RequestServices { get; }
}
}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidator.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidator.cs
index ab4a8751e1..7bc9d64e14 100644
--- a/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidator.cs
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Validation/DataAnnotationsModelValidator.cs
@@ -61,6 +61,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public virtual IEnumerable GetClientValidationRules(
[NotNull] ClientModelValidationContext context)
{
+ var customValidator = Attribute as IClientModelValidator;
+ if (customValidator != null)
+ {
+ return customValidator.GetClientValidationRules(context);
+ }
+
return Enumerable.Empty();
}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/RemoteAttributeTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/RemoteAttributeTest.cs
index 9b7ff69921..e460886f6b 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/RemoteAttributeTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/RemoteAttributeTest.cs
@@ -1,37 +1,54 @@
-// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+// 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;
using System.Collections.Generic;
-using System.Linq;
-using System.Web.Routing;
-using Microsoft.TestCommon;
+using Microsoft.AspNet.Builder;
+using Microsoft.AspNet.Http.Core;
+using Microsoft.AspNet.Mvc.ModelBinding;
+using Microsoft.AspNet.Routing;
+using Microsoft.Framework.DependencyInjection;
+using Microsoft.Framework.DependencyInjection.Fallback;
+using Microsoft.Framework.Logging;
+using Microsoft.Framework.OptionsModel;
using Moq;
+using Xunit;
-namespace System.Web.Mvc.Test
+namespace Microsoft.AspNet.Mvc
{
public class RemoteAttributeTest
{
- // Good route name, bad route name
- // Controller + Action
+ private static readonly IModelMetadataProvider _metadataProvider = new EmptyModelMetadataProvider();
+ private static readonly ModelMetadata _metadata = _metadataProvider.GetMetadataForProperty(
+ modelAccessor: null,
+ containerType: typeof(string),
+ propertyName: "Length");
- [Fact]
- public void GuardClauses()
+ public static TheoryData SomeNames
{
- // Act & Assert
- Assert.ThrowsArgumentNullOrEmpty(
- () => new RemoteAttribute(null, "controller"),
- "action");
- Assert.ThrowsArgumentNullOrEmpty(
- () => new RemoteAttribute("action", null),
- "controller");
- Assert.ThrowsArgumentNullOrEmpty(
- () => new RemoteAttribute(null),
- "routeName");
- Assert.ThrowsArgumentNullOrEmpty(
- () => RemoteAttribute.FormatPropertyForClientValidation(String.Empty),
- "property");
- Assert.ThrowsArgumentNullOrEmpty(
- () => new RemoteAttribute("foo").FormatAdditionalFieldsForClientValidation(String.Empty),
- "property");
+ get
+ {
+ return new TheoryData
+ {
+ string.Empty,
+ "Action",
+ "In a controller",
+ " slightly\t odd\t whitespace\t\r\n",
+ };
+ }
+ }
+
+ // Null or empty property names are invalid. (Those containing just whitespace are legal.)
+ public static TheoryData NullOrEmptyNames
+ {
+ get
+ {
+ return new TheoryData
+ {
+ null,
+ string.Empty,
+ };
+ }
}
[Fact]
@@ -43,408 +60,583 @@ namespace System.Web.Mvc.Test
}
[Fact]
- public void BadRouteNameThrows()
+ public void Constructor_WithNullAction_IgnoresArgument()
{
- // Arrange
- ControllerContext context = new ControllerContext();
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(object));
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("RouteName");
-
- // Act & Assert
- Assert.Throws(
- () => new List(attribute.GetClientValidationRules(metadata, context)),
- "A route named 'RouteName' could not be found in the route collection.\r\nParameter name: name");
- }
-
- [Fact]
- public void NoRouteWithActionControllerThrows()
- {
- // Arrange
- ControllerContext context = new ControllerContext();
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(null, typeof(string), "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
-
- // Act & Assert
- Assert.Throws(
- () => new List(attribute.GetClientValidationRules(metadata, context)),
- "No url for remote validation could be found.");
- }
-
- [Fact]
- public void GoodRouteNameReturnsCorrectClientData()
- {
- // Arrange
- string url = null;
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(null, typeof(string), "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("RouteName");
- attribute.RouteTable.Add("RouteName", new Route("my/url", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule = attribute.GetClientValidationRules(metadata, GetMockControllerContext(url)).Single();
+ // Arrange & Act
+ var attribute = new TestableRemoteAttribute(action: null, controller: "AController");
// Assert
- Assert.Equal("remote", rule.ValidationType);
- Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
- Assert.Equal(2, rule.ValidationParameters.Count);
- Assert.Equal("/my/url", rule.ValidationParameters["url"]);
+ var keyValuePair = Assert.Single(attribute.RouteData);
+ Assert.Equal(keyValuePair.Key, "controller");
}
[Fact]
- public void ActionControllerReturnsCorrectClientDataWithoutNamedParameters()
+ public void Constructor_WithNullController_IgnoresArgument()
{
- // Arrange
- string url = null;
-
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(null, typeof(string), "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule = attribute.GetClientValidationRules(metadata, GetMockControllerContext(url)).Single();
+ // Arrange & Act
+ var attribute = new TestableRemoteAttribute("AnAction", controller: null);
// Assert
+ var keyValuePair = Assert.Single(attribute.RouteData);
+ Assert.Equal(keyValuePair.Key, "action");
+ Assert.Null(attribute.RouteName);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [MemberData(nameof(SomeNames))]
+ public void Constructor_WithRouteName_UpdatesProperty(string routeName)
+ {
+ // Arrange & Act
+ var attribute = new TestableRemoteAttribute(routeName);
+
+ // Assert
+ Assert.Empty(attribute.RouteData);
+ Assert.Equal(routeName, attribute.RouteName);
+ }
+
+ [Theory]
+ [MemberData(nameof(SomeNames))]
+ public void Constructor_WithActionController_UpdatesActionRouteData(string action)
+ {
+ // Arrange & Act
+ var attribute = new TestableRemoteAttribute(action, "AController");
+
+ // Assert
+ Assert.Equal(2, attribute.RouteData.Count);
+ Assert.Contains("controller", attribute.RouteData.Keys);
+ var resultName = Assert.Single(
+ attribute.RouteData,
+ keyValuePair => string.Equals(keyValuePair.Key, "action", StringComparison.Ordinal))
+ .Value;
+ Assert.Equal(action, resultName);
+ Assert.Null(attribute.RouteName);
+ }
+
+ [Theory]
+ [MemberData(nameof(SomeNames))]
+ public void Constructor_WithActionController_UpdatesControllerRouteData(string controller)
+ {
+ // Arrange & Act
+ var attribute = new TestableRemoteAttribute("AnAction", controller);
+
+ // Assert
+ Assert.Equal(2, attribute.RouteData.Count);
+ Assert.Contains("action", attribute.RouteData.Keys);
+ var resultName = Assert.Single(
+ attribute.RouteData,
+ keyValuePair => string.Equals(keyValuePair.Key, "controller", StringComparison.Ordinal))
+ .Value;
+ Assert.Equal(controller, resultName);
+ Assert.Null(attribute.RouteName);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [MemberData(nameof(SomeNames))]
+ public void Constructor_WithActionControllerAreaName_UpdatesAreaRouteData(string areaName)
+ {
+ // Arrange & Act
+ var attribute = new TestableRemoteAttribute("AnAction", "AController", areaName: areaName);
+
+ // Assert
+ Assert.Equal(3, attribute.RouteData.Count);
+ Assert.Contains("action", attribute.RouteData.Keys);
+ Assert.Contains("controller", attribute.RouteData.Keys);
+ var resultName = Assert.Single(
+ attribute.RouteData,
+ keyValuePair => string.Equals(keyValuePair.Key, "area", StringComparison.Ordinal))
+ .Value;
+ Assert.Equal(areaName, resultName);
+ Assert.Null(attribute.RouteName);
+ }
+
+ [Theory]
+ [MemberData(nameof(NullOrEmptyNames))]
+ public void FormatAdditionalFieldsForClientValidation_WithInvalidPropertyName_Throws(string property)
+ {
+ // Arrange
+ var attribute = new RemoteAttribute(routeName: "default");
+ var expected = "Value cannot be null or empty." + Environment.NewLine + "Parameter name: property";
+
+ // Act & Assert
+ var exception = Assert.Throws(
+ "property",
+ () => attribute.FormatAdditionalFieldsForClientValidation(property));
+ Assert.Equal(expected, exception.Message);
+ }
+
+ [Theory]
+ [MemberData(nameof(NullOrEmptyNames))]
+ public void FormatPropertyForClientValidation_WithInvalidPropertyName_Throws(string property)
+ {
+ // Arrange
+ var expected = "Value cannot be null or empty." + Environment.NewLine + "Parameter name: property";
+
+ // Act & Assert
+ var exception = Assert.Throws(
+ "property",
+ () => RemoteAttribute.FormatPropertyForClientValidation(property));
+ Assert.Equal(expected, exception.Message);
+ }
+
+ [Fact]
+ public void GetClientValidationRules_WithBadRouteName_Throws()
+ {
+ // Arrange
+ var attribute = new RemoteAttribute("nonexistentRoute");
+ var context = GetValidationContextWithArea(currentArea: null);
+
+ // Act & Assert
+ var exception = Assert.Throws(() => attribute.GetClientValidationRules(context));
+ Assert.Equal("No URL for remote validation could be found.", exception.Message);
+ }
+
+ [Fact]
+ public void GetClientValidationRules_WithActionController_NoController_Throws()
+ {
+ // Arrange
+ var attribute = new RemoteAttribute("Action", "Controller");
+ var context = GetValidationContextWithNoController();
+
+ // Act & Assert
+ var exception = Assert.Throws(() => attribute.GetClientValidationRules(context));
+ Assert.Equal("No URL for remote validation could be found.", exception.Message);
+ }
+
+ [Fact]
+ public void GetClientValidationRules_WithRoute_CallsUrlHelperWithExpectedValues()
+ {
+ // Arrange
+ var routeName = "RouteName";
+ var attribute = new RemoteAttribute(routeName);
+ var url = "/my/URL";
+ var urlHelper = new MockUrlHelper(url, routeName);
+ var context = GetValidationContext(urlHelper);
+
+ // Act & Assert
+ var rule = Assert.Single(attribute.GetClientValidationRules(context));
Assert.Equal("remote", rule.ValidationType);
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
+
Assert.Equal(2, rule.ValidationParameters.Count);
- Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
- Assert.Throws(
- () => rule.ValidationParameters["type"],
- "The given key was not present in the dictionary.");
+ Assert.Equal(url, rule.ValidationParameters["url"]);
+
+ var routeDictionary = Assert.IsType(urlHelper.RouteValues);
+ Assert.Empty(routeDictionary);
}
[Fact]
- public void ActionControllerReturnsCorrectClientDataWithNamedParameters()
+ public void GetClientValidationRules_WithActionController_CallsUrlHelperWithExpectedValues()
{
// Arrange
- string url = null;
+ var attribute = new RemoteAttribute("Action", "Controller");
+ var url = "/Controller/Action";
+ var urlHelper = new MockUrlHelper(url, routeName: null);
+ var context = GetValidationContext(urlHelper);
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(null, typeof(string), "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
- attribute.HttpMethod = "POST";
- attribute.AdditionalFields = "Password,ConfirmPassword";
-
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule = attribute.GetClientValidationRules(metadata, GetMockControllerContext(url)).Single();
-
- // Assert
+ // Act & Assert
+ var rule = Assert.Single(attribute.GetClientValidationRules(context));
Assert.Equal("remote", rule.ValidationType);
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
+
+ Assert.Equal(2, rule.ValidationParameters.Count);
+ Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
+ Assert.Equal(url, rule.ValidationParameters["url"]);
+
+ var routeDictionary = Assert.IsType(urlHelper.RouteValues);
+ Assert.Equal(2, routeDictionary.Count);
+ Assert.Equal("Action", routeDictionary["action"] as string);
+ Assert.Equal("Controller", routeDictionary["controller"] as string);
+ }
+
+ [Fact]
+ public void GetClientValidationRules_WithActionController_PropertiesSet_CallsUrlHelperWithExpectedValues()
+ {
+ // Arrange
+ var attribute = new RemoteAttribute("Action", "Controller")
+ {
+ HttpMethod = "POST",
+ AdditionalFields = "Password,ConfirmPassword",
+ };
+ var url = "/Controller/Action";
+ var urlHelper = new MockUrlHelper(url, routeName: null);
+ var context = GetValidationContext(urlHelper);
+
+ // Act & Assert
+ var rule = Assert.Single(attribute.GetClientValidationRules(context));
+ Assert.Equal("remote", rule.ValidationType);
+ Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
+
Assert.Equal(3, rule.ValidationParameters.Count);
- Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
Assert.Equal("*.Length,*.Password,*.ConfirmPassword", rule.ValidationParameters["additionalfields"]);
Assert.Equal("POST", rule.ValidationParameters["type"]);
+ Assert.Equal(url, rule.ValidationParameters["url"]);
+
+ var routeDictionary = Assert.IsType(urlHelper.RouteValues);
+ Assert.Equal(2, routeDictionary.Count);
+ Assert.Equal("Action", routeDictionary["action"] as string);
+ Assert.Equal("Controller", routeDictionary["controller"] as string);
}
- // Current area is root in this case.
[Fact]
- public void ActionController_RemoteFindsControllerInCurrentArea()
+ public void GetClientValidationRules_WithActionControllerArea_CallsUrlHelperWithExpectedValues()
{
// Arrange
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
- containerType: typeof(string), propertyName: "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
- attribute.HttpMethod = "POST";
+ var attribute = new RemoteAttribute("Action", "Controller", "Test")
+ {
+ HttpMethod = "POST",
+ };
+ var url = "/Test/Controller/Action";
+ var urlHelper = new MockUrlHelper(url, routeName: null);
+ var context = GetValidationContext(urlHelper);
- var context = new AreaRegistrationContext("Test", attribute.RouteTable);
- context.MapRoute(name: null, url: "Test/{controller}/{action}");
-
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule =
- attribute.GetClientValidationRules(metadata, GetMockControllerContext(url: null)).Single();
-
- // Assert
+ // Act & Assert
+ var rule = Assert.Single(attribute.GetClientValidationRules(context));
Assert.Equal("remote", rule.ValidationType);
+ Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
+
+ Assert.Equal(3, rule.ValidationParameters.Count);
+ Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
+ Assert.Equal("POST", rule.ValidationParameters["type"]);
+ Assert.Equal(url, rule.ValidationParameters["url"]);
+
+ var routeDictionary = Assert.IsType(urlHelper.RouteValues);
+ Assert.Equal(3, routeDictionary.Count);
+ Assert.Equal("Action", routeDictionary["action"] as string);
+ Assert.Equal("Controller", routeDictionary["controller"] as string);
+ Assert.Equal("Test", routeDictionary["area"] as string);
+ }
+
+ // Root area is current in this case.
+ [Fact]
+ public void GetClientValidationRules_WithActionController_FindsControllerInCurrentArea()
+ {
+ // Arrange
+ var attribute = new RemoteAttribute("Action", "Controller");
+ var context = GetValidationContextWithArea(currentArea: null);
+
+ // Act & Assert
+ var rule = Assert.Single(attribute.GetClientValidationRules(context));
+ Assert.Equal("remote", rule.ValidationType);
+ Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
+
+ Assert.Equal(2, rule.ValidationParameters.Count);
+ Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
}
+ // Test area is current in this case.
[Fact]
- public void ActionControllerArea_RemoteFindsControllerInNamedArea()
+ public void GetClientValidationRules_WithActionControllerInArea_FindsControllerInCurrentArea()
{
// Arrange
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
- containerType: typeof(string), propertyName: "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "Test");
- attribute.HttpMethod = "POST";
+ var attribute = new RemoteAttribute("Action", "Controller");
+ var context = GetValidationContextWithArea(currentArea: "Test");
- var context = new AreaRegistrationContext("Test", attribute.RouteTable);
- context.MapRoute(name: null, url: "Test/{controller}/{action}");
-
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule =
- attribute.GetClientValidationRules(metadata, GetMockControllerContext(url: null)).Single();
-
- // Assert
+ // Act & Assert
+ var rule = Assert.Single(attribute.GetClientValidationRules(context));
Assert.Equal("remote", rule.ValidationType);
+ Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
+
+ Assert.Equal(2, rule.ValidationParameters.Count);
+ Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
}
- // Current area is root in this case.
- [Fact]
- public void ActionControllerArea_WithEmptyArea_RemoteFindsControllerInCurrentArea()
+ // Explicit reference to the (current) root area.
+ [Theory]
+ [MemberData(nameof(NullOrEmptyNames))]
+ public void GetClientValidationRules_WithActionControllerArea_FindsControllerInRootArea(string areaName)
{
// Arrange
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
- containerType: typeof(string), propertyName: "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "");
- attribute.HttpMethod = "POST";
+ var attribute = new RemoteAttribute("Action", "Controller", areaName);
+ var context = GetValidationContextWithArea(currentArea: null);
- var context = new AreaRegistrationContext("Test", attribute.RouteTable);
- context.MapRoute(name: null, url: "Test/{controller}/{action}");
-
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule =
- attribute.GetClientValidationRules(metadata, GetMockControllerContext(url: null)).Single();
-
- // Assert
+ // Act & Assert
+ var rule = Assert.Single(attribute.GetClientValidationRules(context));
Assert.Equal("remote", rule.ValidationType);
+ Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
+
+ Assert.Equal(2, rule.ValidationParameters.Count);
+ Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
}
- // Current area is root in this case.
- [Fact]
- public void ActionControllerAreaReference_WithUseCurrent_RemoteFindsControllerInCurrentArea()
+ // Test area is current in this case.
+ [Theory]
+ [MemberData(nameof(NullOrEmptyNames))]
+ public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInRootArea(string areaName)
{
// Arrange
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
- containerType: typeof(string), propertyName: "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseCurrent);
- attribute.HttpMethod = "POST";
+ var attribute = new RemoteAttribute("Action", "Controller", areaName);
+ var context = GetValidationContextWithArea(currentArea: "Test");
- var context = new AreaRegistrationContext("Test", attribute.RouteTable);
- context.MapRoute(name: null, url: "Test/{controller}/{action}");
-
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule =
- attribute.GetClientValidationRules(metadata, GetMockControllerContext(url: null)).Single();
-
- // Assert
+ // Act & Assert
+ var rule = Assert.Single(attribute.GetClientValidationRules(context));
Assert.Equal("remote", rule.ValidationType);
+ Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
+
+ Assert.Equal(2, rule.ValidationParameters.Count);
+ Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
}
+ // Root area is current in this case.
[Fact]
- public void ActionControllerAreaReference_WithUseRoot_RemoteFindsControllerInRoot()
+ public void GetClientValidationRules_WithActionControllerArea_FindsControllerInNamedArea()
{
// Arrange
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
- containerType: typeof(string), propertyName: "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseRoot);
- attribute.HttpMethod = "POST";
+ var attribute = new RemoteAttribute("Action", "Controller", "Test");
+ var context = GetValidationContextWithArea(currentArea: null);
- var context = new AreaRegistrationContext("Test", attribute.RouteTable);
- context.MapRoute(name: null, url: "Test/{controller}/{action}");
-
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule =
- attribute.GetClientValidationRules(metadata, GetMockControllerContext(url: null)).Single();
-
- // Assert
+ // Act & Assert
+ var rule = Assert.Single(attribute.GetClientValidationRules(context));
Assert.Equal("remote", rule.ValidationType);
- Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
- }
+ Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
- // Current area is Test in this case.
- [Fact]
- public void ActionController_InArea_RemoteFindsControllerInCurrentArea()
- {
- // Arrange
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
- containerType: typeof(string), propertyName: "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
- attribute.HttpMethod = "POST";
-
- var context = new AreaRegistrationContext("Test", attribute.RouteTable);
- context.MapRoute(name: null, url: "Test/{controller}/{action}");
-
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule =
- attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
- .Single();
-
- // Assert
- Assert.Equal("remote", rule.ValidationType);
+ Assert.Equal(2, rule.ValidationParameters.Count);
+ Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
}
- // Explicit reference to the Test area.
+ // Explicit reference to the current (Test) area.
[Fact]
- public void ActionControllerArea_InSameArea_RemoteFindsControllerInNamedArea()
+ public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInNamedArea()
{
// Arrange
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
- containerType: typeof(string), propertyName: "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "Test");
- attribute.HttpMethod = "POST";
+ var attribute = new RemoteAttribute("Action", "Controller", "Test");
+ var context = GetValidationContextWithArea(currentArea: "Test");
- var context = new AreaRegistrationContext("Test", attribute.RouteTable);
- context.MapRoute(name: null, url: "Test/{controller}/{action}");
-
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule =
- attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
- .Single();
-
- // Assert
+ // Act & Assert
+ var rule = Assert.Single(attribute.GetClientValidationRules(context));
Assert.Equal("remote", rule.ValidationType);
+ Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
+
+ Assert.Equal(2, rule.ValidationParameters.Count);
+ Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
}
+ // Test area is current in this case.
[Fact]
- public void ActionControllerArea_InArea_RemoteFindsControllerInNamedArea()
+ public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInDifferentArea()
{
// Arrange
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
- containerType: typeof(string), propertyName: "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "AnotherArea");
- attribute.HttpMethod = "POST";
+ var attribute = new RemoteAttribute("Action", "Controller", "AnotherArea");
+ var context = GetValidationContextWithArea(currentArea: "Test");
- var context = new AreaRegistrationContext("Test", attribute.RouteTable);
- context.MapRoute(name: null, url: "Test/{controller}/{action}");
- context = new AreaRegistrationContext("AnotherArea", attribute.RouteTable);
- context.MapRoute(name: null, url: "AnotherArea/{controller}/{action}");
-
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule =
- attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
- .Single();
-
- // Assert
+ // Act & Assert
+ var rule = Assert.Single(attribute.GetClientValidationRules(context));
Assert.Equal("remote", rule.ValidationType);
+ Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
+
+ Assert.Equal(2, rule.ValidationParameters.Count);
+ Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
Assert.Equal("/AnotherArea/Controller/Action", rule.ValidationParameters["url"]);
}
- // Current area is Test in this case.
- [Fact]
- public void ActionControllerArea_WithEmptyAreaInArea_RemoteFindsControllerInCurrentArea()
+ private static ClientModelValidationContext GetValidationContext(IUrlHelper urlHelper)
{
- // Arrange
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
- containerType: typeof(string), propertyName: "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "");
- attribute.HttpMethod = "POST";
+ var serviceCollection = GetServiceCollection();
+ serviceCollection.AddInstance(urlHelper);
+ var serviceProvider = serviceCollection.BuildServiceProvider();
- var context = new AreaRegistrationContext("Test", attribute.RouteTable);
- context.MapRoute(name: null, url: "Test/{controller}/{action}");
-
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule =
- attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
- .Single();
-
- // Assert
- Assert.Equal("remote", rule.ValidationType);
- Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
+ return new ClientModelValidationContext(_metadata, _metadataProvider, serviceProvider);
}
- // Current area is Test in this case.
- [Fact]
- public void ActionControllerAreaReference_WithUseCurrentInArea_RemoteFindsControllerInCurrentArea()
+ private static ClientModelValidationContext GetValidationContextWithArea(string currentArea)
{
- // Arrange
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
- containerType: typeof(string), propertyName: "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseCurrent);
- attribute.HttpMethod = "POST";
+ var serviceCollection = GetServiceCollection();
+ var serviceProvider = serviceCollection.BuildServiceProvider();
+ var routeCollection = GetRouteCollectionWithArea(serviceProvider);
+ var routeData = new RouteData
+ {
+ Routers =
+ {
+ routeCollection,
+ },
+ Values =
+ {
+ { "action", "Index" },
+ { "controller", "Home" },
+ },
+ };
+ if (!string.IsNullOrEmpty(currentArea))
+ {
+ routeData.Values["area"] = currentArea;
+ }
- var context = new AreaRegistrationContext("Test", attribute.RouteTable);
- context.MapRoute(name: null, url: "Test/{controller}/{action}");
+ var contextAccessor = GetContextAccessor(serviceProvider, routeData);
+ var actionSelector = new Mock(MockBehavior.Strict);
+ var urlHelper = new UrlHelper(contextAccessor, actionSelector.Object);
+ serviceCollection.AddInstance(urlHelper);
+ serviceProvider = serviceCollection.BuildServiceProvider();
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule =
- attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
- .Single();
-
- // Assert
- Assert.Equal("remote", rule.ValidationType);
- Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
+ return new ClientModelValidationContext(_metadata, _metadataProvider, serviceProvider);
}
- [Fact]
- public void ActionControllerAreaReference_WithUseRootInArea_RemoteFindsControllerInRoot()
+ private static ClientModelValidationContext GetValidationContextWithNoController()
{
- // Arrange
- ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
- containerType: typeof(string), propertyName: "Length");
- TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseRoot);
- attribute.HttpMethod = "POST";
+ var serviceCollection = GetServiceCollection();
+ var serviceProvider = serviceCollection.BuildServiceProvider();
+ var routeCollection = GetRouteCollectionWithNoController(serviceProvider);
+ var routeData = new RouteData
+ {
+ Routers =
+ {
+ routeCollection,
+ },
+ };
- var context = new AreaRegistrationContext("Test", attribute.RouteTable);
- context.MapRoute(name: null, url: "Test/{controller}/{action}");
+ var contextAccessor = GetContextAccessor(serviceProvider, routeData);
+ var actionSelector = new Mock(MockBehavior.Strict);
+ var urlHelper = new UrlHelper(contextAccessor, actionSelector.Object);
+ serviceCollection.AddInstance(urlHelper);
+ serviceProvider = serviceCollection.BuildServiceProvider();
- attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
-
- // Act
- ModelClientValidationRule rule =
- attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
- .Single();
-
- // Assert
- Assert.Equal("remote", rule.ValidationType);
- Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
+ return new ClientModelValidationContext(_metadata, _metadataProvider, serviceProvider);
}
- private ControllerContext GetMockControllerContext(string url)
+ private static IRouter GetRouteCollectionWithArea(IServiceProvider serviceProvider)
{
- Mock context = new Mock();
- context.Setup(c => c.HttpContext.Request.ApplicationPath)
- .Returns("/");
- context.Setup(c => c.HttpContext.Response.ApplyAppPathModifier(It.IsAny()))
- .Callback(vpath => url = vpath)
- .Returns(() => url);
+ var builder = GetRouteBuilder(serviceProvider, isBound: true);
- return context.Object;
+ // Setting IsBound to true makes order more important than usual. First try the route that requires the
+ // area value. Skip usual "area:exists" constraint because that isn't relevant for link generation and it
+ // complicates the setup significantly.
+ builder.MapRoute("areaRoute", "{area}/{controller}/{action}");
+ builder.MapRoute("default", "{controller}/{action}", new { controller = "Home", action = "Index" });
+
+ return builder.Build();
}
- private ControllerContext GetMockControllerContextWithArea(string url, string areaName)
+ private static IRouter GetRouteCollectionWithNoController(IServiceProvider serviceProvider)
{
- Mock context = new Mock();
- context.Setup(c => c.HttpContext.Request.ApplicationPath)
- .Returns("/");
- context.Setup(c => c.HttpContext.Response.ApplyAppPathModifier(It.IsAny()))
- .Callback(vpath => url = vpath)
- .Returns(() => url);
+ var builder = GetRouteBuilder(serviceProvider, isBound: false);
+ builder.MapRoute("default", "static/route");
- var controllerContext = context.Object;
+ return builder.Build();
+ }
- controllerContext.RequestContext.RouteData.DataTokens.Add("area", areaName);
+ private static RouteBuilder GetRouteBuilder(IServiceProvider serviceProvider, bool isBound)
+ {
+ var builder = new RouteBuilder
+ {
+ ServiceProvider = serviceProvider,
+ };
- return controllerContext;
+ var handler = new Mock(MockBehavior.Strict);
+ handler
+ .Setup(router => router.GetVirtualPath(It.IsAny()))
+ .Callback(context => context.IsBound = isBound)
+ .Returns((string)null);
+ builder.DefaultHandler = handler.Object;
+
+ return builder;
+ }
+
+ private static IScopedInstance GetContextAccessor(
+ IServiceProvider serviceProvider,
+ RouteData routeData = null)
+ {
+ // Set IServiceProvider properties because TemplateRoute gets services (e.g. an ILoggerFactory instance)
+ // through the HttpContext.
+ var httpContext = new DefaultHttpContext
+ {
+ ApplicationServices = serviceProvider,
+ RequestServices = serviceProvider,
+ };
+
+ if (routeData == null)
+ {
+ routeData = new RouteData
+ {
+ Routers = { Mock.Of(), },
+ };
+ }
+
+ var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());
+ var contextAccessor = new Mock>();
+ contextAccessor
+ .SetupGet(accessor => accessor.Value)
+ .Returns(actionContext);
+
+ return contextAccessor.Object;
+ }
+
+ private static ServiceCollection GetServiceCollection()
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddInstance(new NullLoggerFactory());
+
+ var routeOptions = new RouteOptions();
+ var accessor = new Mock>();
+ accessor
+ .SetupGet(options => options.Options)
+ .Returns(routeOptions);
+
+ // DefaultInlineConstraintResolver constructor does not currently check its serviceProvider parameter (e.g.
+ // for null) and the class does not look up any services.
+ serviceCollection.AddInstance(new DefaultInlineConstraintResolver(
+ serviceProvider: null,
+ routeOptions: accessor.Object));
+
+ return serviceCollection;
+ }
+
+ private class MockUrlHelper : IUrlHelper
+ {
+ private readonly string _routeName;
+ private readonly string _url;
+
+ public MockUrlHelper(string url, string routeName)
+ {
+ _routeName = routeName;
+ _url = url;
+ }
+
+ public object RouteValues { get; private set; }
+
+ public string Action(
+ string action,
+ string controller,
+ object values,
+ string protocol,
+ string host,
+ string fragment)
+ {
+ throw new NotImplementedException();
+ }
+
+ public string Content(string contentPath)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsLocalUrl(string url)
+ {
+ throw new NotImplementedException();
+ }
+
+ public string RouteUrl(string routeName, object values, string protocol, string host, string fragment)
+ {
+ Assert.Equal(_routeName, routeName);
+ Assert.Null(protocol);
+ Assert.Null(host);
+ Assert.Null(fragment);
+
+ RouteValues = values;
+
+ return _url;
+ }
}
private class TestableRemoteAttribute : RemoteAttribute
{
- public RouteCollection RouteTable = new RouteCollection();
-
- public TestableRemoteAttribute(string action, string controller, AreaReference areaReference)
- : base(action, controller, areaReference)
- {
- }
-
- public TestableRemoteAttribute(string action, string controller, string areaName)
- : base(action, controller, areaName)
+ public TestableRemoteAttribute(string routeName)
+ : base(routeName)
{
}
@@ -453,14 +645,25 @@ namespace System.Web.Mvc.Test
{
}
- public TestableRemoteAttribute(string routeName)
- : base(routeName)
+ public TestableRemoteAttribute(string action, string controller, string areaName)
+ : base(action, controller, areaName)
{
}
- protected override RouteCollection Routes
+ public new string RouteName
{
- get { return RouteTable; }
+ get
+ {
+ return base.RouteName;
+ }
+ }
+
+ public new RouteValueDictionary RouteData
+ {
+ get
+ {
+ return base.RouteData;
+ }
}
}
}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/UrlHelperTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/UrlHelperTest.cs
index fc7228709a..0732479ecd 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/UrlHelperTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/UrlHelperTest.cs
@@ -13,7 +13,7 @@ using Microsoft.Framework.OptionsModel;
using Moq;
using Xunit;
-namespace Microsoft.AspNet.Mvc.Core.Test
+namespace Microsoft.AspNet.Mvc
{
public class UrlHelperTest
{
@@ -695,7 +695,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
private static UrlHelper CreateUrlHelperWithRouteCollection(string appPrefix)
{
var routeCollection = GetRouter();
- return CreateUrlHelper("/app", routeCollection);
+ return CreateUrlHelper(appPrefix, routeCollection);
}
private static IRouter GetRouter()
@@ -708,13 +708,9 @@ namespace Microsoft.AspNet.Mvc.Core.Test
var rt = new RouteBuilder();
var target = new Mock(MockBehavior.Strict);
target
- .Setup(e => e.GetVirtualPath(It.IsAny()))
- .Callback(c =>
- {
- rt.ToString();
- c.IsBound = true;
- })
- .Returns(rc => null);
+ .Setup(router => router.GetVirtualPath(It.IsAny()))
+ .Callback(context => context.IsBound = true)
+ .Returns(context => null);
rt.DefaultHandler = target.Object;
var serviceProviderMock = new Mock();
var accessorMock = new Mock>();
diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompareAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompareAttributeAdapterTest.cs
index 5135f805d2..36ff524623 100644
--- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompareAttributeAdapterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/CompareAttributeAdapterTest.cs
@@ -3,6 +3,8 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNet.Testing;
+using Microsoft.Framework.DependencyInjection;
+using Microsoft.Framework.DependencyInjection.Fallback;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
@@ -17,7 +19,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyDisplayNameModel), "MyProperty");
var attribute = new CompareAttribute("OtherProperty");
- var context = new ClientModelValidationContext(metadata, metadataProvider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, metadataProvider, requestServices);
var adapter = new CompareAttributeAdapter(attribute);
// Act
@@ -36,7 +40,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyNameModel), "MyProperty");
var attribute = new CompareAttribute("OtherProperty");
- var context = new ClientModelValidationContext(metadata, metadataProvider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, metadataProvider, requestServices);
var adapter = new CompareAttributeAdapter(attribute);
// Act
@@ -57,7 +63,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
ErrorMessage = "Hello '{0}', goodbye '{1}'."
};
- var context = new ClientModelValidationContext(metadata, metadataProvider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, metadataProvider, requestServices);
var adapter = new CompareAttributeAdapter(attribute);
// Act
@@ -85,7 +93,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
ErrorMessageResourceName = "CompareAttributeTestResource",
ErrorMessageResourceType = typeof(Test.Resources),
};
- var context = new ClientModelValidationContext(metadata, metadataProvider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, metadataProvider, requestServices);
var adapter = new CompareAttributeAdapter(attribute);
// Act
diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorTest.cs
index ade8168826..678190e862 100644
--- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorTest.cs
+++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/DataAnnotationsModelValidatorTest.cs
@@ -1,10 +1,15 @@
// 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;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
#if ASPNET50
using System.Linq;
+#endif
+using Microsoft.Framework.DependencyInjection;
+using Microsoft.Framework.DependencyInjection.Fallback;
+#if ASPNET50
using Moq;
using Moq.Protected;
#endif
@@ -196,6 +201,51 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
#endif
+ [Fact]
+ public void GetClientValidationRules_ReturnsEmptyRuleSet()
+ {
+ // Arrange
+ var attribute = new FileExtensionsAttribute();
+ var validator = new DataAnnotationsModelValidator(attribute);
+ var metadata = _metadataProvider.GetMetadataForProperty(
+ modelAccessor: null,
+ containerType: typeof(string),
+ propertyName: nameof(string.Length));
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, _metadataProvider, requestServices);
+
+ // Act
+ var results = validator.GetClientValidationRules(context);
+
+ // Assert
+ Assert.Empty(results);
+ }
+
+ [Fact]
+ public void GetClientValidationRules_WithIClientModelValidator_CallsAttribute()
+ {
+ // Arrange
+ var attribute = new TestableAttribute();
+ var validator = new DataAnnotationsModelValidator(attribute);
+ var metadata = _metadataProvider.GetMetadataForProperty(
+ modelAccessor: null,
+ containerType: typeof(string),
+ propertyName: nameof(string.Length));
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, _metadataProvider, requestServices);
+
+ // Act
+ var results = validator.GetClientValidationRules(context);
+
+ // Assert
+ var rule = Assert.Single(results);
+ Assert.Equal("an error", rule.ErrorMessage);
+ Assert.Empty(rule.ValidationParameters);
+ Assert.Equal("testable", rule.ValidationType);
+ }
+
[Fact]
public void IsRequiredTests()
{
@@ -221,5 +271,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
public string Name { get; set; }
}
+
+ private class TestableAttribute : ValidationAttribute, IClientModelValidator
+ {
+ public IEnumerable GetClientValidationRules(ClientModelValidationContext context)
+ {
+ return new[] { new ModelClientValidationRule(validationType: "testable", errorMessage: "an error") };
+ }
+ }
}
}
diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MaxLengthAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MaxLengthAttributeAdapterTest.cs
index d0878a1bc6..4d78528e21 100644
--- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MaxLengthAttributeAdapterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MaxLengthAttributeAdapterTest.cs
@@ -3,6 +3,8 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNet.Testing;
+using Microsoft.Framework.DependencyInjection;
+using Microsoft.Framework.DependencyInjection.Fallback;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
@@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var attribute = new MaxLengthAttribute(10);
var adapter = new MaxLengthAttributeAdapter(attribute);
- var context = new ClientModelValidationContext(metadata, provider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, provider, requestServices);
// Act
var rules = adapter.GetClientValidationRules(context);
@@ -42,7 +46,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), propertyName);
var attribute = new MaxLengthAttribute(5) { ErrorMessage = message };
var adapter = new MaxLengthAttributeAdapter(attribute);
- var context = new ClientModelValidationContext(metadata, provider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, provider, requestServices);
// Act
var rules = adapter.GetClientValidationRules(context);
diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MinLengthAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MinLengthAttributeAdapterTest.cs
index 70eb2a4a57..d8b5b0b0cb 100644
--- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MinLengthAttributeAdapterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/MinLengthAttributeAdapterTest.cs
@@ -3,6 +3,8 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNet.Testing;
+using Microsoft.Framework.DependencyInjection;
+using Microsoft.Framework.DependencyInjection.Fallback;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
@@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var attribute = new MinLengthAttribute(6);
var adapter = new MinLengthAttributeAdapter(attribute);
- var context = new ClientModelValidationContext(metadata, provider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, provider, requestServices);
// Act
var rules = adapter.GetClientValidationRules(context);
@@ -42,7 +46,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), propertyName);
var attribute = new MinLengthAttribute(2) { ErrorMessage = message };
var adapter = new MinLengthAttributeAdapter(attribute);
- var context = new ClientModelValidationContext(metadata, provider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, provider, requestServices);
// Act
var rules = adapter.GetClientValidationRules(context);
diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RangeAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RangeAttributeAdapterTest.cs
index c739fe7c42..b608739856 100644
--- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RangeAttributeAdapterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RangeAttributeAdapterTest.cs
@@ -3,6 +3,8 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNet.Testing;
+using Microsoft.Framework.DependencyInjection;
+using Microsoft.Framework.DependencyInjection.Fallback;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
@@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var attribute = new RangeAttribute(typeof(decimal), "0", "100");
var adapter = new RangeAttributeAdapter(attribute);
- var context = new ClientModelValidationContext(metadata, provider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, provider, requestServices);
// Act
var rules = adapter.GetClientValidationRules(context);
diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RequiredAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RequiredAttributeAdapterTest.cs
index d4fff33eb2..c70ba8d5f6 100644
--- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RequiredAttributeAdapterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/RequiredAttributeAdapterTest.cs
@@ -3,6 +3,8 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNet.Testing;
+using Microsoft.Framework.DependencyInjection;
+using Microsoft.Framework.DependencyInjection.Fallback;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
@@ -19,7 +21,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var attribute = new RequiredAttribute();
var adapter = new RequiredAttributeAdapter(attribute);
- var context = new ClientModelValidationContext(metadata, provider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, provider, requestServices);
// Act
var rules = adapter.GetClientValidationRules(context);
diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/StringLengthAttributeAdapterTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/StringLengthAttributeAdapterTest.cs
index 52c758fe84..644e905b44 100644
--- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/StringLengthAttributeAdapterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Validation/StringLengthAttributeAdapterTest.cs
@@ -3,6 +3,8 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNet.Testing;
+using Microsoft.Framework.DependencyInjection;
+using Microsoft.Framework.DependencyInjection.Fallback;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
@@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var attribute = new StringLengthAttribute(8);
var adapter = new StringLengthAttributeAdapter(attribute);
- var context = new ClientModelValidationContext(metadata, provider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, provider, requestServices);
// Act
var rules = adapter.GetClientValidationRules(context);
@@ -40,7 +44,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var attribute = new StringLengthAttribute(10) { MinimumLength = 3 };
var adapter = new StringLengthAttributeAdapter(attribute);
- var context = new ClientModelValidationContext(metadata, provider);
+ var serviceCollection = new ServiceCollection();
+ var requestServices = serviceCollection.BuildServiceProvider();
+ var context = new ClientModelValidationContext(metadata, provider, requestServices);
// Act
var rules = adapter.GetClientValidationRules(context);