Limit [FromServices] to apply only to parameters

Fixes #3507
This commit is contained in:
Pranav K 2015-11-16 13:37:45 -08:00
parent db94a8ed19
commit b520b0cb22
28 changed files with 113 additions and 439 deletions

View File

@ -93,13 +93,12 @@ namespace MvcSample.Web
return View("MyView", user); return View("MyView", user);
} }
[FromServices]
public IHostingEnvironment HostingEnvironment { get; set; }
/// <summary> /// <summary>
/// Action that shows multiple file upload. /// Action that shows multiple file upload.
/// </summary> /// </summary>
public async Task<ActionResult> PostFile(IList<IFormFile> files) public async Task<ActionResult> PostFile(
[FromServices] IHostingEnvironment hostingEnvironment,
IList<IFormFile> files)
{ {
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
@ -108,7 +107,7 @@ namespace MvcSample.Web
foreach (var f in files) foreach (var f in files)
{ {
await f.SaveAsAsync(Path.Combine(HostingEnvironment.WebRootPath, "test-file" + files.IndexOf(f))); await f.SaveAsAsync(Path.Combine(hostingEnvironment.WebRootPath, "test-file" + files.IndexOf(f)));
} }
return View(); return View();
} }

View File

@ -7,25 +7,9 @@ using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
/// <summary> /// <summary>
/// Specifies that a parameter or property should be bound using the request services. /// Specifies that an action parameter should be bound using the request services.
/// </summary> /// </summary>
/// <example> /// <example>
/// In this example, the LocationService property on the VehicleWithDealerViewModel class
/// will be bound to the value resolved for the ILocationService service from the request services.
///
/// <code>
/// public class VehicleWithDealerViewModel
/// {
/// [FromServices]
/// public ILocationService LocationService { get; set; }
///
/// public void Update()
/// {
/// LocationService.Update();
/// }
/// }
/// </code>
///
/// In this example an implementation of IProductModelRequestService is registered as a service. /// In this example an implementation of IProductModelRequestService is registered as a service.
/// Then in the GetProduct action, the parameter is bound to an instance of IProductModelRequestService /// Then in the GetProduct action, the parameter is bound to an instance of IProductModelRequestService
/// which is resolved from the the request services. /// which is resolved from the the request services.
@ -38,10 +22,10 @@ namespace Microsoft.AspNet.Mvc
/// } /// }
/// </code> /// </code>
/// </example> /// </example>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)] [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public class FromServicesAttribute : Attribute, IBindingSourceMetadata public class FromServicesAttribute : Attribute, IBindingSourceMetadata
{ {
/// <inheritdoc /> /// <inheritdoc />
public BindingSource BindingSource { get { return BindingSource.Services; } } public BindingSource BindingSource => BindingSource.Services;
} }
} }

View File

