Updated data chapter with EF, caching, NoSQL

Added caching support and some diagnostic code.
This commit is contained in:
Steve Smith
2017-04-23 16:24:19 -04:00
parent a69afa2264
commit 708e85233e
8 changed files with 137 additions and 10 deletions

View File

@@ -1,4 +1,6 @@
namespace Microsoft.eShopWeb.ApplicationCore.Entities using System.Collections.Generic;
namespace Microsoft.eShopWeb.ApplicationCore.Entities
{ {
public class CatalogBrand : BaseEntity<int> public class CatalogBrand : BaseEntity<int>
{ {

View File

@@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.eShopWeb.ApplicationCore.Entities; using Microsoft.eShopWeb.ApplicationCore.Entities;
namespace Microsoft.eShopWeb.Infrastructure namespace Microsoft.eShopWeb.Infrastructure
{ {

View File

@@ -0,0 +1,54 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.eShopWeb.ViewModels;
using Microsoft.Extensions.Caching.Memory;
using System;
namespace Microsoft.eShopWeb.Services
{
public class CachedCatalogService : ICatalogService
{
private readonly IMemoryCache _cache;
private readonly CatalogService _catalogService;
private static readonly string _brandsKey = "brands";
private static readonly string _typesKey = "types";
private static readonly string _itemsKeyTemplate = "items-{0}-{1}-{2}-{3}";
private static readonly TimeSpan _defaultCacheDuration = TimeSpan.FromSeconds(30);
public CachedCatalogService(IMemoryCache cache,
CatalogService catalogService)
{
_cache = cache;
_catalogService = catalogService;
}
public async Task<IEnumerable<SelectListItem>> GetBrands()
{
return await _cache.GetOrCreateAsync(_brandsKey, async entry =>
{
entry.SlidingExpiration = _defaultCacheDuration;
return await _catalogService.GetBrands();
});
}
public async Task<Catalog> GetCatalogItems(int pageIndex, int itemsPage, int? brandID, int? typeId)
{
string cacheKey = String.Format(_itemsKeyTemplate, pageIndex, itemsPage, brandID, typeId);
return await _cache.GetOrCreateAsync(cacheKey, async entry =>
{
entry.SlidingExpiration = _defaultCacheDuration;
return await _catalogService.GetCatalogItems(pageIndex, itemsPage, brandID, typeId);
});
}
public async Task<IEnumerable<SelectListItem>> GetTypes()
{
return await _cache.GetOrCreateAsync(_typesKey, async entry =>
{
entry.SlidingExpiration = _defaultCacheDuration;
return await _catalogService.GetTypes();
});
}
}
}

View File

@@ -7,6 +7,9 @@ using Microsoft.Extensions.Options;
using Microsoft.eShopWeb.Infrastructure; using Microsoft.eShopWeb.Infrastructure;
using Microsoft.eShopWeb.ViewModels; using Microsoft.eShopWeb.ViewModels;
using Microsoft.eShopWeb.ApplicationCore.Entities; using Microsoft.eShopWeb.ApplicationCore.Entities;
using System.Data.SqlClient;
using Dapper;
using Microsoft.Extensions.Logging;
namespace Microsoft.eShopWeb.Services namespace Microsoft.eShopWeb.Services
{ {
@@ -14,15 +17,20 @@ namespace Microsoft.eShopWeb.Services
{ {
private readonly CatalogContext _context; private readonly CatalogContext _context;
private readonly IOptionsSnapshot<CatalogSettings> _settings; private readonly IOptionsSnapshot<CatalogSettings> _settings;
private readonly ILogger<CatalogService> _logger;
public CatalogService(CatalogContext context, IOptionsSnapshot<CatalogSettings> settings) public CatalogService(CatalogContext context,
IOptionsSnapshot<CatalogSettings> settings,
ILoggerFactory loggerFactory)
{ {
_context = context; _context = context;
_settings = settings; _settings = settings;
_logger = loggerFactory.CreateLogger<CatalogService>();
} }
public async Task<Catalog> GetCatalogItems(int pageIndex, int itemsPage, int? brandId, int? typeId) public async Task<Catalog> GetCatalogItems(int pageIndex, int itemsPage, int? brandId, int? typeId)
{ {
_logger.LogInformation("GetCatalogItems called.");
var root = (IQueryable<CatalogItem>)_context.CatalogItems; var root = (IQueryable<CatalogItem>)_context.CatalogItems;
if (typeId.HasValue) if (typeId.HasValue)
@@ -50,7 +58,29 @@ namespace Microsoft.eShopWeb.Services
public async Task<IEnumerable<SelectListItem>> GetBrands() public async Task<IEnumerable<SelectListItem>> GetBrands()
{ {
_logger.LogInformation("GetBrands called.");
var brands = await _context.CatalogBrands.ToListAsync(); var brands = await _context.CatalogBrands.ToListAsync();
//// create
//var newBrand = new CatalogBrand() { Brand = "Acme" };
//_context.Add(newBrand);
//await _context.SaveChangesAsync();
//// read and update
//var existingBrand = _context.Find<CatalogBrand>(1);
//existingBrand.Brand = "Updated Brand";
//await _context.SaveChangesAsync();
//// delete
//var brandToDelete = _context.Find<CatalogBrand>(2);
//_context.CatalogBrands.Remove(brandToDelete);
//await _context.SaveChangesAsync();
//var brandsWithItems = await _context.CatalogBrands
// .Include(b => b.Items)
// .ToListAsync();
var items = new List<SelectListItem> var items = new List<SelectListItem>
{ {
new SelectListItem() { Value = null, Text = "All", Selected = true } new SelectListItem() { Value = null, Text = "All", Selected = true }
@@ -65,6 +95,7 @@ namespace Microsoft.eShopWeb.Services
public async Task<IEnumerable<SelectListItem>> GetTypes() public async Task<IEnumerable<SelectListItem>> GetTypes()
{ {
_logger.LogInformation("GetTypes called.");
var types = await _context.CatalogTypes.ToListAsync(); var types = await _context.CatalogTypes.ToListAsync();
var items = new List<SelectListItem> var items = new List<SelectListItem>
{ {
@@ -88,5 +119,16 @@ namespace Microsoft.eShopWeb.Services
return items; return items;
} }
//public async Task<IEnumerable<CatalogType>> GetCatalogTypes()
//{
// return await _context.CatalogTypes.ToListAsync();
//}
//private readonly SqlConnection _conn;
//public async Task<IEnumerable<CatalogType>> GetCatalogTypesWithDapper()
//{
// return await _conn.QueryAsync<CatalogType>("SELECT * FROM CatalogType");
//}
} }
} }

View File

@@ -9,11 +9,14 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Infrastructure.Identity; using Infrastructure.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using System.Text;
using Microsoft.AspNetCore.Http;
namespace Microsoft.eShopWeb namespace Microsoft.eShopWeb
{ {
public class Startup public class Startup
{ {
private IServiceCollection _services;
public Startup(IHostingEnvironment env) public Startup(IHostingEnvironment env)
{ {
var builder = new ConfigurationBuilder() var builder = new ConfigurationBuilder()
@@ -35,8 +38,8 @@ namespace Microsoft.eShopWeb
{ {
try try
{ {
c.UseInMemoryDatabase("Catalog"); //c.UseInMemoryDatabase("Catalog");
//c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection")); c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection"));
c.ConfigureWarnings(wb => c.ConfigureWarnings(wb =>
{ {
//By default, in this application, we don't want to have client evaluations //By default, in this application, we don't want to have client evaluations
@@ -51,17 +54,20 @@ namespace Microsoft.eShopWeb
// Add Identity DbContext // Add Identity DbContext
services.AddDbContext<AppIdentityDbContext>(options => services.AddDbContext<AppIdentityDbContext>(options =>
options.UseInMemoryDatabase("Identity")); //options.UseInMemoryDatabase("Identity"));
// options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection"))); options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>() services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppIdentityDbContext>() .AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders(); .AddDefaultTokenProviders();
services.AddMemoryCache();
services.AddTransient<ICatalogService, CatalogService>(); services.AddScoped<ICatalogService, CachedCatalogService>();
services.AddScoped<CatalogService>();
services.Configure<CatalogSettings>(Configuration); services.Configure<CatalogSettings>(Configuration);
services.AddMvc(); services.AddMvc();
_services = services;
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@@ -74,6 +80,25 @@ namespace Microsoft.eShopWeb
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
app.UseBrowserLink(); app.UseBrowserLink();
app.Map("/allservices", builder => builder.Run(async context =>
{
var sb = new StringBuilder();
sb.Append("<h1>All Services</h1>");
sb.Append("<table><thead>");
sb.Append("<tr><th>Type</th><th>Lifetime</th><th>Instance</th></tr>");
sb.Append("</thead><tbody>");
foreach (var svc in _services)
{
sb.Append("<tr>");
sb.Append($"<td>{svc.ServiceType.FullName}</td>");
sb.Append($"<td>{svc.Lifetime}</td>");
sb.Append($"<td>{svc.ImplementationType?.FullName}</td>");
sb.Append("</tr>");
}
sb.Append("</tbody></table>");
await context.Response.WriteAsync(sb.ToString());
}));
} }
else else
{ {

View File

@@ -11,6 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapper" Version="1.50.2" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.0" /> <PackageReference Include="Microsoft.AspNetCore" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.0" />
@@ -20,6 +21,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="1.1.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.0" /> <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="1.0.0-msbuild3-final" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="1.0.0-msbuild3-final" />

View File

@@ -7,7 +7,8 @@
"Logging": { "Logging": {
"IncludeScopes": false, "IncludeScopes": false,
"LogLevel": { "LogLevel": {
"Default": "Warning" "Default": "Warning",
"Microsoft" : "Warning"
} }
} }
} }