Swapping data access to use EF
Enabling data access against new EF stack. Uses SQL Server when running on Net45 and an in-memory database for K10 (because SQL Client isn't available on K10 yet). Various workarounds in place to compensate for missing features in EF (all marked with "// TODO [EF]..."
This commit is contained in:
parent
5497541e08
commit
965046813e
|
|
@ -11,9 +11,7 @@ namespace MusicStore.Controllers
|
|||
//[Authorize]
|
||||
public class CheckoutController : Controller
|
||||
{
|
||||
//Bug: Missing EF
|
||||
//MusicStoreEntities storeDB = new MusicStoreEntities();
|
||||
MusicStoreEntities storeDB = MusicStoreEntities.Instance;
|
||||
MusicStoreContext db = new MusicStoreContext();
|
||||
const string PromoCode = "FREE";
|
||||
|
||||
//
|
||||
|
|
@ -48,19 +46,35 @@ namespace MusicStore.Controllers
|
|||
}
|
||||
else
|
||||
{
|
||||
// TODO [EF] Swap to store generated identity key when supported
|
||||
var nextId = db.Orders.Any()
|
||||
? db.Orders.Max(o => o.OrderId) + 1
|
||||
: 1;
|
||||
|
||||
//Bug: Object values should come from page (putting in hard coded values as EF can't work with nulls against SQL Server yet)
|
||||
//Bug: Identity not available
|
||||
order.Username = null; //User.Identity.Name;
|
||||
order.Username = "unknown"; //User.Identity.Name;
|
||||
order.OrderId = nextId;
|
||||
order.OrderDate = DateTime.Now;
|
||||
order.FirstName = "John";
|
||||
order.LastName = "Doe";
|
||||
order.Address = "One Microsoft Way";
|
||||
order.City = "Redmond";
|
||||
order.State = "WA";
|
||||
order.Country = "USA";
|
||||
order.Email = "john.doe@example.com";
|
||||
order.Phone = "555-555-5555";
|
||||
order.PostalCode = "98052";
|
||||
|
||||
//Add the Order
|
||||
storeDB.Orders.Add(order);
|
||||
db.Orders.Add(order);
|
||||
|
||||
//Process the order
|
||||
var cart = ShoppingCart.GetCart(storeDB, this.Context);
|
||||
var cart = ShoppingCart.GetCart(db, this.Context);
|
||||
cart.CreateOrder(order);
|
||||
|
||||
// Save all changes
|
||||
storeDB.SaveChanges();
|
||||
db.SaveChanges();
|
||||
|
||||
//Bug: Helper not available
|
||||
//return RedirectToAction("Complete",
|
||||
|
|
@ -87,7 +101,7 @@ namespace MusicStore.Controllers
|
|||
// o => o.OrderId == id &&
|
||||
// o.Username == User.Identity.Name);
|
||||
|
||||
bool isValid = storeDB.Orders.Any(
|
||||
bool isValid = db.Orders.Any(
|
||||
o => o.OrderId == id);
|
||||
|
||||
if (isValid)
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ namespace MvcMusicStore.Controllers
|
|||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
/// Bug: Hacking to return a singleton. Should go away once we have EF.
|
||||
//private MusicStoreEntities storeDB = new MusicStoreEntities();
|
||||
private MusicStoreEntities storeDB = MusicStoreEntities.Instance;
|
||||
private MusicStoreContext db = new MusicStoreContext();
|
||||
|
||||
//
|
||||
// GET: /Home/
|
||||
public IActionResult Index()
|
||||
|
|
@ -27,7 +26,8 @@ namespace MvcMusicStore.Controllers
|
|||
// Group the order details by album and return
|
||||
// the albums with the highest count
|
||||
|
||||
return storeDB.Albums
|
||||
// TODO [EF] We don't query related data as yet, so the OrderByDescending isn't doing anything
|
||||
return db.Albums
|
||||
.OrderByDescending(a => a.OrderDetails.Count())
|
||||
.Take(count)
|
||||
.ToList();
|
||||
|
|
|
|||
|
|
@ -9,16 +9,14 @@ namespace MusicStore.Controllers
|
|||
{
|
||||
public class ShoppingCartController : Controller
|
||||
{
|
||||
//Bug: No EF yet
|
||||
//private MusicStoreEntities storeDB = new MusicStoreEntities();
|
||||
private MusicStoreEntities storeDB = MusicStoreEntities.Instance;
|
||||
private MusicStoreContext db = new MusicStoreContext();
|
||||
|
||||
//
|
||||
// GET: /ShoppingCart/
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
var cart = ShoppingCart.GetCart(storeDB, this.Context);
|
||||
var cart = ShoppingCart.GetCart(db, this.Context);
|
||||
|
||||
// Set up our ViewModel
|
||||
var viewModel = new ShoppingCartViewModel
|
||||
|
|
@ -38,15 +36,15 @@ namespace MusicStore.Controllers
|
|||
{
|
||||
|
||||
// Retrieve the album from the database
|
||||
var addedAlbum = storeDB.Albums
|
||||
var addedAlbum = db.Albums
|
||||
.Single(album => album.AlbumId == id);
|
||||
|
||||
// Add it to the shopping cart
|
||||
var cart = ShoppingCart.GetCart(storeDB, this.Context);
|
||||
var cart = ShoppingCart.GetCart(db, this.Context);
|
||||
|
||||
cart.AddToCart(addedAlbum);
|
||||
|
||||
storeDB.SaveChanges();
|
||||
db.SaveChanges();
|
||||
|
||||
// Go back to the main store page for more shopping
|
||||
//Bug: Helper method not available
|
||||
|
|
@ -62,16 +60,17 @@ namespace MusicStore.Controllers
|
|||
public IActionResult RemoveFromCart(int id)
|
||||
{
|
||||
// Retrieve the current user's shopping cart
|
||||
var cart = ShoppingCart.GetCart(storeDB, this.Context);
|
||||
var cart = ShoppingCart.GetCart(db, this.Context);
|
||||
|
||||
// Get the name of the album to display confirmation
|
||||
string albumName = storeDB.Carts
|
||||
.Single(item => item.RecordId == id).Album.Title;
|
||||
// TODO [EF] Turn into one query once query of related data is enabled
|
||||
int albumId = db.Carts.Single(item => item.RecordId == id).AlbumId;
|
||||
string albumName = db.Albums.Single(a => a.AlbumId == albumId).Title;
|
||||
|
||||
// Remove from cart
|
||||
int itemCount = cart.RemoveFromCart(id);
|
||||
|
||||
storeDB.SaveChanges();
|
||||
db.SaveChanges();
|
||||
|
||||
string removed = (itemCount > 0) ? " 1 copy of " : string.Empty;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,15 +8,14 @@ namespace MusicStore.Controllers
|
|||
{
|
||||
public class StoreController : Controller
|
||||
{
|
||||
//Bug: Need to remove singleton instance after EF is implemented.
|
||||
//MusicStoreEntities storeDB = new MusicStoreEntities();
|
||||
MusicStoreEntities storeDB = MusicStoreEntities.Instance;
|
||||
MusicStoreContext db = new MusicStoreContext();
|
||||
|
||||
//
|
||||
// GET: /Store/
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
var genres = storeDB.Genres.ToList();
|
||||
var genres = db.Genres.ToList();
|
||||
|
||||
return View(genres);
|
||||
}
|
||||
|
|
@ -27,21 +26,17 @@ namespace MusicStore.Controllers
|
|||
public IActionResult Browse(string genre)
|
||||
{
|
||||
// Retrieve Genre genre and its Associated associated Albums albums from database
|
||||
//Bug: Include is part of EF. We need to work around this temporarily
|
||||
//var genreModel = storeDB.Genres.Include("Albums")
|
||||
// .Single(g => g.Name == genre);
|
||||
|
||||
var genreModel = storeDB.Genres.Single(g => g.Name == genre);
|
||||
genreModel.Albums = storeDB.Albums.Where(a => a.GenreId == genreModel.GenreId).ToList();
|
||||
// TODO [EF] Swap to native support for loading related data when available
|
||||
var genreModel = db.Genres.Single(g => g.Name == genre);
|
||||
genreModel.Albums = db.Albums.Where(a => a.GenreId == genreModel.GenreId).ToList();
|
||||
|
||||
return View(genreModel);
|
||||
}
|
||||
|
||||
public IActionResult Details(int id)
|
||||
{
|
||||
//Bug: Need Find method from EF.
|
||||
//var album = storeDB.Albums.Find(id);
|
||||
var album = storeDB.Albums.Single(a => a.AlbumId == id);
|
||||
var album = db.Albums.Single(a => a.AlbumId == id);
|
||||
|
||||
return View(album);
|
||||
}
|
||||
|
|
@ -50,7 +45,8 @@ namespace MusicStore.Controllers
|
|||
//[ChildActionOnly]
|
||||
public IActionResult GenreMenu()
|
||||
{
|
||||
var genres = storeDB.Genres
|
||||
// TODO [EF] We don't query related data as yet, so the OrderByDescending isn't doing anything
|
||||
var genres = db.Genres
|
||||
.OrderByDescending(
|
||||
g => g.Albums.Sum(
|
||||
a => a.OrderDetails.Sum(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.Data.Entity;
|
||||
using MusicStore.Models;
|
||||
using System.Linq;
|
||||
|
||||
|
|
@ -11,9 +12,7 @@ namespace MusicStore.Controllers
|
|||
//[Authorize(Roles="Administrator")]
|
||||
public class StoreManagerController : Controller
|
||||
{
|
||||
//Bug: No EF yet
|
||||
//private MusicStoreEntities db = new MusicStoreEntities();
|
||||
private MusicStoreEntities db = MusicStoreEntities.Instance;
|
||||
private MusicStoreContext db = new MusicStoreContext();
|
||||
|
||||
//
|
||||
// GET: /StoreManager/
|
||||
|
|
@ -111,8 +110,7 @@ namespace MusicStore.Controllers
|
|||
//Bug: ModelState.IsValid missing
|
||||
//if (ModelState.IsValid)
|
||||
{
|
||||
//Bug: Missing EF
|
||||
//db.Entry(album).State = EntityState.Modified;
|
||||
db.ChangeTracker.Entry(album).State = EntityState.Modified;
|
||||
db.SaveChanges();
|
||||
//Bug: Missing RedirectToAction helper
|
||||
//return RedirectToAction("Index");
|
||||
|
|
@ -127,8 +125,6 @@ namespace MusicStore.Controllers
|
|||
|
||||
public IActionResult Delete(int id = 0)
|
||||
{
|
||||
//Bug: EF missing
|
||||
//Album album = db.Albums.Find(id);
|
||||
Album album = db.Albums.Single(a => a.AlbumId == id);
|
||||
if (album == null)
|
||||
{
|
||||
|
|
@ -145,10 +141,9 @@ namespace MusicStore.Controllers
|
|||
//TODO: How to have an action with same name 'Delete'??
|
||||
public IActionResult DeleteConfirmed(int id)
|
||||
{
|
||||
//Bug: EF missing
|
||||
//Album album = db.Albums.Find(id);
|
||||
Album album = db.Albums.Single(a => a.AlbumId == id);
|
||||
db.Albums.Remove(album);
|
||||
// TODO [EF] Replace with EntitySet.Remove when querying attaches instances
|
||||
db.ChangeTracker.Entry(album).State = EntityState.Deleted;
|
||||
db.SaveChanges();
|
||||
//Bug: Missing helper
|
||||
//return RedirectToAction("Index");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Data.Entity;
|
||||
using Microsoft.Data.Entity.Metadata;
|
||||
using Microsoft.Data.InMemory;
|
||||
using Microsoft.Data.SqlServer;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class MusicStoreContext : EntityContext
|
||||
{
|
||||
private static EntityConfiguration _config;
|
||||
|
||||
public EntitySet<Album> Albums { get; set; }
|
||||
public EntitySet<Artist> Artists { get; set; }
|
||||
public EntitySet<Order> Orders { get; set; }
|
||||
public EntitySet<Genre> Genres { get; set; }
|
||||
public EntitySet<Cart> Carts { get; set; }
|
||||
public EntitySet<OrderDetail> OrderDetails { get; set; }
|
||||
|
||||
public MusicStoreContext()
|
||||
: base(GetConfiguration())
|
||||
{ }
|
||||
|
||||
// TODO Not using OnModelCreating and OnConfiguring because model is not cached and that breaks InMemoryDataStore
|
||||
// because IEntityType is a different instance for the same type between context instances
|
||||
private static EntityConfiguration GetConfiguration()
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
var model = new Model();
|
||||
var modelBuilder = new ModelBuilder(model);
|
||||
modelBuilder.Entity<Album>().Key(a => a.AlbumId);
|
||||
modelBuilder.Entity<Artist>().Key(a => a.ArtistId);
|
||||
modelBuilder.Entity<Order>().Key(o => o.OrderId).StorageName("[Order]");
|
||||
modelBuilder.Entity<Genre>().Key(g => g.GenreId);
|
||||
modelBuilder.Entity<Cart>().Key(c => c.RecordId);
|
||||
modelBuilder.Entity<OrderDetail>().Key(o => o.OrderDetailId);
|
||||
new SimpleTemporaryConvention().Apply(model);
|
||||
|
||||
var builder = new EntityConfigurationBuilder();
|
||||
|
||||
// TODO [EF] Remove once SQL Client is available on K10
|
||||
#if NET45
|
||||
builder.UseSqlServer(@"Server=(localdb)\v11.0;Database=MusicStore;Trusted_Connection=True;");
|
||||
#else
|
||||
builder.UseDataStore(new InMemoryDataStore());
|
||||
#endif
|
||||
builder.UseModel(model);
|
||||
|
||||
_config = builder.BuildConfiguration();
|
||||
}
|
||||
|
||||
return _config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Bug: Mocked entities set. We should substitute this with DbSet once EF is available.
|
||||
/// </summary>
|
||||
public class MusicStoreEntities
|
||||
{
|
||||
public List<Album> Albums { get; set; }
|
||||
public List<Genre> Genres { get; set; }
|
||||
public List<Artist> Artists { get; set; }
|
||||
public List<Cart> Carts { get; set; }
|
||||
public List<Order> Orders { get; set; }
|
||||
public List<OrderDetail> OrderDetails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Bug: Need to remove this method. Just adding this to unblock from compilation errors
|
||||
/// </summary>
|
||||
public void SaveChanges()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private static MusicStoreEntities instance;
|
||||
|
||||
public static MusicStoreEntities Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
//TODO: Sync issues not handled.
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new MusicStoreEntities();
|
||||
SampleData.Seed(instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bug: This is to just initialize the lists. Once we have EF this should be removed.
|
||||
/// </summary>
|
||||
/// <param name="dummy"></param>
|
||||
private MusicStoreEntities()
|
||||
{
|
||||
this.Albums = new List<Album>();
|
||||
this.Genres = new List<Genre>();
|
||||
this.Artists = new List<Artist>();
|
||||
this.Carts = new List<Cart>();
|
||||
this.Orders = new List<Order>();
|
||||
this.OrderDetails = new List<OrderDetail>();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
using Microsoft.AspNet.Abstractions;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.Data.Entity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
@ -10,17 +11,17 @@ namespace MusicStore.Models
|
|||
{
|
||||
public partial class ShoppingCart
|
||||
{
|
||||
MusicStoreEntities _db;
|
||||
MusicStoreContext _db;
|
||||
string ShoppingCartId { get; set; }
|
||||
|
||||
public ShoppingCart(MusicStoreEntities db)
|
||||
public ShoppingCart(MusicStoreContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public const string CartSessionKey = "CartId";
|
||||
|
||||
public static ShoppingCart GetCart(MusicStoreEntities db, HttpContext context)
|
||||
public static ShoppingCart GetCart(MusicStoreContext db, HttpContext context)
|
||||
{
|
||||
var cart = new ShoppingCart(db);
|
||||
cart.ShoppingCartId = cart.GetCartId(context);
|
||||
|
|
@ -43,9 +44,15 @@ namespace MusicStore.Models
|
|||
|
||||
if (cartItem == null)
|
||||
{
|
||||
// TODO [EF] Swap to store generated key once we support identity pattern
|
||||
var nextRecordId = _db.Carts.Any()
|
||||
? _db.Carts.Max(c => c.RecordId) + 1
|
||||
: 1;
|
||||
|
||||
// Create a new cart item if no cart item exists
|
||||
cartItem = new Cart
|
||||
{
|
||||
RecordId = nextRecordId,
|
||||
AlbumId = album.AlbumId,
|
||||
CartId = ShoppingCartId,
|
||||
Count = 1,
|
||||
|
|
@ -58,6 +65,9 @@ namespace MusicStore.Models
|
|||
{
|
||||
// If the item does exist in the cart, then add one to the quantity
|
||||
cartItem.Count++;
|
||||
|
||||
// TODO [EF] Remove this line once change detection is available
|
||||
_db.ChangeTracker.Entry(cartItem).State = Microsoft.Data.Entity.EntityState.Modified;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,6 +85,10 @@ namespace MusicStore.Models
|
|||
if (cartItem.Count > 1)
|
||||
{
|
||||
cartItem.Count--;
|
||||
|
||||
// TODO [EF] Remove this line once change detection is available
|
||||
_db.ChangeTracker.Entry(cartItem).State = EntityState.Modified;
|
||||
|
||||
itemCount = cartItem.Count;
|
||||
}
|
||||
else
|
||||
|
|
@ -93,7 +107,8 @@ namespace MusicStore.Models
|
|||
|
||||
foreach (var cartItem in cartItems)
|
||||
{
|
||||
_db.Carts.Remove(cartItem);
|
||||
// TODO [EF] Change to EntitySet.Remove once querying attaches instances
|
||||
_db.ChangeTracker.Entry(cartItem).State = EntityState.Deleted;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -119,10 +134,16 @@ namespace MusicStore.Models
|
|||
// 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
|
||||
decimal? total = (from cartItems in _db.Carts
|
||||
where cartItems.CartId == ShoppingCartId
|
||||
select (int?)cartItems.Count * cartItems.Album.Price).Sum();
|
||||
return total ?? decimal.Zero;
|
||||
|
||||
// TODO Collapse to a single query once EF supports querying related data
|
||||
decimal total = 0;
|
||||
foreach (var item in _db.Carts.Where(c => c.CartId == ShoppingCartId))
|
||||
{
|
||||
var album = _db.Albums.Single(a => a.AlbumId == item.AlbumId);
|
||||
total += item.Count * album.Price;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public int CreateOrder(Order order)
|
||||
|
|
@ -131,6 +152,11 @@ namespace MusicStore.Models
|
|||
|
||||
var cartItems = GetCartItems();
|
||||
|
||||
// TODO [EF] Swap to store generated identity key when supported
|
||||
var nextId = _db.OrderDetails.Any()
|
||||
? _db.OrderDetails.Max(o => o.OrderDetailId) + 1
|
||||
: 1;
|
||||
|
||||
// Iterate over the items in the cart, adding the order details for each
|
||||
foreach (var item in cartItems)
|
||||
{
|
||||
|
|
@ -140,6 +166,7 @@ namespace MusicStore.Models
|
|||
|
||||
var orderDetail = new OrderDetail
|
||||
{
|
||||
OrderDetailId = nextId,
|
||||
AlbumId = item.AlbumId,
|
||||
OrderId = order.OrderId,
|
||||
UnitPrice = album.Price,
|
||||
|
|
@ -147,9 +174,11 @@ namespace MusicStore.Models
|
|||
};
|
||||
|
||||
// Set the order total of the shopping cart
|
||||
orderTotal += (item.Count * item.Album.Price);
|
||||
orderTotal += (item.Count * album.Price);
|
||||
|
||||
_db.OrderDetails.Add(orderDetail);
|
||||
|
||||
nextId++;
|
||||
}
|
||||
|
||||
// Set the order's total to the orderTotal count
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Microsoft.AspNet.Identity.InMemory;
|
|||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using MusicStore.Models;
|
||||
using MusicStore.Web.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
|
@ -38,6 +39,8 @@ public class Startup
|
|||
new { controller = "Home" });
|
||||
|
||||
app.UseRouter(routes);
|
||||
|
||||
SampleData.InitializeMusicStoreDatabase();
|
||||
}
|
||||
|
||||
//Bug: We need EF to integrate with SQL server. Until then we will use in memory store
|
||||
|
|
|
|||
|
|
@ -18,13 +18,18 @@
|
|||
"Microsoft.AspNet.Security.DataProtection": "0.1-alpha-*",
|
||||
"Microsoft.AspNet.Identity": "0.1-alpha-*",
|
||||
"Microsoft.AspNet.Identity.Entity": "0.1-alpha-*",
|
||||
"Microsoft.AspNet.Identity.InMemory": "0.1-alpha-*"
|
||||
"Microsoft.AspNet.Identity.InMemory": "0.1-alpha-*",
|
||||
"Microsoft.Data.Entity": "0.1-alpha-*",
|
||||
"Microsoft.Data.Relational": "0.1-alpha-*",
|
||||
"Microsoft.Data.SqlServer": "0.1-pre-*",
|
||||
"Microsoft.Data.InMemory": "0.1-alpha-*"
|
||||
},
|
||||
"configurations": {
|
||||
"net45": {
|
||||
"dependencies": {
|
||||
"System.Runtime": "",
|
||||
"System.ComponentModel.DataAnnotations": ""
|
||||
"System.ComponentModel.DataAnnotations": "",
|
||||
"System.Data": ""
|
||||
}
|
||||
},
|
||||
"k10": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue