chsakell分享了前端使用AngularJS,后端使用ASP.NET Web API的购物车案例,非常精彩,这里这里记录下对此项目的理解。
文章:
http://chsakell.com/2015/01/31/angularjs-feat-web-api/
http://chsakell.com/2015/03/07/angularjs-feat-web-api-enable-session-state/
源码:
https://github.com/chsakell/webapiangularjssecurity
本系列共三篇,本篇是第一篇。
购物车Demo,前端使用AngularJS,后端使用ASP.NET Web API(1)--后端
购物车Demo,前端使用AngularJS,后端使用ASP.NET Web API(2)--前端,以及前后端Session
购物车Demo,前端使用AngularJS,后端使用ASP.NET Web API(3)--Idetity,OWIN前后端验证
■ 配置EF
首先搞清模型之间的关系:
public class Gadget { public int GadgetID{get;set;} ... public int CategoryID{get;set;} public Category Category{get;set;} } public class Category { public int CategoryID{get;set;} ... public List<Gadget> Gadgets{get;set;} } public class Order { public int OrderID{get;set;} ... public List<Gadget> Gadgets{get;set;} } public class GadgetOrder { public int GadgetOrderID{get;set;} public int OrderID{get;set;} public Order Order{get;set;} public int GadgetID{get;set;} public Gadget Gadget{get;set;} }
以上,Category和Gadget是1对多的关系,GadgetOrder是Order和Gadget的中间表。
接着需要通过EF Fluent API来配置领域,需要实现EntityTypeConfiguration<TModel>这个基类。比如:
public class OrderConfiguration : EntityTypeConfiguration<Order> { public OrderConfiguration() { Ignore(o => o.Gadgets); } }
然后就配置上下文,继承DbContext这个基类。
public class StoreContext : DbContext { protected override void OnModelCreating(DbModelBuilder modelBuilder) { ... modelBuilder.Configurations.Add(new OrderConfiguration()); } public DbSet<Order> Orders{get;set;} ... }
我们还希望在生成数据库的时候生成一些种子数据,需要继承DropCreateDatabaseIfModelChanges<TContext>这个泛型类。
public class StoreInitializer : DropCreateDatabaseIfModelChanges<StoreContext> { protected override void Seed(StoreContext context) { try { GetCategoreis().ForEach(c => context.Categories.Add(c)); ... } catch(Exception ex) { Debug.WriteLine(ex.Message); } } private static List<Category> GetCategories() { ... } }
如何调用数据库种子数据的类StoreInitializer呢?有一种方法使在项目全局文件中配置。(还有一种方法使在DbContext的构造函数中配置,还有一种在数据库迁移文件中配置,etc.)
void Application_Start(object sender, EventArgs e) { Database.SetInitializer(new StoreIntializer()); }
最后,关于EF的配置部分,需要在Web.config中配置,大致如下:
<connectionStrings> <add name="ProductServiceContext" connectionString="Data Source=.;User=someusername;Password=somepassword;Initial Catalog=MyProductService;Integrated Security=True" providerName="System.Data.SqlClient"/> </connectionStrings>
■ CategoriesController
先俯瞰。
public class CategoriesController : ApiController { private StoreContext db = new SotoreContext(); ... private bool CategoryExists(int id) { return db.Categories.Count(g => g.CategoryID=id) > 0; } protected override void Dispose(boo disposing) { if(disposing) { db.Dispose(); } base.Dispose(disposing); } }
再细节。
/GET api/Categories public IQueryable<Category> GetCategories() { return db.Categories; } //GET api/Categories/5 [ResponseType(typeof(Category))] public async Task<IHttpActionResult> GetCategory(int id) { Category category = await db.Categories.FindAsync(id); if(category == null) { return NotFound(); } return Ok(category); } //put api/Categories/5 [ResponseType(typeof(void))] public async Task<IHttpActionResult> PutCategory(int id, Category category) { if(!ModelState.IsValid) { return BadRequest(ModelState); } if(id != category.CategoryID) { return BadRequest(); } db.Entry(category).State = EntityState.Modified; try { await db.SavheChangesAsync(); } catch(DbUpdateConcurrencyException) { if(!CategoryExists(id)) { return NotFound(); } else { throw; } } return StatusCode(HttpStatusCOde.NoContet); } //post api/Categories [ResponseType(typeof(Category))] public async Task<IHttpActionResult> PostCategory(Category category) { if(!ModelState.IsValid) { return BadRequest(ModelState); } db.Categories.Add(category); await db.SaveChangesAsync(); //返回到指定的路由 return CreatedAtRoute("DefaultApi", new {id = category.CategoryID}, category); } //delete api/Categoreis/5 [ResponseType(typeof(Category))] public async Task<IHttpActionResult> DeleteCategory(int id) { Category category = await db.Categories.FindAsync(id); if(category == null) { return NotFound(); } db.Categories.Remove(category); await db.SaveChangesAsync(); return Ok(category); }
■ GadgetsController
先俯瞰。
public class GadgetsController : ApiController { private StoreContext db = new StoreContext(); protected override void Dispose(bool disposing) { if(disposing) { db.Dispose(); } base.Dispose(disposing); } private bool GadgetExists(int id) { return db.Gadgets.Count(g => g.GadgetID == ID) > 0; } }
再细节。
//get api/Gadgets public IQueryable<Gadget> GetGadgets() { return db.Gadgets; } //get api/Gadgets/5 [ResponseType(typeof(Gadgets))] public async Task<IHttpActionResult> GetGadget(int id) { Gadget gadget = await db.Gadgets.FindAsync(id); if(gadget == null) { return NotFound(); } return Ok(gadget); } //put api/Gadgets/5 [ResponseType(typeof(void))] public async Task<IHttpActionResult> PutGadget(int id, Gadget gadget) { if(!ModelState.IsValid) { return BadRequest(ModelState); } if(d != gadget.GadgetID) { return BadRequest(); } db.Entry(gadget).State = EntityState.Modified; try { await db.SaveChangesAsync(); } catch(DbUpdateConcurrencyException) { if(!GadgetExists(id)) { return NotFound(); } else { throw; } } } //post api/Gadgets [ResposneType(typeof(Gadget))] public async Task<IHttpActionResult> PostGadget(Gadget gadget) { if(!ModelState.IsValid) { return BadRequest(ModelState); } db.Gadgets.Add(gadget); await db.SaveChangesAsync(); return CreatedAtRoute("DefaultApi", new {id=gadget.GadgetID}, gadget) } //delete api/Gadgets/5 [ResponseType(typeof(Gadget))] public async Task<IHttpActionResult> DeleteGadget(int id) { Gadget gadget = await db.Gadgets.FindAsync(id); if(gadget == null) { return NotFound(); } db.Gadgets.Remove(gadget); await db.SaveChangesAsync(); return Ok(gadget); }
■ OrdersController
firstly overview.
public class OrdersController : ApiController { private StoreContext db = new StoreContext(); protected override void Dispose(bool dispoing) { if(disposing) { db.Dispose(); } base.Dispose(disposing); } private bool OrderExists(int id) { return db.Orders.Count(g => g.OrderID == id) > 0; } }
then details.
// get api/Orders public IQueryable<Order> GetOrders() { return db.Orders; } //get api/Orders/5 [ResponseType(typeof(Order))] public async Task<IHttpActionResult> GetOrder(int id) { Order order = await db.Orders.FindAsync(id); if(order == null) { return NotFound(); } return Ok(order); } //put api/Orders/5 [ResponseType(typeof(void))] public async Task<IHttpActionResult> PutOrder(int id, Order order) { if(!ModelState.IsValid) { return BadRequest(ModelState); } if(id != order.OrderID) { return BadRequest(); } db.Entry(order).State = EntityState.Modified; try { await db.SaveChangesAsync(); } catch(DbUpdateConcurrecyException) { if(!OrderExists(id)) { return NotFound(); } else { throw; } } return StatusCode(HttpStatusCOde.NoContet); } //post api/Orders [RequestType(typeof(Order))] public async Task<IHttpActionResult> PostOrder(Order order) { if(!ModelState.IsValid) { reuturn BadRequest(ModelState); } try { db.Orders.Add(order); foreach(Gadget gadget in order.Gadgets) { db.GadgetOrders.Add(new GadgetOrder{ OrderID = order.OrderID, GadgetID = gadget.GadgetID }) } await db.SaveChangesAsync(); } catch(Exception ex) { return BadRequest(ex.Message); } return CreatedAtRoutne("Default", new {controller = "Home", action="viewOrder", id = order.orderID}, order); } //delete api/Orders/5 [ResponseType(typeof(Order))] public async Task<IHttpActionResult> DeleteOrder(int id) { Order order = await db.Orders.FindAsync(id); if(order == null) { return NotFound(); } db.Orders.Remoe(order); await db.SaveChangesAsync(); return Ok(order); }
待续~~