Adding additional functional tests for ModelBinding
This commit is contained in:
parent
e078076408
commit
ce8d840cc6
|
|
@ -7,11 +7,14 @@ using System.Linq;
|
|||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using ModelBindingWebSite;
|
||||
using ModelBindingWebSite.ViewModels;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -1083,5 +1086,244 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
// Should Update all included properties.
|
||||
Assert.Equal("March", user.RegisterationMonth);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateVehicle_WithJson_ProducesModelStateErrors()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
var content = new
|
||||
{
|
||||
Year = 3012,
|
||||
InspectedDates = new[]
|
||||
{
|
||||
new DateTime(4065, 10, 10)
|
||||
},
|
||||
Make = "Volttrax",
|
||||
Model = "Epsum"
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await client.PutAsJsonAsync("http://localhost/api/vehicles/520", content);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var modelStateErrors = JsonConvert.DeserializeObject<IDictionary<string, IEnumerable<string>>>(body);
|
||||
|
||||
Assert.Equal(3, modelStateErrors.Count);
|
||||
Assert.Equal(new[] {
|
||||
"The field Year must be between 1980 and 2034.",
|
||||
"Year is invalid"
|
||||
}, modelStateErrors["model.Year"]);
|
||||
|
||||
var vinError = Assert.Single(modelStateErrors["model.Vin"]);
|
||||
Assert.Equal("The Vin field is required.", vinError);
|
||||
|
||||
var trackingIdError = Assert.Single(modelStateErrors["X-TrackingId"]);
|
||||
Assert.Equal("A value is required but was not present in the request.", trackingIdError);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateVehicle_WithJson_DoesPropertyValidationPriorToValidationAtType()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
var content = new
|
||||
{
|
||||
Year = 2007,
|
||||
InspectedDates = new[]
|
||||
{
|
||||
new DateTime(4065, 10, 10)
|
||||
},
|
||||
Make = "Volttrax",
|
||||
Model = "Epsum",
|
||||
Vin = "Pqrs"
|
||||
};
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation("X-TrackingId", "trackingid");
|
||||
|
||||
// Act
|
||||
var response = await client.PutAsJsonAsync("http://localhost/api/vehicles/520", content);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var modelStateErrors = JsonConvert.DeserializeObject<IDictionary<string, IEnumerable<string>>>(body);
|
||||
|
||||
var item = Assert.Single(modelStateErrors);
|
||||
Assert.Equal("model.InspectedDates", item.Key);
|
||||
var error = Assert.Single(item.Value);
|
||||
Assert.Equal("Inspection date cannot be later than year of manufacture.", error);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateVehicle_WithJson_BindsBodyAndServices()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
var trackingId = Guid.NewGuid().ToString();
|
||||
var postedContent = new
|
||||
{
|
||||
Year = 2010,
|
||||
InspectedDates = new List<DateTime>
|
||||
{
|
||||
new DateTime(2008, 10, 01),
|
||||
new DateTime(2009, 03, 01),
|
||||
},
|
||||
Make = "Volttrax",
|
||||
Model = "Epsum",
|
||||
Vin = "PQRS"
|
||||
};
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation("X-TrackingId", trackingId);
|
||||
|
||||
// Act
|
||||
var response = await client.PutAsJsonAsync("http://localhost/api/vehicles/520", postedContent);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var actual = JsonConvert.DeserializeObject<VehicleViewModel>(body);
|
||||
|
||||
Assert.Equal(postedContent.Vin, actual.Vin);
|
||||
Assert.Equal(postedContent.Make, actual.Make);
|
||||
Assert.Equal(postedContent.InspectedDates, actual.InspectedDates.Select(d => d.DateTime));
|
||||
Assert.Equal(trackingId, actual.LastUpdatedTrackingId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateVehicle_WithXml_BindsBodyServicesAndHeaders()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
var trackingId = Guid.NewGuid().ToString();
|
||||
var postedContent = new VehicleViewModel
|
||||
{
|
||||
Year = 2010,
|
||||
InspectedDates = new DateTimeOffset[]
|
||||
{
|
||||
new DateTimeOffset(2008, 10, 01, 8, 3, 1, TimeSpan.Zero),
|
||||
new DateTime(2009, 03, 01),
|
||||
},
|
||||
Make = "Volttrax",
|
||||
Model = "Epsum",
|
||||
Vin = "PQRS"
|
||||
};
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation("X-TrackingId", trackingId);
|
||||
|
||||
// Act
|
||||
var response = await client.PutAsXmlAsync("http://localhost/api/vehicles/520", postedContent);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var actual = JsonConvert.DeserializeObject<VehicleViewModel>(body);
|
||||
|
||||
Assert.Equal(postedContent.Vin, actual.Vin);
|
||||
Assert.Equal(postedContent.Make, actual.Make);
|
||||
Assert.Equal(postedContent.InspectedDates, actual.InspectedDates);
|
||||
Assert.Equal(trackingId, actual.LastUpdatedTrackingId);
|
||||
}
|
||||
|
||||
// Simulates a browser based client that does a Ajax post for partial page updates.
|
||||
[Fact]
|
||||
public async Task UpdateDealerVehicle_PopulatesPropertyErrorsInViews()
|
||||
{
|
||||
// Arrange
|
||||
var expectedContent = await GetType().GetTypeInfo().Assembly.ReadResourceAsStringAsync(
|
||||
"compiler/resources/UpdateDealerVehicle_PopulatesPropertyErrorsInViews.txt");
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
var postedContent = new
|
||||
{
|
||||
Year = 9001,
|
||||
InspectedDates = new List<DateTime>
|
||||
{
|
||||
new DateTime(2008, 01, 01)
|
||||
},
|
||||
Make = "Acme",
|
||||
Model = "Epsum",
|
||||
Vin = "LongerThan8Chars",
|
||||
|
||||
};
|
||||
var url = "http://localhost/dealers/32/update-vehicle?dealer.name=TestCarDealer&dealer.location=SE";
|
||||
|
||||
// Act
|
||||
var response = await client.PostAsJsonAsync(url, postedContent);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal(expectedContent, body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateDealerVehicle_PopulatesValidationSummary()
|
||||
{
|
||||
// Arrange
|
||||
var expectedContent = await GetType().GetTypeInfo().Assembly.ReadResourceAsStringAsync(
|
||||
"compiler/resources/UpdateDealerVehicle_PopulatesValidationSummary.txt");
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
var postedContent = new
|
||||
{
|
||||
Year = 2013,
|
||||
InspectedDates = new List<DateTime>
|
||||
{
|
||||
new DateTime(2008, 01, 01)
|
||||
},
|
||||
Make = "Acme",
|
||||
Model = "Epsum",
|
||||
Vin = "8chars",
|
||||
|
||||
};
|
||||
var url = "http://localhost/dealers/43/update-vehicle?dealer.name=TestCarDealer&dealer.location=SE";
|
||||
|
||||
// Act
|
||||
var response = await client.PostAsJsonAsync(url, postedContent);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal(expectedContent, body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateDealerVehicle_UsesDefaultValuesForOptionalProperties()
|
||||
{
|
||||
// Arrange
|
||||
var expectedContent = await GetType().GetTypeInfo().Assembly.ReadResourceAsStringAsync(
|
||||
"compiler/resources/UpdateDealerVehicle_UpdateSuccessful.txt");
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
var postedContent = new
|
||||
{
|
||||
Year = 2013,
|
||||
InspectedDates = new DateTimeOffset[]
|
||||
{
|
||||
new DateTime(2008, 11, 01)
|
||||
},
|
||||
Make = "RealSlowCars",
|
||||
Model = "Epsum",
|
||||
Vin = "8chars",
|
||||
|
||||
};
|
||||
var url = "http://localhost/dealers/43/update-vehicle?dealer.name=TestCarDealer&dealer.location=NE";
|
||||
|
||||
// Act
|
||||
var response = await client.PostAsJsonAsync(url, postedContent);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal(expectedContent, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<div>
|
||||
<span class="bold">TestCarDealer</span>
|
||||
<em>SE</em>
|
||||
<input id="Dealer_Id" name="Dealer.Id" type="hidden" value="32" />
|
||||
</div>
|
||||
<div class="validation-summary-errors"><ul><li style="display:none"></li>
|
||||
</ul></div>
|
||||
<form action="/dealers/32/update-vehicle?dealer.name=TestCarDealer&dealer.location=SE" method="post"> <fieldset>
|
||||
<input class="input-validation-error" data-val="true" data-val-length="The field Vin must be a string with a maximum length of 8." data-val-length-max="8" data-val-required="The Vin field is required." id="Vehicle_Vin" name="Vehicle.Vin" type="text" value="LongerThan8Chars" />
|
||||
<span class="field-validation-error" data-valmsg-for="Vehicle.Vin" data-valmsg-replace="true">The field Vin must be a string with a maximum length of 8.</span>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<input class="input-validation-error text-box single-line" data-val="true" data-val-range="The field Year must be between 1980 and 2034." data-val-range-max="2034" data-val-range-min="1980" id="Vehicle_Year" name="Vehicle.Year" type="number" value="9001" />
|
||||
<span class="field-validation-error" data-valmsg-for="Vehicle.Year" data-valmsg-replace="true">The field Year must be between 1980 and 2034.</span>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<div>
|
||||
<span class="bold">TestCarDealer</span>
|
||||
<em>SE</em>
|
||||
<input id="Dealer_Id" name="Dealer.Id" type="hidden" value="43" />
|
||||
</div>
|
||||
<div class="validation-summary-errors"><ul><li>Make is invalid for region.</li>
|
||||
</ul></div>
|
||||
<form action="/dealers/43/update-vehicle?dealer.name=TestCarDealer&dealer.location=SE" method="post"> <fieldset>
|
||||
<input data-val="true" data-val-length="The field Vin must be a string with a maximum length of 8." data-val-length-max="8" data-val-required="The Vin field is required." id="Vehicle_Vin" name="Vehicle.Vin" type="text" value="8chars" />
|
||||
<span class="field-validation-valid" data-valmsg-for="Vehicle.Vin" data-valmsg-replace="true"></span>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<input class="text-box single-line" data-val="true" data-val-range="The field Year must be between 1980 and 2034." data-val-range-max="2034" data-val-range-min="1980" id="Vehicle_Year" name="Vehicle.Year" type="number" value="2013" />
|
||||
<span class="field-validation-valid" data-valmsg-for="Vehicle.Year" data-valmsg-replace="true"></span>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<div class="left">
|
||||
<ul>
|
||||
<li>
|
||||
Vin: 8chars
|
||||
</li>
|
||||
<li>
|
||||
Inspected Dates: 11/1/2008 12:00:00 AM -07:00
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li>
|
||||
Dealer: 43
|
||||
</li>
|
||||
<li>
|
||||
Phone: 999-99-0000
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
Tracked by
|
||||
</footer>
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using ModelBindingWebSite.Services;
|
||||
using ModelBindingWebSite.ViewModels;
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class VehicleController : Controller
|
||||
{
|
||||
[HttpPut("/api/vehicles/{id}")]
|
||||
[Produces("application/json")]
|
||||
public object UpdateVehicleApi(
|
||||
[Range(1, 500)] int id,
|
||||
[FromBody] VehicleViewModel model,
|
||||
[FromServices] IVehicleService service,
|
||||
[FromHeader(Name = "X-TrackingId")] string trackingId)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return SerializeModelState();
|
||||
}
|
||||
|
||||
service.Update(id, model, trackingId);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
[HttpPost("/dealers/{dealer.id:int}/update-vehicle")]
|
||||
public IActionResult UpdateDealerVehicle(VehicleWithDealerViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return PartialView("UpdateVehicle", model);
|
||||
}
|
||||
|
||||
model.Update();
|
||||
return PartialView("UpdateSuccessful", model);
|
||||
}
|
||||
|
||||
public IDictionary<string, IEnumerable<string>> SerializeModelState()
|
||||
{
|
||||
Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
|
||||
return ModelState.Where(item => item.Value.Errors.Count > 0)
|
||||
.ToDictionary(item => item.Key, item => item.Value.Errors.Select(e => e.ErrorMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// 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 ModelBindingWebSite.ViewModels;
|
||||
|
||||
namespace ModelBindingWebSite.Services
|
||||
{
|
||||
public interface ILocationService
|
||||
{
|
||||
bool IsValidMakeForRegion(string make, string region);
|
||||
|
||||
bool Update(VehicleWithDealerViewModel model);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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 ModelBindingWebSite.ViewModels;
|
||||
|
||||
namespace ModelBindingWebSite.Services
|
||||
{
|
||||
public interface IVehicleService
|
||||
{
|
||||
void Update(int id, VehicleViewModel vehicle, string trackingId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using ModelBindingWebSite.ViewModels;
|
||||
|
||||
namespace ModelBindingWebSite.Services
|
||||
{
|
||||
public class VehicleService : IVehicleService
|
||||
{
|
||||
public void Update(int id, VehicleViewModel vehicle, string trackingId)
|
||||
{
|
||||
if (trackingId == null)
|
||||
{
|
||||
throw new ArgumentException(nameof(trackingId));
|
||||
}
|
||||
|
||||
vehicle.LastUpdatedTrackingId = trackingId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,8 @@
|
|||
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Logging;
|
||||
using ModelBindingWebSite.Services;
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
|
|
@ -28,17 +27,15 @@ namespace ModelBindingWebSite
|
|||
|
||||
services.AddSingleton<ICalculator, DefaultCalculator>();
|
||||
services.AddSingleton<ITestService, TestService>();
|
||||
|
||||
services.AddTransient<IVehicleService, VehicleService>();
|
||||
services.AddTransient<ILocationService, LocationService>();
|
||||
});
|
||||
|
||||
app.UseErrorReporter();
|
||||
|
||||
// Add MVC to the request pipeline
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
routes.MapRoute("ActionAsMethod", "{controller}/{action}",
|
||||
defaults: new { controller = "Home", action = "Index" });
|
||||
|
||||
});
|
||||
app.UseMvc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ModelBindingWebSite.ViewModels
|
||||
{
|
||||
public class DealerViewModel
|
||||
{
|
||||
private const string DefaultCustomerServiceNumber = "999-99-0000";
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Location { get; set; }
|
||||
|
||||
[DataType(DataType.PhoneNumber)]
|
||||
public string Phone { get; set; } = DefaultCustomerServiceNumber;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// 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.Linq;
|
||||
|
||||
namespace ModelBindingWebSite.ViewModels
|
||||
{
|
||||
public class VehicleViewModel : IValidatableObject
|
||||
{
|
||||
[Required]
|
||||
[StringLength(8)]
|
||||
public string Vin { get; set; }
|
||||
|
||||
public string Make { get; set; }
|
||||
|
||||
public string Model { get; set; }
|
||||
|
||||
[Range(1980, 2034)]
|
||||
[CustomValidation(typeof(VehicleViewModel), nameof(ValidateYear))]
|
||||
public int Year { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(10)]
|
||||
public DateTimeOffset[] InspectedDates { get; set; }
|
||||
|
||||
public string LastUpdatedTrackingId { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (InspectedDates.Any(d => d.Year > Year))
|
||||
{
|
||||
yield return new ValidationResult("Inspection date cannot be later than year of manufacture.",
|
||||
new[] { nameof(InspectedDates) });
|
||||
}
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateYear(int year)
|
||||
{
|
||||
if (year > DateTime.UtcNow.Year)
|
||||
{
|
||||
return new ValidationResult("Year is invalid");
|
||||
}
|
||||
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using ModelBindingWebSite.Services;
|
||||
|
||||
namespace ModelBindingWebSite.ViewModels
|
||||
{
|
||||
public class VehicleWithDealerViewModel : IValidatableObject
|
||||
{
|
||||
[Required]
|
||||
public DealerViewModel Dealer { get; set; }
|
||||
|
||||
[Required]
|
||||
[FromBody]
|
||||
public VehicleViewModel Vehicle { get; set; }
|
||||
|
||||
[FromServices]
|
||||
public ILocationService LocationService { get; set; }
|
||||
|
||||
[FromHeader(Name = "X-TrackingId")]
|
||||
public string TrackingId { get; set; } = "default-tracking-id";
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (!LocationService.IsValidMakeForRegion(Vehicle.Make, Dealer.Location))
|
||||
{
|
||||
yield return new ValidationResult("Make is invalid for region.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
LocationService.Update(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
@model ModelBindingWebSite.ViewModels.VehicleWithDealerViewModel
|
||||
|
||||
<div class="left">
|
||||
<ul>
|
||||
<li>
|
||||
Vin: @Html.DisplayFor(m => m.Vehicle.Vin)
|
||||
</li>
|
||||
<li>
|
||||
Inspected Dates: @Html.DisplayFor(m => m.Vehicle.InspectedDates)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li>
|
||||
Dealer: @Html.DisplayFor(m => m.Dealer.Id)
|
||||
</li>
|
||||
<li>
|
||||
Phone: @Html.DisplayFor(m => m.Dealer.Phone)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
Tracked by @Model.TrackingId
|
||||
</footer>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
@model ModelBindingWebSite.ViewModels.VehicleWithDealerViewModel
|
||||
|
||||
<div>
|
||||
<span class="bold">@Model.Dealer.Name</span>
|
||||
<em>@Model.Dealer.Location</em>
|
||||
@Html.HiddenFor(m => m.Dealer.Id)
|
||||
</div>
|
||||
@Html.ValidationSummary(excludePropertyErrors: true)
|
||||
@using (Html.BeginForm())
|
||||
{
|
||||
<fieldset>
|
||||
@Html.TextBoxFor(m => m.Vehicle.Vin)
|
||||
@Html.ValidationMessageFor(m => m.Vehicle.Vin)
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
@Html.EditorFor(m => m.Vehicle.Year)
|
||||
@Html.ValidationMessageFor(m => m.Vehicle.Year)
|
||||
</fieldset>
|
||||
}
|
||||
Loading…
Reference in New Issue