diff --git a/src/MusicStore/Components/GenreMenuComponent.cs b/src/MusicStore/Components/GenreMenuComponent.cs index 20f7352..883144b 100644 --- a/src/MusicStore/Components/GenreMenuComponent.cs +++ b/src/MusicStore/Components/GenreMenuComponent.cs @@ -26,16 +26,13 @@ namespace MusicStore.Components private async Task> GetGenres() { - // TODO [EF] We don't query related data as yet, so the OrderByDescending isn't doing anything - //var genres = _dbContext.Genres - //.OrderByDescending( - // g => g.Albums.Sum( - // a => a.OrderDetails.Sum( - // od => od.Quantity))) - //.Take(9) - //.ToList(); - - return await DbContext.Genres.Take(9).ToListAsync(); + return await DbContext.Genres + .Include(g => g.Albums).ThenInclude(a => a.OrderDetails) + // TODO use nested sum https://github.com/aspnet/EntityFramework/issues/3792 + //.OrderByDescending( + // g => g.Albums.Sum(a => a.OrderDetails.Sum(od => od.Quantity))) + .Take(9) + .ToListAsync(); } } } \ No newline at end of file diff --git a/src/MusicStore/Controllers/HomeController.cs b/src/MusicStore/Controllers/HomeController.cs index df110fa..042e6d0 100644 --- a/src/MusicStore/Controllers/HomeController.cs +++ b/src/MusicStore/Controllers/HomeController.cs @@ -60,9 +60,8 @@ namespace MusicStore.Controllers // Group the order details by album and return // the albums with the highest count - // TODO [EF] We don't query related data as yet, so the OrderByDescending isn't doing anything return await dbContext.Albums - .OrderByDescending(a => a.OrderDetails.Count()) + .OrderByDescending(a => a.OrderDetails.Count) .Take(count) .ToListAsync(); } diff --git a/src/MusicStore/Models/CartItem.cs b/src/MusicStore/Models/CartItem.cs index 40e3094..64550ab 100644 --- a/src/MusicStore/Models/CartItem.cs +++ b/src/MusicStore/Models/CartItem.cs @@ -5,6 +5,7 @@ namespace MusicStore.Models { public class CartItem { + [Key] public int CartItemId { get; set; } [Required] diff --git a/src/MusicStore/Models/MusicStoreContext.cs b/src/MusicStore/Models/MusicStoreContext.cs index 89a7f14..7954da9 100644 --- a/src/MusicStore/Models/MusicStoreContext.cs +++ b/src/MusicStore/Models/MusicStoreContext.cs @@ -13,20 +13,5 @@ namespace MusicStore.Models public DbSet Genres { get; set; } public DbSet CartItems { get; set; } public DbSet OrderDetails { get; set; } - - protected override void OnModelCreating(ModelBuilder builder) - { - builder.Entity().HasKey(b => b.CartItemId); - - // TODO: Remove when explicit values insertion removed. - builder.Entity().Property(a => a.ArtistId).ValueGeneratedNever(); - builder.Entity().Property(g => g.GenreId).ValueGeneratedNever(); - - //Deleting an album fails with this relation - builder.Entity().Ignore(a => a.OrderDetails); - builder.Entity().Ignore(od => od.Album); - - base.OnModelCreating(builder); - } } } \ No newline at end of file diff --git a/src/MusicStore/Models/SampleData.cs b/src/MusicStore/Models/SampleData.cs index 835bdd1..9a048d0 100644 --- a/src/MusicStore/Models/SampleData.cs +++ b/src/MusicStore/Models/SampleData.cs @@ -913,12 +913,9 @@ namespace MusicStore.Models new Artist { Name = "אריק אינשטיין"} }; - // TODO [EF] Swap to store generated keys when available - int artistId = 1; artists = new Dictionary(); foreach (Artist artist in artistsList) { - artist.ArtistId = artistId++; artists.Add(artist.Name, artist); } } @@ -954,15 +951,9 @@ namespace MusicStore.Models }; genres = new Dictionary(); - // TODO [EF] Swap to store generated keys when available - int genreId = 1; + foreach (Genre genre in genresList) { - genre.GenreId = genreId++; - - // TODO [EF] Remove when null values are supported by update pipeline - genre.Description = genre.Name + " is great music (if you like it)."; - genres.Add(genre.Name, genre); } } diff --git a/src/MusicStore/Models/ShoppingCart.cs b/src/MusicStore/Models/ShoppingCart.cs index c963609..c06727a 100644 --- a/src/MusicStore/Models/ShoppingCart.cs +++ b/src/MusicStore/Models/ShoppingCart.cs @@ -10,25 +10,25 @@ namespace MusicStore.Models public class ShoppingCart { private readonly MusicStoreContext _dbContext; - private string ShoppingCartId { get; set; } + private readonly string _shoppingCartId; - public ShoppingCart(MusicStoreContext dbContext) + private ShoppingCart(MusicStoreContext dbContext, string id) { _dbContext = dbContext; + _shoppingCartId = id; } - public static ShoppingCart GetCart(MusicStoreContext db, HttpContext context) - { - var cart = new ShoppingCart(db); - cart.ShoppingCartId = cart.GetCartId(context); - return cart; - } + public static ShoppingCart GetCart(MusicStoreContext db, HttpContext context) + => GetCart(db, GetCartId(context)); + + public static ShoppingCart GetCart(MusicStoreContext db, string cartId) + => new ShoppingCart(db, cartId); public void AddToCart(Album album) { // Get the matching cart and album instances var cartItem = _dbContext.CartItems.SingleOrDefault( - c => c.CartId == ShoppingCartId + c => c.CartId == _shoppingCartId && c.AlbumId == album.AlbumId); if (cartItem == null) @@ -37,7 +37,7 @@ namespace MusicStore.Models cartItem = new CartItem { AlbumId = album.AlbumId, - CartId = ShoppingCartId, + CartId = _shoppingCartId, Count = 1, DateCreated = DateTime.Now }; @@ -55,7 +55,7 @@ namespace MusicStore.Models { // Get the cart var cartItem = _dbContext.CartItems.Single( - cart => cart.CartId == ShoppingCartId + cart => cart.CartId == _shoppingCartId && cart.CartItemId == id); int itemCount = 0; @@ -76,39 +76,45 @@ namespace MusicStore.Models return itemCount; } - public void EmptyCart() + public async void EmptyCart() { - var cartItems = _dbContext.CartItems.Where(cart => cart.CartId == ShoppingCartId).ToArray(); + var cartItems = await _dbContext + .CartItems + .Where(cart => cart.CartId == _shoppingCartId) + .ToArrayAsync(); + _dbContext.CartItems.RemoveRange(cartItems); } - public async Task> GetCartItems() + public Task> GetCartItems() { - return await _dbContext.CartItems. - Where(cart => cart.CartId == ShoppingCartId). + return _dbContext.CartItems. + Where(cart => cart.CartId == _shoppingCartId). Include(c => c.Album). ToListAsync(); } - public async Task GetCount() + public Task GetCount() { // Get the count of each item in the cart and sum them up - return await (from cartItem in _dbContext.CartItems - where cartItem.CartId == ShoppingCartId - select cartItem.Count).SumAsync(); + return _dbContext + .CartItems + .Where(c => c.CartId == _shoppingCartId) + .Select(c => c.Count) + .SumAsync(); } - public async Task GetTotal() + public Task GetTotal() { // Multiply album price by count of that album to get // the current price for each of those albums in the cart // sum all album price totals to get the cart total - // TODO: Use nav prop traversal instead of joins (EF #https://github.com/aspnet/EntityFramework/issues/325) - return await (from cartItem in _dbContext.CartItems - join album in _dbContext.Albums on cartItem.AlbumId equals album.AlbumId - where cartItem.CartId == ShoppingCartId - select cartItem.Count * album.Price).SumAsync(); + return _dbContext.CartItems + .Include(c => c.Album) + .Where(c => c.CartId == _shoppingCartId) + .Select(c => c.Album.Price * c.Count) + .SumAsync(); } public async Task CreateOrder(Order order) @@ -148,7 +154,7 @@ namespace MusicStore.Models } // We're using HttpContextBase to allow access to sessions. - private string GetCartId(HttpContext context) + private static string GetCartId(HttpContext context) { var cartId = context.Session.GetString("Session"); diff --git a/test/MusicStore.Test/Models/ShoppingCartTest.cs b/test/MusicStore.Test/Models/ShoppingCartTest.cs new file mode 100644 index 0000000..0d51cef --- /dev/null +++ b/test/MusicStore.Test/Models/ShoppingCartTest.cs @@ -0,0 +1,55 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using MusicStore.Models; +using Xunit; + +namespace MusicStore.Test +{ + public class ShoppingCartTest : IClassFixture + { + private readonly ShoppingCartFixture _fixture; + + public ShoppingCartTest(ShoppingCartFixture fixture) + { + _fixture = fixture; + } + + [Fact] + public async void ComputesTotal() + { + var cartId = Guid.NewGuid().ToString(); + using (var db = _fixture.CreateContext()) + { + var a = db.Albums.Add( + new Album + { + Price = 15.99m + }).Entity; + + db.CartItems.Add(new CartItem { Album = a, Count = 2, CartId = cartId }); + + db.SaveChanges(); + + Assert.Equal(31.98m, await ShoppingCart.GetCart(db, cartId).GetTotal()); + } + } + } + + public class ShoppingCartFixture + { + private readonly IServiceProvider _serviceProvider; + + public ShoppingCartFixture() + { + var services = new ServiceCollection(); + services.AddEntityFramework() + .AddInMemoryDatabase() + .AddDbContext(options => options.UseInMemoryDatabase()); + _serviceProvider = services.BuildServiceProvider(); + } + + public virtual MusicStoreContext CreateContext() + => _serviceProvider.GetRequiredService(); + } +}