@ -10,6 +10,7 @@ using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.WebApiCompatShim; using Microsoft.AspNet.Mvc.WebApiCompatShim;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -22,6 +23,9 @@ namespace System.Web.Http
public abstract class ApiController : IDisposable public abstract class ApiController : IDisposable
{ {
private HttpRequestMessage _request; private HttpRequestMessage _request;
private IModelMetadataProvider _metadataProvider;
private IObjectModelValidator _objectValidator;
private IUrlHelper _urlHelper;
/// <summary> /// <summary>
/// Gets the action context. /// Gets the action context.
@ -52,12 +56,42 @@ namespace System.Web.Http
/// Gets the <see cref="IModelMetadataProvider"/>. /// Gets the <see cref="IModelMetadataProvider"/>.
/// </summary> /// </summary>
/// <remarks>The setter is intended for unit testing purposes only.</remarks> /// <remarks>The setter is intended for unit testing purposes only.</remarks>
[FromServices] public IModelMetadataProvider MetadataProvider
public IModelMetadataProvider MetadataProvider { get; set; } {
get
{
if (_metadataProvider == null)
{
_metadataProvider = Context?.RequestServices?.GetRequiredService<IModelMetadataProvider>();
}
return _metadataProvider;
}
set
{
_metadataProvider = value;
}
}
[FromServices] /// <summary>
public IObjectModelValidator ObjectValidator { get; set; } /// Gets or sets the <see cref="IObjectModelValidator"/>.
/// </summary>
public IObjectModelValidator ObjectValidator
{
get
{
if (_objectValidator == null)
{
_objectValidator = Context?.RequestServices?.GetRequiredService<IObjectModelValidator>();
}
return _objectValidator;
}
set
{
_objectValidator = value;
}
}
/// <summary> /// <summary>
/// Gets model state after the model binding process. This ModelState will be empty before model binding /// Gets model state after the model binding process. This ModelState will be empty before model binding
@ -96,8 +130,22 @@ namespace System.Web.Http
/// Gets a factory used to generate URLs to other APIs. /// Gets a factory used to generate URLs to other APIs.
/// </summary> /// </summary>
/// <remarks>The setter is intended for unit testing purposes only.</remarks> /// <remarks>The setter is intended for unit testing purposes only.</remarks>
[FromServices] public IUrlHelper Url
public IUrlHelper Url { get; set; } {
get
{
if (_urlHelper == null)
{
_urlHelper = Context?.RequestServices?.GetRequiredService<IUrlHelper>();
}
return _urlHelper;
}
set
{
_urlHelper = value;
}
}
/// <summary> /// <summary>
/// Gets or sets the current principal associated with this request. /// Gets or sets the current principal associated with this request.

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var modelType = typeof(BaseViewModel); var modelType = typeof(BaseViewModel);
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "BaseProperty"); var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == nameof(BaseModel.BaseProperty));
// Act // Act
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property); var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
@ -34,7 +34,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var modelType = typeof(BaseViewModel); var modelType = typeof(BaseViewModel);
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "TestProperty"); var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == nameof(BaseModel.TestProperty));
// Act // Act
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property); var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
@ -64,7 +64,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var modelType = typeof(DerivedViewModel); var modelType = typeof(DerivedViewModel);
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "BaseProperty"); var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == nameof(BaseModel.BaseProperty));
// Act // Act
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property); var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
@ -82,7 +82,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var modelType = typeof(DerivedViewModel); var modelType = typeof(DerivedViewModel);
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "TestProperty"); var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == nameof(BaseModel.TestProperty));
// Act // Act
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property); var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
@ -102,7 +102,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var modelType = typeof(DerivedViewModel); var modelType = typeof(DerivedViewModel);
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "VirtualProperty"); var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == nameof(BaseModel.VirtualProperty));
// Act // Act
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property); var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
@ -120,17 +120,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var modelType = typeof(DerivedViewModel); var modelType = typeof(DerivedViewModel);
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "Calculator"); var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == nameof(BaseModel.RouteValue));
// Act // Act
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property); var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
// Assert // Assert
Assert.Single(attributes.Attributes.OfType<RequiredAttribute>()); Assert.Single(attributes.Attributes.OfType<RequiredAttribute>());
Assert.Single(attributes.Attributes.OfType<FromServicesAttribute>()); Assert.Single(attributes.Attributes.OfType<FromRouteAttribute>());
Assert.Single(attributes.PropertyAttributes.OfType<RequiredAttribute>()); Assert.Single(attributes.PropertyAttributes.OfType<RequiredAttribute>());
Assert.Single(attributes.PropertyAttributes.OfType<FromServicesAttribute>()); Assert.Single(attributes.PropertyAttributes.OfType<FromRouteAttribute>());
} }
[Fact] [Fact]
@ -191,8 +191,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[Required] [Required]
public virtual int VirtualProperty { get; set; } public virtual int VirtualProperty { get; set; }
[FromServices] [FromRoute]
public ICalculator Calculator { get; set; } public string RouteValue { get; set; }
} }
private class DerivedModel : BaseModel private class DerivedModel : BaseModel
@ -218,7 +218,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public string BaseProperty { get; set; } public string BaseProperty { get; set; }
[Required] [Required]
public ICalculator Calculator { get; set; } public string RouteValue { get; set; }
} }
[ModelMetadataType(typeof(DerivedModel))] [ModelMetadataType(typeof(DerivedModel))]

