Advanced further with Repositories and Dynamic-Queries
This commit is contained in:
parent
ec6256ef6e
commit
1ba7087965
@ -1,7 +1,8 @@
|
||||
{
|
||||
"projects": [
|
||||
"src",
|
||||
"test"
|
||||
"test",
|
||||
"src/Services/Ordering"
|
||||
],
|
||||
|
||||
"sdk": {
|
||||
|
@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
public class OrderingController : Controller
|
||||
{
|
||||
private OrderingDbContext _context;
|
||||
private IOrderRepository _orderRepository;
|
||||
private OrderingQueries _queries;
|
||||
|
||||
public OrderingController(IOrderRepository orderRepository,
|
||||
OrderingQueries orderingQueries,
|
||||
OrderingDbContext context)
|
||||
{
|
||||
//Injected objects from the IoC container
|
||||
_orderRepository = orderRepository;
|
||||
_queries = orderingQueries;
|
||||
|
||||
_context = context;
|
||||
}
|
||||
|
||||
|
||||
// GET api/ordering/orders
|
||||
[HttpGet("orders")]
|
||||
public async Task<IActionResult> GetAllOrders()
|
||||
{
|
||||
dynamic response = await _queries.GetAllOrdersIncludingValueObjectsAndChildEntities();
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
|
||||
// GET api/ordering/orders/xxxGUIDxxxx
|
||||
[HttpGet("orders/{orderId:Guid}")]
|
||||
public async Task<IActionResult> GetOrderByGuid(Guid orderId)
|
||||
{
|
||||
//var order = await _orderRepository.Get(orderId);
|
||||
|
||||
var order = await _context.Orders
|
||||
.Include(o => o.ShippingAddress)
|
||||
.Include(o => o.BillingAddress)
|
||||
.Where(o => o.Id == orderId)
|
||||
.SingleOrDefaultAsync<Order>();
|
||||
|
||||
// Dynamically generated a Response-Model that includes only the fields you need in the response.
|
||||
// This keeps the JSON response minimal.
|
||||
// Could also use var
|
||||
dynamic response = new
|
||||
{
|
||||
id = order.Id,
|
||||
orderDate = order.OrderDate,
|
||||
shippingAddress = order.ShippingAddress,
|
||||
billingAddress = order.BillingAddress,
|
||||
//items = order.Items.Select(i => i.Content)
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
//Alternate method if using several parameters instead of part of the URL
|
||||
// GET api/ordering/orders/?orderId=xxxGUIDxxx&otherParam=value
|
||||
//[HttpGet("orders")]
|
||||
//public Order GetOrderByGuid([FromUri] Guid orderId, [FromUri] string otherParam)
|
||||
|
||||
|
||||
// POST api/ordering/orders/create
|
||||
[HttpPut("orders/create")]
|
||||
public async Task<int> Post([FromBody]Order order)
|
||||
{
|
||||
return await _orderRepository.Add(order);
|
||||
|
||||
//_context.Orders.Add(order);
|
||||
//return await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// PUT api/ordering/orders/xxxOrderGUIDxxxx/update
|
||||
[HttpPut("orders/{orderId:Guid}/update")]
|
||||
public async Task<int> UpdateOrder(Guid orderID, [FromBody] Order orderToUpdate)
|
||||
{
|
||||
return await _orderRepository.Add(orderToUpdate);
|
||||
|
||||
//_context.Orders.Update(orderToUpdate);
|
||||
//return await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// DELETE api/ordering/orders/xxxOrderGUIDxxxx
|
||||
[HttpDelete("orders/{orderId:Guid}/delete")]
|
||||
public async Task<int> Delete(Guid id)
|
||||
{
|
||||
return await _orderRepository.Remove(id);
|
||||
|
||||
//Order orderToDelete = _context.Orders
|
||||
// .Where(o => o.Id == id)
|
||||
// .SingleOrDefault();
|
||||
|
||||
//_context.Orders.Remove(orderToDelete);
|
||||
//return await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,81 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
|
||||
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
public class OrdersController : Controller
|
||||
{
|
||||
private OrderingContext _context;
|
||||
|
||||
public OrdersController(OrderingContext context)
|
||||
{
|
||||
//Injected DbContext from the IoC container
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// GET api/orders
|
||||
[HttpGet]
|
||||
public IEnumerable<Order> Get()
|
||||
{
|
||||
//Create generic Address ValueObject
|
||||
Address sampleAddress = new Address("15703 NE 61st Ct.",
|
||||
"Redmond",
|
||||
"Washington",
|
||||
"WA",
|
||||
"United States",
|
||||
"US",
|
||||
"98052",
|
||||
47.661492,
|
||||
-122.131309
|
||||
);
|
||||
|
||||
//Create sample Orders
|
||||
Order order1 = new Order(Guid.NewGuid(), sampleAddress, sampleAddress);
|
||||
_context.Orders.Add(order1);
|
||||
_context.SaveChanges();
|
||||
|
||||
return _context.Orders.ToList();
|
||||
}
|
||||
|
||||
// GET api/orders/xxxGUIDxxxx
|
||||
[HttpGet("{id}")]
|
||||
public string Get(Guid id)
|
||||
{
|
||||
return "value TBD";
|
||||
}
|
||||
|
||||
// POST api/orders
|
||||
[HttpPost]
|
||||
public void Post([FromBody]Order order)
|
||||
{
|
||||
_context.Orders.Add(order);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
// PUT api/orders/xxxGUIDxxxx
|
||||
[HttpPut("{id}")]
|
||||
public void Put(int id, [FromBody]Order value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// DELETE api/orders/xxxGUIDxxxx
|
||||
[HttpDelete("{id}")]
|
||||
public void Delete(Guid id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.DTO
|
||||
{
|
||||
public class OrderDTO
|
||||
{
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.DTO
|
||||
{
|
||||
[DataContract]
|
||||
public sealed class OrderItemDTO
|
||||
{
|
||||
public OrderItemDTO() { }
|
||||
|
||||
[DataMember]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public int Quantity { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public int FulfillmentRemaining { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("ID: {0}, Quantity: {1}, Fulfillment Remaing: {2}", this.Id, this.Quantity, this.FulfillmentRemaining);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,11 +3,11 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
|
||||
|
||||
namespace Ordering.API.Migrations
|
||||
{
|
||||
[DbContext(typeof(OrderingContext))]
|
||||
[DbContext(typeof(OrderingDbContext))]
|
||||
[Migration("20160909202620_MyFirstMigration")]
|
||||
partial class MyFirstMigration
|
||||
{
|
||||
|
@ -3,11 +3,11 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
|
||||
|
||||
namespace Ordering.API.Migrations
|
||||
{
|
||||
[DbContext(typeof(OrderingContext))]
|
||||
[DbContext(typeof(OrderingDbContext))]
|
||||
[Migration("20160909223213_Migration2")]
|
||||
partial class Migration2
|
||||
{
|
||||
|
@ -3,11 +3,11 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
|
||||
|
||||
namespace Ordering.API.Migrations
|
||||
{
|
||||
[DbContext(typeof(OrderingContext))]
|
||||
[DbContext(typeof(OrderingDbContext))]
|
||||
[Migration("20160909233852_Migration3")]
|
||||
partial class Migration3
|
||||
{
|
||||
|
@ -3,11 +3,11 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
|
||||
|
||||
namespace Ordering.API.Migrations
|
||||
{
|
||||
[DbContext(typeof(OrderingContext))]
|
||||
[DbContext(typeof(OrderingDbContext))]
|
||||
partial class OrderingContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
|
@ -11,7 +11,7 @@
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "api/orders",
|
||||
"launchUrl": "api/ordering/orders",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
@ -8,8 +8,11 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Repositories;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API
|
||||
{
|
||||
@ -36,7 +39,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API
|
||||
|
||||
//Add EF Core Context (UnitOfWork)
|
||||
var connection = @"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;";
|
||||
services.AddDbContext<OrderingContext>(options => options.UseSqlServer(connection));
|
||||
services.AddDbContext<OrderingDbContext>(options => options.UseSqlServer(connection));
|
||||
|
||||
services.AddTransient<IOrderRepository, OrderRepository>();
|
||||
services.AddTransient<OrderingQueries, OrderingQueries>();
|
||||
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork
|
||||
{
|
||||
public class OrderingContext : DbContext
|
||||
{
|
||||
public OrderingContext(DbContextOptions<OrderingContext> options)
|
||||
: base(options)
|
||||
{ }
|
||||
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
|
||||
}
|
||||
}
|
@ -18,7 +18,8 @@
|
||||
"Microsoft.EntityFrameworkCore": "1.0.0",
|
||||
"Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0",
|
||||
"Ordering.Domain": "1.0.0-*",
|
||||
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
|
||||
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
|
||||
"Ordering.SqlData": "1.0.0-*"
|
||||
},
|
||||
|
||||
"tools": {
|
||||
|
@ -3,9 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts
|
||||
{
|
||||
interface IBuyerRepository
|
||||
public interface IBuyerRepository : IRepository<Order>
|
||||
{
|
||||
//TBD - To define Specific Actions Not In Base Repo
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContrac
|
||||
{
|
||||
public interface IOrderRepository : IRepository<Order>
|
||||
{
|
||||
Order Get(int associatedConferenceId);
|
||||
//TBD - To define Specific Actions Not In Base Repo
|
||||
|
||||
void ModifyOrder(Order seatOrder);
|
||||
Task<int> Remove(Guid id);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,88 +6,24 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Base interface for implement a "Repository Pattern", for
|
||||
/// Base interface to implement a "Repository Pattern", for
|
||||
/// more information about this pattern see http://martinfowler.com/eaaCatalog/repository.html
|
||||
/// or http://blogs.msdn.com/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Indeed, DbSet is already a generic repository and therefore you would not need a Repo.
|
||||
/// But using this Repo interface allows us to ensure PI (Persistence Ignorant) principle
|
||||
/// within our domain model
|
||||
/// Indeed, DbSet is already a generic repository and for Data-Driven apps
|
||||
/// you might not need custom Repositories.
|
||||
/// But using this interface allows us to ensure PI (Persistence Ignorance) principle
|
||||
/// from the Domain and Application code
|
||||
/// </remarks>
|
||||
/// <typeparam name="TEntity">Type of entity for this repository </typeparam>
|
||||
public interface IRepository<TEntity> : IDisposable
|
||||
where TEntity : Entity
|
||||
where TEntity : AggregateRoot //1:1 relationship between Repository and AggregateRoot
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the unit of work in this repository
|
||||
/// </summary>
|
||||
IUnitOfWork UnitOfWork { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Add item into repository
|
||||
/// </summary>
|
||||
/// <param name="item">Item to add to repository</param>
|
||||
void Add(TEntity item);
|
||||
|
||||
/// <summary>
|
||||
/// Delete item
|
||||
/// </summary>
|
||||
/// <param name="item">Item to delete</param>
|
||||
void Remove(TEntity item);
|
||||
|
||||
/// <summary>
|
||||
/// Set item as modified
|
||||
/// </summary>
|
||||
/// <param name="item">Item to modify</param>
|
||||
void Modify(TEntity item);
|
||||
|
||||
/// <summary>
|
||||
///Track entity into this repository, really in UnitOfWork.
|
||||
///In EF this can be done with Attach and with Update in NH
|
||||
/// </summary>
|
||||
/// <param name="item">Item to attach</param>
|
||||
void TrackItem(TEntity item);
|
||||
|
||||
/// <summary>
|
||||
/// Sets modified entity into the repository.
|
||||
/// When calling Commit() method in UnitOfWork
|
||||
/// these changes will be saved into the storage
|
||||
/// </summary>
|
||||
/// <param name="persisted">The persisted item</param>
|
||||
/// <param name="current">The current item</param>
|
||||
void Merge(TEntity persisted, TEntity current);
|
||||
|
||||
/// <summary>
|
||||
/// Get element by entity key
|
||||
/// </summary>
|
||||
/// <param name="id">Entity key value</param>
|
||||
/// <returns></returns>
|
||||
TEntity Get(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Get all elements of type TEntity in repository
|
||||
/// </summary>
|
||||
/// <returns>List of selected elements</returns>
|
||||
IEnumerable<TEntity> GetAll();
|
||||
|
||||
/// <summary>
|
||||
/// Get all elements of type TEntity in repository
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">Page index</param>
|
||||
/// <param name="pageCount">Number of elements in each page</param>
|
||||
/// <param name="orderByExpression">Order by expression for this query</param>
|
||||
/// <param name="ascending">Specify if order is ascending</param>
|
||||
/// <returns>List of selected elements</returns>
|
||||
IEnumerable<TEntity> GetPaged<KProperty>(int pageIndex, int pageCount, Expression<Func<TEntity, KProperty>> orderByExpression, bool ascending);
|
||||
|
||||
/// <summary>
|
||||
/// Get elements of type TEntity in repository
|
||||
/// </summary>
|
||||
/// <param name="filter">Filter that each element do match</param>
|
||||
/// <returns>List of selected elements</returns>
|
||||
IEnumerable<TEntity> GetFiltered(Expression<Func<TEntity, bool>> filter);
|
||||
Task<int> Add(TEntity item);
|
||||
Task<int> Remove(TEntity item);
|
||||
Task<int> Update(TEntity item);
|
||||
Task<TEntity> Get(Guid id);
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
{
|
||||
/// <summary>
|
||||
/// Contract for ‘UnitOfWork pattern’. For more
|
||||
/// related info see http://martinfowler.com/eaaCatalog/unitOfWork.html or
|
||||
/// http://msdn.microsoft.com/en-us/magazine/dd882510.aspx
|
||||
/// In this Microservice, the Unit Of Work is implemented using the out-of-box
|
||||
/// Entity Framework Context (EF Core DbContext) persistence engine. But in order to
|
||||
/// comply the PI (Persistence Ignorant) principle in our Domain, we implement this interface/contract.
|
||||
/// This interface/contract should be complied by any UoW implementation to be used with this Domain.
|
||||
/// </summary>
|
||||
public interface IUnitOfWork
|
||||
: IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Commit all changes made in a container.
|
||||
/// </summary>
|
||||
///<remarks>
|
||||
/// If the entity have fixed properties and any optimistic concurrency problem exists,
|
||||
/// then an exception is thrown
|
||||
///</remarks>
|
||||
void Commit();
|
||||
|
||||
/// <summary>
|
||||
/// Commit all changes made in a container.
|
||||
/// </summary>
|
||||
///<remarks>
|
||||
/// If the entity have fixed properties and any optimistic concurrency problem exists,
|
||||
/// then 'client changes' are refreshed - Client wins
|
||||
///</remarks>
|
||||
void CommitAndRefreshChanges();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Rollback tracked changes. See references of UnitOfWork pattern
|
||||
/// </summary>
|
||||
void RollbackChanges();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries
|
||||
{
|
||||
public class OrderingQueries
|
||||
{
|
||||
private OrderingDbContext _dbContext;
|
||||
|
||||
public OrderingQueries(OrderingDbContext orderingDbContext)
|
||||
{
|
||||
_dbContext = orderingDbContext;
|
||||
}
|
||||
|
||||
public async Task<dynamic> GetAllOrdersIncludingValueObjectsAndChildEntities()
|
||||
{
|
||||
var orders = await _dbContext.Orders
|
||||
.Include(o => o.ShippingAddress)
|
||||
.Include(o => o.BillingAddress)
|
||||
//.Include(o => o.Items)
|
||||
.ToListAsync<Order>();
|
||||
|
||||
// Dynamically generated a Response-Model that includes only the fields you need in the response.
|
||||
// This keeps the JSON response minimal.
|
||||
// Could also use var
|
||||
dynamic response = orders.Select(o => new
|
||||
{
|
||||
id = o.Id,
|
||||
orderDate = o.OrderDate,
|
||||
shippingAddress = o.ShippingAddress,
|
||||
billingAddress = o.BillingAddress,
|
||||
//items = o.Items.Select(i => i.Content)
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.SeedWork;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.Repositories
|
||||
{
|
||||
//1:1 relationship between Repository and Aggregate (i.e. OrderRepository and Order)
|
||||
public class OrderRepository
|
||||
: Repository<Order>, IOrderRepository
|
||||
{
|
||||
public OrderRepository(OrderingDbContext unitOfWork)
|
||||
: base(unitOfWork) { }
|
||||
|
||||
//TBD - To define Specific Actions Not In Base Repository class
|
||||
|
||||
public async Task<int> Remove(Guid orderId)
|
||||
{
|
||||
if (orderId == null)
|
||||
return 0;
|
||||
|
||||
Order orderToDelete = await this.Get(orderId);
|
||||
|
||||
|
||||
//attach item if not exist
|
||||
_unitOfWork.Attach(orderToDelete);
|
||||
|
||||
//set as "removed"
|
||||
_unitOfWork.Remove(orderToDelete);
|
||||
|
||||
return await _unitOfWork.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,82 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.SeedWork
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.SeedWork
|
||||
{
|
||||
public class Repository
|
||||
public class Repository<TEntity> : IRepository<TEntity>
|
||||
where TEntity : AggregateRoot //1:1 relationship between Repository and AggregateRoot
|
||||
{
|
||||
protected readonly DbContext _unitOfWork;
|
||||
|
||||
//DbContext injected thru DI from ASP.NET Core bootstrap
|
||||
public Repository(DbContext unitOfWork)
|
||||
{
|
||||
if (unitOfWork == null)
|
||||
throw new ArgumentNullException("unitOfWork");
|
||||
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public DbContext UnitOfWork
|
||||
{
|
||||
get
|
||||
{
|
||||
return _unitOfWork;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task<int> Add(TEntity item)
|
||||
{
|
||||
if (item == null)
|
||||
return 0;
|
||||
|
||||
_unitOfWork.Set<TEntity>().Add(item); // add new item in this set
|
||||
|
||||
return await _unitOfWork.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public virtual async Task<int> Remove(TEntity item)
|
||||
{
|
||||
if (item == null)
|
||||
return 0;
|
||||
|
||||
//attach item if not exist
|
||||
_unitOfWork.Set<TEntity>().Attach(item);
|
||||
|
||||
//set as "removed"
|
||||
_unitOfWork.Set<TEntity>().Remove(item);
|
||||
|
||||
return await _unitOfWork.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public virtual async Task<int> Update(TEntity item)
|
||||
{
|
||||
if (item == null)
|
||||
return 0;
|
||||
|
||||
_unitOfWork.Set<TEntity>().Update(item);
|
||||
|
||||
return await _unitOfWork.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public virtual async Task<TEntity> Get(Guid id)
|
||||
{
|
||||
if (id != Guid.Empty)
|
||||
return await _unitOfWork.Set<TEntity>().FirstOrDefaultAsync(o => o.Id == id);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_unitOfWork != null)
|
||||
_unitOfWork.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork
|
||||
{
|
||||
public class OrderingDbContext : DbContext
|
||||
{
|
||||
public OrderingDbContext(DbContextOptions<OrderingDbContext> options)
|
||||
: base(options)
|
||||
{ }
|
||||
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
//If running from ASP.NET Core, config is done at StartUp.cs --> ConfigureServices() outside
|
||||
//and injected through DI later on. The following config is used when running Tests or similar contexts
|
||||
if (!optionsBuilder.IsConfigured)
|
||||
{
|
||||
//SQL LOCALDB
|
||||
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
{
|
||||
{
|
||||
"version": "1.0.0-*",
|
||||
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "1.6.0"
|
||||
"Microsoft.EntityFrameworkCore": "1.0.0",
|
||||
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
|
||||
"NETStandard.Library": "1.6.0",
|
||||
"Ordering.Domain": "1.0.0-*"
|
||||
},
|
||||
|
||||
"frameworks": {
|
||||
|
104
test/Services/Ordering.Test/DataIntegrationTests.cs
Normal file
104
test/Services/Ordering.Test/DataIntegrationTests.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xunit;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace DataIntegrationTests
|
||||
{
|
||||
//Basic documentation for Testing EF Core classes
|
||||
// http://ef.readthedocs.io/en/latest/miscellaneous/testing.html
|
||||
public class Tests
|
||||
{
|
||||
private static DbContextOptions<OrderingDbContext> CreateNewContextOptionsForInMemoryDB()
|
||||
{
|
||||
// Create a fresh service provider, and therefore a fresh
|
||||
// InMemory database instance.
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddEntityFrameworkInMemoryDatabase()
|
||||
.BuildServiceProvider();
|
||||
|
||||
// Create a new options instance telling the context to use an
|
||||
// InMemory database and the new service provider.
|
||||
var builder = new DbContextOptionsBuilder<OrderingDbContext>();
|
||||
builder.UseInMemoryDatabase()
|
||||
.UseInternalServiceProvider(serviceProvider);
|
||||
|
||||
return builder.Options;
|
||||
}
|
||||
|
||||
private static DbContextOptions<OrderingDbContext> CreateNewContextOptionsForSqlDB()
|
||||
{
|
||||
// Create a new options instance telling the context to use a Sql database
|
||||
var builder = new DbContextOptionsBuilder<OrderingDbContext>();
|
||||
|
||||
//SQL LOCALDB
|
||||
builder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;");
|
||||
|
||||
return builder.Options;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_orders_to_database()
|
||||
{
|
||||
// All contexts that share the same service provider will share the same database
|
||||
|
||||
//Using InMemory DB
|
||||
var options = CreateNewContextOptionsForInMemoryDB();
|
||||
|
||||
//Using Sql LocalDB
|
||||
//var options = CreateNewContextOptionsForSqlDB();
|
||||
|
||||
|
||||
// Run the test against one instance of the context
|
||||
using (var context = new OrderingDbContext(options))
|
||||
{
|
||||
|
||||
//Create generic Address ValueObject
|
||||
Address sampleAddress = new Address("15703 NE 61st Ct.",
|
||||
"Redmond",
|
||||
"Washington",
|
||||
"WA",
|
||||
"United States",
|
||||
"US",
|
||||
"98052",
|
||||
47.661492,
|
||||
-122.131309
|
||||
);
|
||||
//Create sample Orders
|
||||
Order order1 = new Order(Guid.NewGuid(), sampleAddress, sampleAddress);
|
||||
context.Orders.Add(order1);
|
||||
context.SaveChanges();
|
||||
|
||||
Assert.True(true);
|
||||
}
|
||||
|
||||
//// Use a separate instance of the context to verify correct data was saved to database
|
||||
using (var context = new OrderingDbContext(options))
|
||||
{
|
||||
var orders = context.Orders
|
||||
.Include(o => o.ShippingAddress)
|
||||
.Include(o => o.BillingAddress)
|
||||
.ToList();
|
||||
//Could be using .Load() if you don't want to create a List
|
||||
|
||||
//SAMPLE
|
||||
//var company = context.Companies
|
||||
// .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Car)
|
||||
// .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Country)
|
||||
// .FirstOrDefault(co => co.companyID == companyID);
|
||||
|
||||
//Assert when running test with a clean In-Memory DB
|
||||
//Assert.Equal(1, context.Orders.Count());
|
||||
|
||||
Assert.Equal("Redmond", orders.First<Order>().ShippingAddress.City);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -11,9 +11,11 @@
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
@ -1,14 +0,0 @@
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Tests
|
||||
{
|
||||
public class Tests
|
||||
{
|
||||
[Fact]
|
||||
public void Test1()
|
||||
{
|
||||
Assert.True(true);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,11 @@
|
||||
"dependencies": {
|
||||
"System.Runtime.Serialization.Primitives": "4.1.1",
|
||||
"xunit": "2.1.0",
|
||||
"dotnet-test-xunit": "2.2.0-preview2-build1029"
|
||||
"dotnet-test-xunit": "2.2.0-preview2-build1029",
|
||||
"Ordering.Domain": "1.0.0-*",
|
||||
"Ordering.SqlData": "1.0.0-*",
|
||||
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
|
||||
"Microsoft.EntityFrameworkCore.InMemory": "1.0.0"
|
||||
},
|
||||
"testRunner": "xunit",
|
||||
"frameworks": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user