C# 7 language feature updates
Controllers/AccountController: Replace single line methods with expression bodied members. Includes constructor, C# 7 feature. Controllers/CatalogController: Remove unnecessary ToString() call. Replace single line methods with expression bodied members. Extensions/HttpClientExtensions: Replace single line methods with expression bodied members. Extensions/SessionExtensions: Replace single line methods with expression bodied members. Services/BasketService: Remove unnecessary ToString() calls. Add ?? to simplify conditional initialization Use TryGetValue and out variable initialization to simplify Quantity calculation Services/CatalogService: Use Value<T> generic method instead of dynamic types. There is a lot of overhead for dynamic and it seems overkill here. Services/IdentityParser: Use the pattern matching is expression. Refactor the LINQ queries to enumerate the collection (and create an enumerator) only once. The previous code had 3 enumerations. Services/Utilities/HttpApiClient: Remove the 'async' modifier and 'await' statements from methods where the only asynchronous statement is the final statement of the method, and the task from the underlying method can be returned. Services/Utilities/HttpApiClientWrapper: Use expression bodied members where applicable. Remove the 'async' modifier and 'await' statements from methods where the only asynchronous statement is the final statement of the method, and the task from the underlying method can be returned. ViewComponents/Cart: Use expression bodied members where applicable. ViewComponents/CartList: Use expression bodied members where applicable. Remove the 'async' modifier and 'await' statements from methods where the only asynchronous statement is the final statement of the method, and the task from the underlying method can be returned. ViewModels/Annotations/CardExpiration: Use the out variable initializer to simplify the validation of the card expiration date. ViewModels/Basket: Use property initializer syntax instead of constructor body ViewModels/CatalogViewModels/IndexViewModel: Use expression bodied property to return the calculated 'Disabled' property ViewModels/Order: Use property initializer syntax.
This commit is contained in:
parent
aee1ac6a06
commit
00491910a2
@ -11,15 +11,10 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
|
|||||||
public class AccountController : Controller
|
public class AccountController : Controller
|
||||||
{
|
{
|
||||||
private readonly IIdentityParser<ApplicationUser> _identityParser;
|
private readonly IIdentityParser<ApplicationUser> _identityParser;
|
||||||
public AccountController(IIdentityParser<ApplicationUser> identityParser)
|
public AccountController(IIdentityParser<ApplicationUser> identityParser) =>
|
||||||
{
|
|
||||||
_identityParser = identityParser;
|
_identityParser = identityParser;
|
||||||
}
|
|
||||||
|
|
||||||
public ActionResult Index()
|
public ActionResult Index() => View();
|
||||||
{
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public IActionResult SignIn(string returnUrl)
|
public IActionResult SignIn(string returnUrl)
|
||||||
|
@ -14,10 +14,8 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
|
|||||||
{
|
{
|
||||||
private ICatalogService _catalogSvc;
|
private ICatalogService _catalogSvc;
|
||||||
|
|
||||||
public CatalogController(ICatalogService catalogSvc)
|
public CatalogController(ICatalogService catalogSvc) =>
|
||||||
{
|
|
||||||
_catalogSvc = catalogSvc;
|
_catalogSvc = catalogSvc;
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IActionResult> Index(int? BrandFilterApplied, int? TypesFilterApplied, int? page)
|
public async Task<IActionResult> Index(int? BrandFilterApplied, int? TypesFilterApplied, int? page)
|
||||||
{
|
{
|
||||||
@ -35,7 +33,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
|
|||||||
ActualPage = page ?? 0,
|
ActualPage = page ?? 0,
|
||||||
ItemsPerPage = catalog.Data.Count,
|
ItemsPerPage = catalog.Data.Count,
|
||||||
TotalItems = catalog.Count,
|
TotalItems = catalog.Count,
|
||||||
TotalPages = int.Parse(Math.Ceiling(((decimal)catalog.Count / itemsPage)).ToString())
|
TotalPages = (int)Math.Ceiling(((decimal)catalog.Count / itemsPage))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -45,10 +43,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
|
|||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IActionResult Error()
|
public IActionResult Error() => View();
|
||||||
{
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,20 +11,14 @@ namespace Microsoft.eShopOnContainers.WebMVC.Extensions
|
|||||||
{
|
{
|
||||||
public static class HttpClientExtensions
|
public static class HttpClientExtensions
|
||||||
{
|
{
|
||||||
public static void SetBasicAuthentication(this HttpClient client, string userName, string password)
|
public static void SetBasicAuthentication(this HttpClient client, string userName, string password) =>
|
||||||
{
|
|
||||||
client.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(userName, password);
|
client.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(userName, password);
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetToken(this HttpClient client, string scheme, string token)
|
public static void SetToken(this HttpClient client, string scheme, string token) =>
|
||||||
{
|
|
||||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(scheme, token);
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(scheme, token);
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetBearerToken(this HttpClient client, string token)
|
public static void SetBearerToken(this HttpClient client, string token) =>
|
||||||
{
|
|
||||||
client.SetToken(JwtConstants.TokenType, token);
|
client.SetToken(JwtConstants.TokenType, token);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BasicAuthenticationHeaderValue : AuthenticationHeaderValue
|
public class BasicAuthenticationHeaderValue : AuthenticationHeaderValue
|
||||||
|
@ -8,10 +8,8 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
public static class SessionExtensions
|
public static class SessionExtensions
|
||||||
{
|
{
|
||||||
public static void SetObject(this ISession session, string key, object value)
|
public static void SetObject(this ISession session, string key, object value) =>
|
||||||
{
|
|
||||||
session.SetString(key, JsonConvert.SerializeObject(value));
|
session.SetString(key, JsonConvert.SerializeObject(value));
|
||||||
}
|
|
||||||
|
|
||||||
public static T GetObject<T>(this ISession session, string key)
|
public static T GetObject<T>(this ISession session, string key)
|
||||||
{
|
{
|
||||||
|
@ -33,16 +33,14 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
|||||||
|
|
||||||
_apiClient.Inst.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
_apiClient.Inst.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||||
|
|
||||||
var basketUrl = $"{_remoteServiceBaseUrl}/{user.Id.ToString()}";
|
var basketUrl = $"{_remoteServiceBaseUrl}/{user.Id}";
|
||||||
var dataString = await _apiClient.GetStringAsync(basketUrl);
|
var dataString = await _apiClient.GetStringAsync(basketUrl);
|
||||||
var response = JsonConvert.DeserializeObject<Basket>(dataString);
|
// Use the ?? Null conditional operator to simplify the initialization of response
|
||||||
if (response == null)
|
var response = JsonConvert.DeserializeObject<Basket>(dataString) ??
|
||||||
{
|
new Basket()
|
||||||
response = new Basket()
|
|
||||||
{
|
{
|
||||||
BuyerId = user.Id
|
BuyerId = user.Id
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@ -67,9 +65,12 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
|||||||
|
|
||||||
basket.Items.ForEach(x =>
|
basket.Items.ForEach(x =>
|
||||||
{
|
{
|
||||||
var quantity = quantities.Where(y => y.Key == x.Id).FirstOrDefault();
|
// Simplify this logic by using the
|
||||||
if (quantities.Where(y => y.Key == x.Id).Count() > 0)
|
// new out variable initializer.
|
||||||
x.Quantity = quantity.Value;
|
if (quantities.TryGetValue(x.Id, out var quantity))
|
||||||
|
{
|
||||||
|
x.Quantity = quantity;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return basket;
|
return basket;
|
||||||
@ -119,7 +120,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
|||||||
var token = await context.Authentication.GetTokenAsync("access_token");
|
var token = await context.Authentication.GetTokenAsync("access_token");
|
||||||
|
|
||||||
_apiClient.Inst.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
_apiClient.Inst.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||||
var basketUrl = $"{_remoteServiceBaseUrl}/{user.Id.ToString()}";
|
var basketUrl = $"{_remoteServiceBaseUrl}/{user.Id}";
|
||||||
var response = await _apiClient.DeleteAsync(basketUrl);
|
var response = await _apiClient.DeleteAsync(basketUrl);
|
||||||
|
|
||||||
//CCE: response status code...
|
//CCE: response status code...
|
||||||
|
@ -61,8 +61,11 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
|||||||
JArray brands = JArray.Parse(dataString);
|
JArray brands = JArray.Parse(dataString);
|
||||||
foreach (JObject brand in brands.Children<JObject>())
|
foreach (JObject brand in brands.Children<JObject>())
|
||||||
{
|
{
|
||||||
dynamic item = brand;
|
items.Add(new SelectListItem()
|
||||||
items.Add(new SelectListItem() { Value = item.id, Text = item.brand });
|
{
|
||||||
|
Value = brand.Value<string>("id"),
|
||||||
|
Text = brand.Value<string>("brand")
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
@ -79,10 +82,12 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
|||||||
JArray brands = JArray.Parse(dataString);
|
JArray brands = JArray.Parse(dataString);
|
||||||
foreach (JObject brand in brands.Children<JObject>())
|
foreach (JObject brand in brands.Children<JObject>())
|
||||||
{
|
{
|
||||||
dynamic item = brand;
|
items.Add(new SelectListItem()
|
||||||
items.Add(new SelectListItem() { Value = item.id, Text = item.type });
|
{
|
||||||
|
Value = brand.Value<string>("id"),
|
||||||
|
Text = brand.Value<string>("type")
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,26 +12,31 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
|||||||
{
|
{
|
||||||
public ApplicationUser Parse(IPrincipal principal)
|
public ApplicationUser Parse(IPrincipal principal)
|
||||||
{
|
{
|
||||||
var user = new ApplicationUser();
|
// Pattern matching 'is' expression
|
||||||
var claims = (ClaimsPrincipal)principal;
|
// assigns "claims" if "principal" is a "ClaimsPrincipal"
|
||||||
|
if (principal is ClaimsPrincipal claims)
|
||||||
|
{
|
||||||
|
return new ApplicationUser
|
||||||
|
{
|
||||||
|
|
||||||
user.CardHolderName = (claims.Claims.Where(x => x.Type == "card_holder").Count() > 0) ? claims.Claims.First(x => x.Type == "card_holder").Value : "";
|
CardHolderName = claims.Claims.FirstOrDefault(x => x.Type == "card_holder")?.Value ?? "",
|
||||||
user.CardNumber = (claims.Claims.Where(x => x.Type == "card_number").Count() > 0) ? claims.Claims.First(x => x.Type == "card_number").Value : "";
|
CardNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_number")?.Value ?? "",
|
||||||
user.Expiration = (claims.Claims.Where(x => x.Type == "card_expiration").Count() > 0) ? claims.Claims.First(x => x.Type == "card_expiration").Value : "";
|
Expiration = claims.Claims.FirstOrDefault(x => x.Type == "card_expiration")?.Value ?? "",
|
||||||
user.CardType = (claims.Claims.Where(x => x.Type == "missing").Count() > 0) ? int.Parse(claims.Claims.First(x => x.Type == "missing").Value) : 0;
|
CardType = int.Parse(claims.Claims.FirstOrDefault(x => x.Type == "missing")?.Value ?? "0"),
|
||||||
user.City = (claims.Claims.Where(x => x.Type == "address_city").Count() > 0) ? claims.Claims.First(x => x.Type == "address_city").Value : "";
|
City = claims.Claims.FirstOrDefault(x => x.Type == "address_city")?.Value ?? "",
|
||||||
user.Country = (claims.Claims.Where(x => x.Type == "address_country").Count() > 0) ? claims.Claims.First(x => x.Type == "address_country").Value : "";
|
Country = claims.Claims.FirstOrDefault(x => x.Type == "address_country")?.Value ?? "",
|
||||||
user.Email = (claims.Claims.Where(x => x.Type == "email").Count() > 0) ? claims.Claims.First(x => x.Type == "email").Value : "";
|
Email = claims.Claims.FirstOrDefault(x => x.Type == "email")?.Value ?? "",
|
||||||
user.Id = (claims.Claims.Where(x => x.Type == "sub").Count() > 0) ? claims.Claims.First(x => x.Type == "sub").Value : "";
|
Id = claims.Claims.FirstOrDefault(x => x.Type == "sub")?.Value ?? "",
|
||||||
user.LastName = (claims.Claims.Where(x => x.Type == "last_name").Count() > 0) ? claims.Claims.First(x => x.Type == "last_name").Value : "";
|
LastName = claims.Claims.FirstOrDefault(x => x.Type == "last_name")?.Value ?? "",
|
||||||
user.Name = (claims.Claims.Where(x => x.Type == "name").Count() > 0) ? claims.Claims.First(x => x.Type == "name").Value : "";
|
Name = claims.Claims.FirstOrDefault(x => x.Type == "name")?.Value ?? "",
|
||||||
user.PhoneNumber = (claims.Claims.Where(x => x.Type == "phone_number").Count() > 0) ? claims.Claims.First(x => x.Type == "phone_number").Value : "";
|
PhoneNumber = claims.Claims.FirstOrDefault(x => x.Type == "phone_number")?.Value ?? "",
|
||||||
user.SecurityNumber = (claims.Claims.Where(x => x.Type == "card_security_number").Count() > 0) ? claims.Claims.First(x => x.Type == "card_security_number").Value : "";
|
SecurityNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_security_number")?.Value ?? "",
|
||||||
user.State = (claims.Claims.Where(x => x.Type == "address_state").Count() > 0) ? claims.Claims.First(x => x.Type == "address_state").Value : "";
|
State = claims.Claims.FirstOrDefault(x => x.Type == "address_state")?.Value ?? "",
|
||||||
user.Street = (claims.Claims.Where(x => x.Type == "address_street").Count() > 0) ? claims.Claims.First(x => x.Type == "address_street").Value : "";
|
Street = claims.Claims.FirstOrDefault(x => x.Type == "address_street")?.Value ?? "",
|
||||||
user.ZipCode = (claims.Claims.Where(x => x.Type == "address_zip_code").Count() > 0) ? claims.Claims.First(x => x.Type == "address_zip_code").Value : "";
|
ZipCode = claims.Claims.FirstOrDefault(x => x.Type == "address_zip_code")?.Value ?? ""
|
||||||
|
};
|
||||||
return user;
|
}
|
||||||
|
throw new ArgumentException(message: "The principal must be a ClaimsPrincipal", paramName: nameof(principal));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,29 +17,29 @@ namespace WebMVC.Services.Utilities
|
|||||||
_logger = new LoggerFactory().CreateLogger(nameof(HttpApiClientWrapper));
|
_logger = new LoggerFactory().CreateLogger(nameof(HttpApiClientWrapper));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetStringAsync(string uri)
|
// Notice that these (and other methods below) are Task
|
||||||
{
|
// returning asynchronous methods. But, they do not
|
||||||
return await HttpInvoker(async () =>
|
// have the 'async' modifier, and do not contain
|
||||||
await _client.GetStringAsync(uri));
|
// any 'await statements. In each of these methods,
|
||||||
}
|
// the only asynchronous call is the last (or only)
|
||||||
|
// statement of the method. In those instances,
|
||||||
|
// a Task returning method that does not use the
|
||||||
|
// async modifier is preferred. The compiler generates
|
||||||
|
// synchronous code for this method, but returns the
|
||||||
|
// task from the underlying asynchronous method. The
|
||||||
|
// generated code does not contain the state machine
|
||||||
|
// generated for asynchronous methods.
|
||||||
|
public Task<string> GetStringAsync(string uri) =>
|
||||||
|
_client.GetStringAsync(uri);
|
||||||
|
|
||||||
public async Task<HttpResponseMessage> PostAsync<T>(string uri, T item)
|
public Task<HttpResponseMessage> PostAsync<T>(string uri, T item)
|
||||||
{
|
{
|
||||||
var contentString = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json");
|
var contentString = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json");
|
||||||
return await HttpInvoker(async () =>
|
return _client.PostAsync(uri, contentString);
|
||||||
await _client.PostAsync(uri, contentString));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<HttpResponseMessage> DeleteAsync(string uri)
|
public Task<HttpResponseMessage> DeleteAsync(string uri) =>
|
||||||
{
|
_client.DeleteAsync(uri);
|
||||||
return await HttpInvoker(async () =>
|
|
||||||
await _client.DeleteAsync(uri));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<T> HttpInvoker<T>(Func<Task<T>> action)
|
|
||||||
{
|
|
||||||
return await action();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,74 +26,72 @@ namespace WebMVC.Services.Utilities
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Policy CreateCircuitBreakerPolicy()
|
private Policy CreateCircuitBreakerPolicy() =>
|
||||||
{
|
Policy.Handle<HttpRequestException>()
|
||||||
return Policy
|
.CircuitBreakerAsync(
|
||||||
.Handle<HttpRequestException>()
|
// number of exceptions before breaking circuit
|
||||||
.CircuitBreakerAsync(
|
3,
|
||||||
// number of exceptions before breaking circuit
|
// time circuit opened before retry
|
||||||
3,
|
TimeSpan.FromMinutes(1),
|
||||||
// time circuit opened before retry
|
(exception, duration) =>
|
||||||
TimeSpan.FromMinutes(1),
|
{
|
||||||
(exception, duration) => {
|
// on circuit opened
|
||||||
// on circuit opened
|
_logger.LogTrace("Circuit breaker opened");
|
||||||
_logger.LogTrace("Circuit breaker opened");
|
},
|
||||||
},
|
() =>
|
||||||
() => {
|
{
|
||||||
// on circuit closed
|
// on circuit closed
|
||||||
_logger.LogTrace("Circuit breaker reset");
|
_logger.LogTrace("Circuit breaker reset");
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
private Policy CreateRetryPolicy()
|
private Policy CreateRetryPolicy() =>
|
||||||
{
|
Policy.Handle<HttpRequestException>()
|
||||||
return Policy
|
.WaitAndRetryAsync(
|
||||||
.Handle<HttpRequestException>()
|
// number of retries
|
||||||
.WaitAndRetryAsync(
|
3,
|
||||||
// number of retries
|
// exponential backofff
|
||||||
3,
|
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
|
||||||
// exponential backofff
|
// on retry
|
||||||
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
|
(exception, timeSpan, retryCount, context) =>
|
||||||
// on retry
|
{
|
||||||
(exception, timeSpan, retryCount, context) =>
|
_logger.LogTrace($"Retry {retryCount} " +
|
||||||
{
|
$"of {context.PolicyKey} " +
|
||||||
_logger.LogTrace($"Retry {retryCount} " +
|
$"at {context.ExecutionKey}, " +
|
||||||
$"of {context.PolicyKey} " +
|
$"due to: {exception}.");
|
||||||
$"at {context.ExecutionKey}, " +
|
}
|
||||||
$"due to: {exception}.");
|
);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetStringAsync(string uri)
|
// Notice that these (and other methods below) are Task
|
||||||
{
|
// returning asynchronous methods. But, they do not
|
||||||
return await HttpInvoker(async () =>
|
// have the 'async' modifier, and do not contain
|
||||||
await _client.GetStringAsync(uri));
|
// any 'await statements. In each of these methods,
|
||||||
}
|
// the only asynchronous call is the last (or only)
|
||||||
|
// statement of the method. In those instances,
|
||||||
|
// a Task returning method that does not use the
|
||||||
|
// async modifier is preferred. The compiler generates
|
||||||
|
// synchronous code for this method, but returns the
|
||||||
|
// task from the underlying asynchronous method. The
|
||||||
|
// generated code does not contain the state machine
|
||||||
|
// generated for asynchronous methods.
|
||||||
|
public Task<string> GetStringAsync(string uri) =>
|
||||||
|
HttpInvoker(() => _client.GetStringAsync(uri));
|
||||||
|
|
||||||
public async Task<HttpResponseMessage> PostAsync<T>(string uri, T item)
|
public Task<HttpResponseMessage> PostAsync<T>(string uri, T item) =>
|
||||||
{
|
|
||||||
// a new StringContent must be created for each retry
|
// a new StringContent must be created for each retry
|
||||||
// as it is disposed after each call
|
// as it is disposed after each call
|
||||||
return await HttpInvoker(async () =>
|
HttpInvoker(() =>_client.PostAsync(uri,
|
||||||
await _client.PostAsync(uri,
|
new StringContent(JsonConvert.SerializeObject(item),
|
||||||
new StringContent(JsonConvert.SerializeObject(item),
|
System.Text.Encoding.UTF8, "application/json")));
|
||||||
System.Text.Encoding.UTF8, "application/json")));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<HttpResponseMessage> DeleteAsync(string uri)
|
public Task<HttpResponseMessage> DeleteAsync(string uri) =>
|
||||||
{
|
HttpInvoker(() => _client.DeleteAsync(uri));
|
||||||
return await HttpInvoker(async () =>
|
|
||||||
await _client.DeleteAsync(uri));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<T> HttpInvoker<T>(Func<Task<T>> action)
|
|
||||||
{
|
private Task<T> HttpInvoker<T>(Func<Task<T>> action) =>
|
||||||
// Executes the action applying all
|
// Executes the action applying all
|
||||||
// the policies defined in the wrapper
|
// the policies defined in the wrapper
|
||||||
return await _policyWrapper
|
_policyWrapper.ExecuteAsync(() => action());
|
||||||
.ExecuteAsync(async () => await action());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents
|
|||||||
{
|
{
|
||||||
private readonly IBasketService _cartSvc;
|
private readonly IBasketService _cartSvc;
|
||||||
|
|
||||||
public Cart(IBasketService cartSvc)
|
public Cart(IBasketService cartSvc) => _cartSvc = cartSvc;
|
||||||
{
|
|
||||||
_cartSvc = cartSvc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user)
|
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user)
|
||||||
{
|
{
|
||||||
|
@ -12,19 +12,29 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents
|
|||||||
{
|
{
|
||||||
private readonly IBasketService _cartSvc;
|
private readonly IBasketService _cartSvc;
|
||||||
|
|
||||||
public CartList(IBasketService cartSvc)
|
public CartList(IBasketService cartSvc) => _cartSvc = cartSvc;
|
||||||
{
|
|
||||||
_cartSvc = cartSvc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user)
|
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user)
|
||||||
{
|
{
|
||||||
var item = await GetItemsAsync(user);
|
var item = await GetItemsAsync(user);
|
||||||
return View(item);
|
return View(item);
|
||||||
}
|
}
|
||||||
private async Task<Basket> GetItemsAsync(ApplicationUser user)
|
|
||||||
{
|
// Notice that this method is a Task
|
||||||
return await _cartSvc.GetBasket(user);
|
// returning asynchronous method. But, it does not
|
||||||
}
|
// have the 'async' modifier, and does not contain
|
||||||
|
// any 'await statements.
|
||||||
|
// The only asynchronous call is the last (or only)
|
||||||
|
// statement of the method. In those instances,
|
||||||
|
// a Task returning method that does not use the
|
||||||
|
// async modifier is preferred. The compiler generates
|
||||||
|
// synchronous code for this method, but returns the
|
||||||
|
// task from the underlying asynchronous method. The
|
||||||
|
// generated code does not contain the state machine
|
||||||
|
// generated for asynchronous methods.
|
||||||
|
// Contrast that with the method above, which calls
|
||||||
|
// and awaits an asynchronous method, and then processes
|
||||||
|
// it further.
|
||||||
|
private Task<Basket> GetItemsAsync(ApplicationUser user) => _cartSvc.GetBasket(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,20 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Annotations
|
|||||||
if (value == null)
|
if (value == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var month = value.ToString().Split('/')[0];
|
var monthString = value.ToString().Split('/')[0];
|
||||||
var year = $"20{value.ToString().Split('/')[1]}";
|
var yearString = $"20{value.ToString().Split('/')[1]}";
|
||||||
DateTime d = new DateTime(int.Parse(year), int.Parse(month), 1);
|
// Use the 'out' variable initializer to simplify
|
||||||
|
// the logic of validating the expiration date
|
||||||
|
if ((int.TryParse(monthString, out var month)) &&
|
||||||
|
(int.TryParse(yearString, out var year)))
|
||||||
|
{
|
||||||
|
DateTime d = new DateTime(year, month, 1);
|
||||||
|
|
||||||
return d > DateTime.UtcNow;
|
return d > DateTime.UtcNow;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,11 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewModels
|
|||||||
{
|
{
|
||||||
public class Basket
|
public class Basket
|
||||||
{
|
{
|
||||||
public Basket()
|
// Use property initializer syntax.
|
||||||
{
|
// While this is often more useful for read only
|
||||||
Items = new List<BasketItem>();
|
// auto implemented properties, it can simplify logic
|
||||||
}
|
// for read/write properties.
|
||||||
public List<BasketItem> Items { get; set; }
|
public List<BasketItem> Items { get; set; } = new List<BasketItem>();
|
||||||
public string BuyerId { get; set; }
|
public string BuyerId { get; set; }
|
||||||
|
|
||||||
public decimal Total()
|
public decimal Total()
|
||||||
|
@ -9,6 +9,6 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.CartViewModels
|
|||||||
public class CartComponentViewModel
|
public class CartComponentViewModel
|
||||||
{
|
{
|
||||||
public int ItemsCount { get; set; }
|
public int ItemsCount { get; set; }
|
||||||
public string Disabled { get { return (ItemsCount == 0) ? "is-disabled" : ""; } }
|
public string Disabled => (ItemsCount == 0) ? "is-disabled" : "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,6 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewModels
|
|||||||
{
|
{
|
||||||
public class Order
|
public class Order
|
||||||
{
|
{
|
||||||
public Order() {
|
|
||||||
OrderItems = new List<OrderItem>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string OrderNumber {get;set;}
|
public string OrderNumber {get;set;}
|
||||||
|
|
||||||
public DateTime Date {get;set;}
|
public DateTime Date {get;set;}
|
||||||
@ -53,7 +49,10 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewModels
|
|||||||
|
|
||||||
public string Buyer { get; set; }
|
public string Buyer { get; set; }
|
||||||
|
|
||||||
public List<OrderItem> OrderItems { get; }
|
// See the property initializer syntax below. This
|
||||||
|
// initializes the compiler generated field for this
|
||||||
|
// auto-implemented property.
|
||||||
|
public List<OrderItem> OrderItems { get; } = new List<OrderItem>();
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public Guid RequestId { get; set; }
|
public Guid RequestId { get; set; }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user