View File

@ -25,7 +25,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
[InlineData("http://localhost/Other/FromFilter")] [InlineData("http://localhost/Other/FromFilter")]
[InlineData("http://localhost/Other/FromView")] [InlineData("http://localhost/Other/FromView")]
[InlineData("http://localhost/Other/FromViewComponent")] [InlineData("http://localhost/Other/FromViewComponent")]
[InlineData("http://localhost/Other/FromModelProperty")]
[InlineData("http://localhost/Other/FromActionArgument")] [InlineData("http://localhost/Other/FromActionArgument")]
public async Task RequestServices(string url) public async Task RequestServices(string url)
{ {
@ -40,6 +39,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var response = await Client.SendAsync(request); var response = await Client.SendAsync(request);
// Assert // Assert
response.EnsureSuccessStatusCode();
var body = (await response.Content.ReadAsStringAsync()).Trim(); var body = (await response.Content.ReadAsStringAsync()).Trim();
Assert.Equal(requestId, body); Assert.Equal(requestId, body);
} }

View File

@ -258,176 +258,6 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
} }
private class Order2
{
public int ProductId { get; set; }
public Person2 Customer { get; set; }
}
private class Person2
{
public string Name { get; set; }
[FromServices]
public IActionBindingContextAccessor BindingContext { get; set; }
}
[Fact]
public async Task MutableObjectModelBinder_BindsNestedPOCO_WithServicesModelBinder_WithPrefix_Success()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order2)
};
// Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements.
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?parameter.Customer.Name=bill");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order2>(modelBindingResult.Model);
Assert.NotNull(model.Customer);
Assert.Equal("bill", model.Customer.Name);
Assert.NotNull(model.Customer.BindingContext);
Assert.Equal(1, modelState.Count);
Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value;
Assert.Equal("bill", entry.AttemptedValue);
Assert.Equal("bill", entry.RawValue);
}
[Fact]
public async Task MutableObjectModelBinder_BindsNestedPOCO_WithServicesModelBinder_WithEmptyPrefix_Success()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order2)
};
// Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements.
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?Customer.Name=bill");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order2>(modelBindingResult.Model);
Assert.NotNull(model.Customer);
Assert.Equal("bill", model.Customer.Name);
Assert.NotNull(model.Customer.BindingContext);
Assert.Equal(1, modelState.Count);
Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "Customer.Name").Value;
Assert.Equal("bill", entry.AttemptedValue);
Assert.Equal("bill", entry.RawValue);
}
// We don't provide enough data in this test for the 'Person' model to be created. So even though there is
// a [FromServices], it won't be used.
[Fact]
public async Task MutableObjectModelBinder_BindsNestedPOCO_WithServicesModelBinder_WithPrefix_PartialData()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order2)
};
// Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements.
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?parameter.ProductId=10");
SetJsonBodyContent(request, AddressBodyContent);
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order2>(modelBindingResult.Model);
Assert.Null(model.Customer);
Assert.Equal(10, model.ProductId);
Assert.Equal(1, modelState.Count);
Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "parameter.ProductId").Value;
Assert.Equal("10", entry.AttemptedValue);
Assert.Equal("10", entry.RawValue);
}
// We don't provide enough data in this test for the 'Person' model to be created. So even though there is
// a [FromServices], it won't be used.
[Fact]
public async Task MutableObjectModelBinder_BindsNestedPOCO_WithServicesModelBinder_WithPrefix_NoData()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order2)
};
// Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements.
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?");
SetJsonBodyContent(request, AddressBodyContent);
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order2>(modelBindingResult.Model);
Assert.Null(model.Customer);
Assert.Equal(0, modelState.Count);
Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid);
}
private class Order3 private class Order3
{ {
public int ProductId { get; set; } public int ProductId { get; set; }
@ -1489,9 +1319,6 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
{ {
[FromBody] [FromBody]
public Address1 Address { get; set; } public Address1 Address { get; set; }
[FromServices]
public IActionBindingContextAccessor BindingContext { get; set; }
} }
// If a nested POCO object has all properties bound from a greedy source, then it should be populated // If a nested POCO object has all properties bound from a greedy source, then it should be populated
@ -1524,7 +1351,6 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var model = Assert.IsType<Order9>(modelBindingResult.Model); var model = Assert.IsType<Order9>(modelBindingResult.Model);
Assert.NotNull(model.Customer); Assert.NotNull(model.Customer);
Assert.NotNull(model.Customer.BindingContext);
Assert.NotNull(model.Customer.Address); Assert.NotNull(model.Customer.Address);
Assert.Equal(AddressStreetContent, model.Customer.Address.Street); Assert.Equal(AddressStreetContent, model.Customer.Address.Street);

View File

@ -13,90 +13,6 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
{ {
public class ServicesModelBinderIntegrationTest public class ServicesModelBinderIntegrationTest
{ {
private class Person
{
public Address Address { get; set; }
}
private class Address
{
// Using a service type already in defaults.
[FromServices]
public JsonOutputFormatter OutputFormatter { get; set; }
}
[Fact]
public async Task BindPropertyFromService_WithData_WithPrefix_GetsBound()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "Parameter1",
BindingInfo = new BindingInfo()
{
BinderModelName = "CustomParameter",
},
ParameterType = typeof(Person)
};
var operationContext = ModelBindingTestHelper.GetOperationBindingContext();
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
// ModelBindingResult
Assert.True(modelBindingResult.IsModelSet);
// Model
var boundPerson = Assert.IsType<Person>(modelBindingResult.Model);
Assert.NotNull(boundPerson);
Assert.NotNull(boundPerson.Address.OutputFormatter);
// ModelState
Assert.True(modelState.IsValid);
Assert.Empty(modelState.Keys);
}
[Fact]
public async Task BindPropertyFromService_WithData_WithEmptyPrefix_GetsBound()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "Parameter1",
BindingInfo = new BindingInfo(),
ParameterType = typeof(Person)
};
var operationContext = ModelBindingTestHelper.GetOperationBindingContext();
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
// ModelBindingResult
Assert.True(modelBindingResult.IsModelSet);
// Model
var boundPerson = Assert.IsType<Person>(modelBindingResult.Model);
Assert.NotNull(boundPerson);
Assert.NotNull(boundPerson.Address.OutputFormatter);
// ModelState
Assert.True(modelState.IsValid);
// For non user bound models there should be no entry in model state.
Assert.Empty(modelState);
}
[Fact] [Fact]
public async Task BindParameterFromService_WithData_GetsBound() public async Task BindParameterFromService_WithData_GetsBound()
{ {

View File

@ -10,9 +10,12 @@ namespace ActionResultsWebSite
{ {
public class ActionResultsVerificationController : Controller public class ActionResultsVerificationController : Controller
{ {
public ActionResultsVerificationController(GuidLookupService guidLookupService)
{
GuidLookupService = guidLookupService;
}
[FromServices] public GuidLookupService GuidLookupService { get; }
public GuidLookupService Service { get; set; }
public IActionResult Index([FromBody]DummyClass test) public IActionResult Index([FromBody]DummyClass test)
{ {
@ -107,7 +110,7 @@ namespace ActionResultsWebSite
public bool GetDisposeCalled(string guid) public bool GetDisposeCalled(string guid)
{ {
bool value; bool value;
if (Service.IsDisposed.TryGetValue(guid, out value)) if (GuidLookupService.IsDisposed.TryGetValue(guid, out value))
{ {
return value; return value;
} }
@ -131,7 +134,7 @@ namespace ActionResultsWebSite
private DisposableType CreateDisposableType(string guid) private DisposableType CreateDisposableType(string guid)
{ {
return new DisposableType(Service, guid); return new DisposableType(GuidLookupService, guid);
} }
private class DisposableType : IDisposable private class DisposableType : IDisposable

View File

@ -8,9 +8,6 @@ namespace ActivatorWebSite
{ {
public class PlainController public class PlainController
{ {
[FromServices]
public MyService Service { get; set; }
[ActionContext] [ActionContext]
public ActionContext ActionContext { get; set; } public ActionContext ActionContext { get; set; }
@ -18,12 +15,12 @@ namespace ActivatorWebSite
public HttpResponse Response => ActionContext.HttpContext.Response; public HttpResponse Response => ActionContext.HttpContext.Response;
public IActionResult Index() public IActionResult Index([FromServices] MyService service)
{ {
Response.Headers["X-Fake-Header"] = "Fake-Value"; Response.Headers["X-Fake-Header"] = "Fake-Value";
var value = Request.Query["foo"]; var value = Request.Query["foo"];
return new ContentResult { Content = Service.Random + "|" + value }; return new ContentResult { Content = service.Random + "|" + value };
} }
} }
} }

View File

@ -1,9 +0,0 @@
// 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.
namespace ApiExplorerWebSite
{
public interface IOrderRepository
{
}
}

View File

@ -7,9 +7,6 @@ namespace ApiExplorerWebSite
{ {
public class OrderDTO public class OrderDTO
{ {
[FromServices]
public IOrderRepository Repository { get; set; }
public string CustomerId { get; set; } public string CustomerId { get; set; }
[FromHeader(Name = "Referrer")] [FromHeader(Name = "Referrer")]

View File

@ -8,9 +8,6 @@ namespace HtmlGenerationWebSite.Controllers
{ {
public class Catalog_CacheTagHelperController : Controller public class Catalog_CacheTagHelperController : Controller
{ {
[FromServices]
public ProductsService ProductsService { get; set; }
[HttpGet("/catalog")] [HttpGet("/catalog")]
public IActionResult Splash(int categoryId, int correlationId, [FromHeader] string locale) public IActionResult Splash(int categoryId, int correlationId, [FromHeader] string locale)
{ {
@ -75,9 +72,9 @@ namespace HtmlGenerationWebSite.Controllers
} }
[HttpPost("/categories/update-products")] [HttpPost("/categories/update-products")]
public void UpdateCategories() public void UpdateCategories([FromServices] ProductsService productsService)
{ {
ProductsService.UpdateProducts(); productsService.UpdateProducts();
} }
[HttpGet("/catalog/GetDealPercentage/{dealPercentage}")] [HttpGet("/catalog/GetDealPercentage/{dealPercentage}")]

View File

@ -7,9 +7,6 @@ namespace ModelBindingWebSite
{ {
public class CalculatorContext public class CalculatorContext
{ {
[FromServices]
public ICalculator Calculator { get; set; }
public int Left { get; set; } public int Left { get; set; }
public int Right { get; set; } public int Right { get; set; }

View File

@ -7,9 +7,9 @@ namespace ModelBindingWebSite.Controllers
{ {
public class FromServices_CalculatorController : Controller public class FromServices_CalculatorController : Controller
{ {
public int Calculate(CalculatorContext context) public int Calculate(CalculatorContext context, [FromServices] ICalculator calculator)
{ {
return context.Calculator.Operation(context.Operator, context.Left, context.Right); return calculator.Operation(context.Operator, context.Left, context.Right);
} }
public int Add(int left, int right, [FromServices] ICalculator calculator) public int Add(int left, int right, [FromServices] ICalculator calculator)
@ -17,9 +17,9 @@ namespace ModelBindingWebSite.Controllers
return calculator.Operation('+', left, right); return calculator.Operation('+', left, right);
} }
public int CalculateWithPrecision(DefaultCalculatorContext context) public int CalculateWithPrecision(DefaultCalculatorContext context, [FromServices] ICalculator calculator)
{ {
return context.Calculator.Operation(context.Operator, context.Left, context.Right); return calculator.Operation(context.Operator, context.Left, context.Right);
} }
} }
} }

View File

@ -18,7 +18,11 @@ namespace ModelBindingWebSite.Controllers
private IHtmlHelper<Person> _personHelper; private IHtmlHelper<Person> _personHelper;
private bool _activated; private bool _activated;
[FromServices] public RoundtripController(IHtmlHelper<Person> personHelper)
{
_personHelper = personHelper;
}
public IHtmlHelper<Person> PersonHelper public IHtmlHelper<Person> PersonHelper
{ {
get get
@ -35,15 +39,11 @@ namespace ModelBindingWebSite.Controllers
TextWriter.Null, TextWriter.Null,
new HtmlHelperOptions()); new HtmlHelperOptions());
((ICanHasViewContext)PersonHelper).Contextualize(context); ((ICanHasViewContext)_personHelper).Contextualize(context);
} }
return _personHelper; return _personHelper;
} }
set
{
_personHelper = value;
}
} }
public string GetPerson() public string GetPerson()

View File

@ -8,9 +8,6 @@ namespace ModelBindingWebSite.Controllers
[Route("Validation/[Action]")] [Route("Validation/[Action]")]
public class ValidationController : Controller public class ValidationController : Controller
{ {
[FromServices]
public ITestService ControllerService { get; set; }
public object AvoidRecursive(SelfishPerson selfishPerson) public object AvoidRecursive(SelfishPerson selfishPerson)
{ {
return ModelState.IsValid; return ModelState.IsValid;

View File

@ -59,7 +59,6 @@ namespace ModelBindingWebSite
return PartialView("UpdateVehicle", model); return PartialView("UpdateVehicle", model);
} }
model.Update();
return PartialView("UpdateSuccessful", model); return PartialView("UpdateSuccessful", model);
} }

View File

@ -1,15 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.ComponentModel.DataAnnotations;
namespace ModelBindingWebSite namespace ModelBindingWebSite
{ {
public interface ITestService public interface ITestService
{ {
[Required]
string NeverBound { get; set; }
bool Test(); bool Test();
} }
} }

View File

@ -1,14 +0,0 @@
// 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 ModelBindingWebSite.ViewModels;
namespace ModelBindingWebSite.Services
{
public interface ILocationService
{
bool IsValidMakeForRegion(string make, string region);
bool Update(VehicleWithDealerViewModel model);
}
}

View File

@ -1,28 +0,0 @@
// 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 ModelBindingWebSite.ViewModels;
namespace ModelBindingWebSite.Services
{
public class LocationService : ILocationService
{
public bool Update(VehicleWithDealerViewModel viewModel)
{
return true;
}
public bool IsValidMakeForRegion(string make, string region)
{
switch (make)
{
case "Acme":
return region == "NW" || "region" == "South Central";
case "FastCars":
return region != "Central";
}
return true;
}
}
}

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder; using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Mvc;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using ModelBindingWebSite.Models; using ModelBindingWebSite.Models;
using ModelBindingWebSite.Services; using ModelBindingWebSite.Services;
@ -32,7 +31,6 @@ namespace ModelBindingWebSite
services.AddSingleton<ITestService, TestService>(); services.AddSingleton<ITestService, TestService>();
services.AddTransient<IVehicleService, VehicleService>(); services.AddTransient<IVehicleService, VehicleService>();
services.AddTransient<ILocationService, LocationService>();
} }
public void Configure(IApplicationBuilder app) public void Configure(IApplicationBuilder app)

View File

@ -1,18 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.ComponentModel.DataAnnotations;
namespace ModelBindingWebSite namespace ModelBindingWebSite
{ {
public class TestService : ITestService public class TestService : ITestService
{ {
[Required] public bool Test() => true;
public string NeverBound { get; set; }
public bool Test()
{
return true;
}
} }
} }

View File

@ -38,8 +38,9 @@ namespace ModelBindingWebSite.ViewModels
{ {
if (InspectedDates.Any(d => d.Year > Year)) if (InspectedDates.Any(d => d.Year > Year))
{ {
yield return new ValidationResult("Inspection date cannot be later than year of manufacture.", yield return new ValidationResult(
new[] { nameof(InspectedDates) }); "Inspection date cannot be later than year of manufacture.",
new[] { nameof(InspectedDates) });
} }
} }

View File

@ -4,7 +4,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc;
using ModelBindingWebSite.Services;
namespace ModelBindingWebSite.ViewModels namespace ModelBindingWebSite.ViewModels
{ {
@ -17,23 +16,28 @@ namespace ModelBindingWebSite.ViewModels
[FromBody] [FromBody]
public VehicleViewModel Vehicle { get; set; } public VehicleViewModel Vehicle { get; set; }
[FromServices]
public ILocationService LocationService { get; set; }
[FromHeader(Name = "X-TrackingId")] [FromHeader(Name = "X-TrackingId")]
public string TrackingId { get; set; } = "default-tracking-id"; public string TrackingId { get; set; } = "default-tracking-id";
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{ {
if (!LocationService.IsValidMakeForRegion(Vehicle.Make, Dealer.Location)) if (!IsValidMakeForRegion(Vehicle.Make, Dealer.Location))
{ {
yield return new ValidationResult("Make is invalid for region."); yield return new ValidationResult("Make is invalid for region.");
} }
} }
public void Update() public bool IsValidMakeForRegion(string make, string region)
{ {
LocationService.Update(this); switch (make)
{
case "Acme":
return region == "NW" || "region" == "South Central";
case "FastCars":
return region != "Central";
}
return true;
} }
} }
} }

View File

@ -40,12 +40,6 @@ namespace RequestServicesWebSite
return View("ViewComponent"); return View("ViewComponent");
} }
[HttpGet]
public string FromModelProperty(RequestModel requestContext)
{
return requestContext.RequestIdService.RequestId;
}
[HttpGet] [HttpGet]
public string FromActionArgument([FromServices] RequestIdService requestIdService) public string FromActionArgument([FromServices] RequestIdService requestIdService)
{ {

View File

@ -8,13 +8,10 @@ namespace RequestServicesWebSite
[Route("RequestScoped/[action]")] [Route("RequestScoped/[action]")]
public class RequestScopedServiceController public class RequestScopedServiceController
{ {
[FromServices]
public RequestIdService RequestIdService { get; set; }
[HttpGet] [HttpGet]
public string FromController() public string FromController([FromServices] RequestIdService requestIdService)
{ {
return RequestIdService.RequestId; return requestIdService.RequestId;
} }
} }
} }

View File

@ -1,13 +0,0 @@
// 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 Microsoft.AspNet.Mvc;
namespace RequestServicesWebSite
{
public class RequestModel
{
[FromServices]
public RequestIdService RequestIdService { get; set; }
}
}

View File

@ -15,9 +15,6 @@ namespace WebApiCompatShimWebSite
{ {
public class BasicApiController : ApiController public class BasicApiController : ApiController
{ {
[FromServices]
public IOptions<WebApiCompatShimOptions> OptionsAccessor { get; set; }
// Verifies property activation // Verifies property activation
[HttpGet] [HttpGet]
public async Task<IActionResult> WriteToHttpContext() public async Task<IActionResult> WriteToHttpContext()
@ -43,9 +40,9 @@ namespace WebApiCompatShimWebSite
// Verifies the default options configure formatters correctly. // Verifies the default options configure formatters correctly.
[HttpGet] [HttpGet]
public string[] GetFormatters() public string[] GetFormatters([FromServices] IOptions<WebApiCompatShimOptions> optionsAccessor)
{ {
return OptionsAccessor.Value.Formatters.Select(f => f.GetType().FullName).ToArray(); return optionsAccessor.Value.Formatters.Select(f => f.GetType().FullName).ToArray();
} }
[HttpGet] [HttpGet]