Browse Source

Added OrderItem features and moved queries to the OrderingQueries class

pull/49/merge
Cesar De la Torre 8 years ago
parent
commit
68bcb70fd2
14 changed files with 546 additions and 94 deletions
  1. +4
    -26
      src/Services/Ordering/Ordering.API/Controllers/OrderingController.cs
  2. +89
    -0
      src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.Designer.cs
  3. +37
    -0
      src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.cs
  4. +121
    -0
      src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.Designer.cs
  5. +46
    -0
      src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.cs
  6. +37
    -0
      src/Services/Ordering/Ordering.API/Migrations/OrderingContextModelSnapshot.cs
  7. +6
    -1
      src/Services/Ordering/Ordering.API/Startup.cs
  8. +68
    -14
      src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/Order.cs
  9. +9
    -13
      src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/OrderItem.cs
  10. +48
    -2
      src/Services/Ordering/Ordering.SqlData/Queries/OrderingQueries.cs
  11. +40
    -0
      src/Services/Ordering/Ordering.SqlData/UnitOfWork/DBContextUtil.cs
  12. +16
    -0
      src/Services/Ordering/Ordering.SqlData/UnitOfWork/OrderingDbContext.cs
  13. +5
    -2
      src/Services/Ordering/Ordering.SqlData/project.json
  14. +20
    -36
      test/Services/Ordering.Test/DataIntegrationTests.cs

+ 4
- 26
src/Services/Ordering/Ordering.API/Controllers/OrderingController.cs View File

@ -15,19 +15,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
[Route("api/[controller]")] [Route("api/[controller]")]
public class OrderingController : Controller public class OrderingController : Controller
{ {
private OrderingDbContext _context;
private IOrderRepository _orderRepository; private IOrderRepository _orderRepository;
private OrderingQueries _queries; private OrderingQueries _queries;
public OrderingController(IOrderRepository orderRepository, public OrderingController(IOrderRepository orderRepository,
OrderingQueries orderingQueries,
OrderingDbContext context)
OrderingQueries orderingQueries
)
{ {
//Injected objects from the IoC container //Injected objects from the IoC container
_orderRepository = orderRepository; _orderRepository = orderRepository;
_queries = orderingQueries; _queries = orderingQueries;
_context = context;
} }
@ -42,28 +39,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
// GET api/ordering/orders/xxxGUIDxxxx // GET api/ordering/orders/xxxGUIDxxxx
[HttpGet("orders/{orderId:Guid}")] [HttpGet("orders/{orderId:Guid}")]
public async Task<IActionResult> GetOrderByGuid(Guid orderId)
public async Task<IActionResult> GetOrderById(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)
};
dynamic response = await _queries.GetOrderById(orderId);
return Ok(response); return Ok(response);
} }


+ 89
- 0
src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.Designer.cs View File

@ -0,0 +1,89 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
namespace Ordering.API.Migrations
{
[DbContext(typeof(OrderingDbContext))]
[Migration("20160913052800_Migration4")]
partial class Migration4
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.0.0-rtm-21431")
.HasAnnotation("Relational:Sequence:shared.OrderSequences", "'OrderSequences', 'shared', '1001', '1', '', '', 'Int32', 'False'")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Address", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("City");
b.Property<string>("Country");
b.Property<string>("CountryCode");
b.Property<double>("Latitude");
b.Property<double>("Longitude");
b.Property<string>("State");
b.Property<string>("StateCode");
b.Property<string>("Street");
b.Property<string>("ZipCode");
b.HasKey("Id");
b.ToTable("Address");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid?>("BillingAddressId");
b.Property<Guid>("BuyerId");
b.Property<DateTime>("OrderDate");
b.Property<int>("SequenceNumber")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("NEXT VALUE FOR shared.OrderSequences");
b.Property<Guid?>("ShippingAddressId");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("BillingAddressId");
b.HasIndex("ShippingAddressId");
b.ToTable("Orders");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Address", "BillingAddress")
.WithMany()
.HasForeignKey("BillingAddressId");
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Address", "ShippingAddress")
.WithMany()
.HasForeignKey("ShippingAddressId");
});
}
}
}

+ 37
- 0
src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.cs View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ordering.API.Migrations
{
public partial class Migration4 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "shared");
migrationBuilder.CreateSequence<int>(
name: "OrderSequences",
schema: "shared",
startValue: 1001L);
migrationBuilder.AddColumn<int>(
name: "SequenceNumber",
table: "Orders",
nullable: false,
defaultValueSql: "NEXT VALUE FOR shared.OrderSequences");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropSequence(
name: "OrderSequences",
schema: "shared");
migrationBuilder.DropColumn(
name: "SequenceNumber",
table: "Orders");
}
}
}

+ 121
- 0
src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.Designer.cs View File

@ -0,0 +1,121 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
namespace Ordering.API.Migrations
{
[DbContext(typeof(OrderingDbContext))]
[Migration("20160913061710_Migration5")]
partial class Migration5
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.0.0-rtm-21431")
.HasAnnotation("Relational:Sequence:shared.OrderSequences", "'OrderSequences', 'shared', '1001', '1', '', '', 'Int32', 'False'")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Address", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("City");
b.Property<string>("Country");
b.Property<string>("CountryCode");
b.Property<double>("Latitude");
b.Property<double>("Longitude");
b.Property<string>("State");
b.Property<string>("StateCode");
b.Property<string>("Street");
b.Property<string>("ZipCode");
b.HasKey("Id");
b.ToTable("Address");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid?>("BillingAddressId");
b.Property<Guid>("BuyerId");
b.Property<DateTime>("OrderDate");
b.Property<int>("SequenceNumber")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("NEXT VALUE FOR shared.OrderSequences");
b.Property<Guid?>("ShippingAddressId");
b.Property<int>("Status");
b.HasKey("Id");
b.HasIndex("BillingAddressId");
b.HasIndex("ShippingAddressId");
b.ToTable("Orders");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<decimal>("Discount");
b.Property<int>("FulfillmentRemaining");
b.Property<Guid>("OrderId");
b.Property<Guid>("ProductId");
b.Property<int>("Quantity");
b.Property<decimal>("UnitPrice");
b.HasKey("Id");
b.HasIndex("OrderId");
b.ToTable("OrderItem");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Address", "BillingAddress")
.WithMany()
.HasForeignKey("BillingAddressId");
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Address", "ShippingAddress")
.WithMany()
.HasForeignKey("ShippingAddressId");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderItem", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order")
.WithMany("OrderItems")
.HasForeignKey("OrderId")
.OnDelete(DeleteBehavior.Cascade);
});
}
}
}

+ 46
- 0
src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.cs View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ordering.API.Migrations
{
public partial class Migration5 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "OrderItem",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
Discount = table.Column<decimal>(nullable: false),
FulfillmentRemaining = table.Column<int>(nullable: false),
OrderId = table.Column<Guid>(nullable: false),
ProductId = table.Column<Guid>(nullable: false),
Quantity = table.Column<int>(nullable: false),
UnitPrice = table.Column<decimal>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OrderItem", x => x.Id);
table.ForeignKey(
name: "FK_OrderItem_Orders_OrderId",
column: x => x.OrderId,
principalTable: "Orders",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_OrderItem_OrderId",
table: "OrderItem",
column: "OrderId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "OrderItem");
}
}
}

+ 37
- 0
src/Services/Ordering/Ordering.API/Migrations/OrderingContextModelSnapshot.cs View File

@ -14,6 +14,7 @@ namespace Ordering.API.Migrations
{ {
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "1.0.0-rtm-21431") .HasAnnotation("ProductVersion", "1.0.0-rtm-21431")
.HasAnnotation("Relational:Sequence:shared.OrderSequences", "'OrderSequences', 'shared', '1001', '1', '', '', 'Int32', 'False'")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Address", b => modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Address", b =>
@ -55,6 +56,10 @@ namespace Ordering.API.Migrations
b.Property<DateTime>("OrderDate"); b.Property<DateTime>("OrderDate");
b.Property<int>("SequenceNumber")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("NEXT VALUE FOR shared.OrderSequences");
b.Property<Guid?>("ShippingAddressId"); b.Property<Guid?>("ShippingAddressId");
b.Property<int>("Status"); b.Property<int>("Status");
@ -68,6 +73,30 @@ namespace Ordering.API.Migrations
b.ToTable("Orders"); b.ToTable("Orders");
}); });
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<decimal>("Discount");
b.Property<int>("FulfillmentRemaining");
b.Property<Guid>("OrderId");
b.Property<Guid>("ProductId");
b.Property<int>("Quantity");
b.Property<decimal>("UnitPrice");
b.HasKey("Id");
b.HasIndex("OrderId");
b.ToTable("OrderItem");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order", b => modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order", b =>
{ {
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Address", "BillingAddress") b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Address", "BillingAddress")
@ -78,6 +107,14 @@ namespace Ordering.API.Migrations
.WithMany() .WithMany()
.HasForeignKey("ShippingAddressId"); .HasForeignKey("ShippingAddressId");
}); });
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderItem", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order")
.WithMany("OrderItems")
.HasForeignKey("OrderId")
.OnDelete(DeleteBehavior.Cascade);
});
} }
} }
} }

+ 6
- 1
src/Services/Ordering/Ordering.API/Startup.cs View File

@ -39,7 +39,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API
//Add EF Core Context (UnitOfWork) //Add EF Core Context (UnitOfWork)
var connection = @"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;"; var connection = @"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;";
services.AddDbContext<OrderingDbContext>(options => options.UseSqlServer(connection));
services.AddDbContext<OrderingDbContext>(options => options.UseSqlServer(connection)
.UseSqlServer(connection, b => b.MigrationsAssembly("Ordering.API"))
//(CDLTLL) MigrationsAssembly will be Ordering.SqlData, but when supported
//Standard Library 1.6 by "Microsoft.EntityFrameworkCore.Tools"
//Version "1.0.0-preview2-final" just supports .NET Core
);
services.AddTransient<IOrderRepository, OrderRepository>(); services.AddTransient<IOrderRepository, OrderRepository>();
services.AddTransient<OrderingQueries, OrderingQueries>(); services.AddTransient<OrderingQueries, OrderingQueries>();


+ 68
- 14
src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/Order.cs View File

@ -20,6 +20,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
this.ShippingAddress = shippingAddress; this.ShippingAddress = shippingAddress;
this.BillingAddress = billingAddress; this.BillingAddress = billingAddress;
this.OrderDate = orderDate; this.OrderDate = orderDate;
this.Status = OrderStatus.New;
} }
//Infrastructure requisite - Parameterless constructor needed by EF //Infrastructure requisite - Parameterless constructor needed by EF
@ -27,22 +29,32 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
//Order ID comes derived from the Entity base class //Order ID comes derived from the Entity base class
//List<OrderItem> _orderItems;
//public virtual List<OrderItem> orderItems
//{
// get
// {
// if (_orderItems == null)
// _orderItems = new List<OrderItem>();
List<OrderItem> _orderItems;
public virtual List<OrderItem> OrderItems
{
get
{
if (_orderItems == null)
_orderItems = new List<OrderItem>();
// return _orderItems;
// }
return _orderItems;
}
// private set
// {
// _orderItems = value;
// }
//}
private set
{
_orderItems = value;
}
}
public string OrderNumber
{
get
{
return string.Format("{0}/{1}-{2}", OrderDate.Year, OrderDate.Month, SequenceNumber);
}
}
public int SequenceNumber { get; set; }
public virtual Guid BuyerId { get; private set; } public virtual Guid BuyerId { get; private set; }
@ -54,5 +66,47 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
public virtual OrderStatus Status { get; private set; } public virtual OrderStatus Status { get; private set; }
////////////////////////////////////////////////////////////////////////////////////////
//Domain Rules and Logic in Order Aggregate-Root (Sample of a "NO ANEMIC DOMAIN MODEL" )
////////////////////////////////////////////////////////////////////////////////////////
public OrderItem AddNewOrderItem(Guid productId, int quantity, decimal unitPrice, decimal discount)
{
//check preconditions
if (productId == Guid.Empty)
throw new ArgumentNullException("productId");
if (quantity <= 0)
{
throw new ArgumentException("The quantity of Product in an Order cannot be equal or less than cero");
}
//check discount values
if (discount < 0)
discount = 0;
if (discount > 100)
discount = 100;
//create new order line
var newOrderItem = new OrderItem()
{
OrderId = this.Id,
ProductId = productId,
Quantity = quantity,
FulfillmentRemaining = quantity,
Discount = discount,
UnitPrice = unitPrice
};
//set identity
newOrderItem.GenerateNewIdentity();
//add order item
this.OrderItems.Add(newOrderItem);
//return added orderline
return newOrderItem;
}
} }
} }

+ 9
- 13
src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/OrderItem.cs View File

@ -7,29 +7,25 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
{ {
public class OrderItem : Entity public class OrderItem : Entity
{ {
//(CDLTLL) Might remove this constructor so
// just with a method in the Order-AggregateRoot you can add an OrderItem..
//public OrderItem(Guid itemId, decimal itemPrice, int quantity)
//{
// this.Id = itemId;
// this.ItemPrice = itemPrice;
// this.Quantity = quantity;
// this.FulfillmentRemaining = quantity; <---- Put this logic into the AggregateRoot method AddOrderItem()
//}
public OrderItem() { } // Infrastructure. EF might need a plain constructor. Do not use.
protected OrderItem() { } // Infrastructure. EF might need a plain constructor. Do not use.
//NOTE: The OrderItem Id (Id) comes from the Entity base class
public Guid ItemId { get; set; }
public Guid ProductId { get; set; }
public decimal ItemPrice { get; set; }
public Guid OrderId { get; set; }
public decimal UnitPrice { get; set; }
public int Quantity { get; set; } public int Quantity { get; set; }
public decimal Discount { get; set; }
public int FulfillmentRemaining { get; set; } public int FulfillmentRemaining { get; set; }
public override string ToString() public override string ToString()
{ {
return String.Format("ID: {0}, Quantity: {1}, Fulfillment Remaing: {2}", this.ItemId, this.Quantity, this.FulfillmentRemaining);
return String.Format("Product Id: {0}, Quantity: {1}, Fulfillment Remaing: {2}", this.Id, this.Quantity, this.FulfillmentRemaining);
} }
} }
} }

+ 48
- 2
src/Services/Ordering/Ordering.SqlData/Queries/OrderingQueries.cs View File

@ -24,7 +24,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries
var orders = await _dbContext.Orders var orders = await _dbContext.Orders
.Include(o => o.ShippingAddress) .Include(o => o.ShippingAddress)
.Include(o => o.BillingAddress) .Include(o => o.BillingAddress)
//.Include(o => o.Items)
.Include(o => o.OrderItems)
.ToListAsync<Order>(); .ToListAsync<Order>();
// Dynamically generated a Response-Model that includes only the fields you need in the response. // Dynamically generated a Response-Model that includes only the fields you need in the response.
@ -33,13 +33,59 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries
dynamic response = orders.Select(o => new dynamic response = orders.Select(o => new
{ {
id = o.Id, id = o.Id,
orderNumber = o.OrderNumber,
buyerId = o.BuyerId,
orderDate = o.OrderDate, orderDate = o.OrderDate,
status = o.Status,
shippingAddress = o.ShippingAddress, shippingAddress = o.ShippingAddress,
billingAddress = o.BillingAddress, billingAddress = o.BillingAddress,
//items = o.Items.Select(i => i.Content)
orderItems = o.OrderItems.Select(i => new
{
id = i.Id,
productId = i.ProductId,
unitPrice = i.UnitPrice,
quantity = i.Quantity,
discount = i.Discount
}
)
}); });
return response; return response;
} }
public async Task<dynamic> GetOrderById(Guid orderId)
{
var order = await _dbContext.Orders
.Include(o => o.ShippingAddress)
.Include(o => o.BillingAddress)
.Include(o => o.OrderItems)
.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,
orderNumber = order.OrderNumber,
buyerId = order.BuyerId,
orderDate = order.OrderDate,
status = order.Status,
shippingAddress = order.ShippingAddress,
billingAddress = order.BillingAddress,
orderItems = order.OrderItems.Select(i => new
{
id = i.Id,
productId = i.ProductId,
unitPrice = i.UnitPrice,
quantity = i.Quantity,
discount = i.Discount
}
)
};
return response;
}
} }
} }

+ 40
- 0
src/Services/Ordering/Ordering.SqlData/UnitOfWork/DBContextUtil.cs View File

@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork
{
public class DbContextUtil
{
public 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;
}
public 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;
}
}
}

+ 16
- 0
src/Services/Ordering/Ordering.SqlData/UnitOfWork/OrderingDbContext.cs View File

@ -27,5 +27,21 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork
} }
} }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Add your customizations after calling base.OnModelCreating(builder);
//Sequence to be used as part of the OrderNumber
modelBuilder.HasSequence<int>("OrderSequences", schema: "shared")
.StartsAt(1001)
.IncrementsBy(1);
modelBuilder.Entity<Order>()
.Property(o => o.SequenceNumber)
.HasDefaultValueSql("NEXT VALUE FOR shared.OrderSequences");
}
} }
} }

+ 5
- 2
src/Services/Ordering/Ordering.SqlData/project.json View File

@ -5,12 +5,15 @@
"Microsoft.EntityFrameworkCore": "1.0.0", "Microsoft.EntityFrameworkCore": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0", "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
"NETStandard.Library": "1.6.0", "NETStandard.Library": "1.6.0",
"Ordering.Domain": "1.0.0-*"
"Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0",
"Ordering.Domain": "1.0.0-*",
"Microsoft.EntityFrameworkCore.InMemory": "1.0.0"
}, },
"frameworks": { "frameworks": {
"netstandard1.6": { "netstandard1.6": {
"imports": "dnxcore50"
"imports": [ "dnxcore50", "portable-net451+win8" ]
} }
} }
} }

+ 20
- 36
test/Services/Ordering.Test/DataIntegrationTests.cs View File

@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Xunit; using Xunit;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork; using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Repositories;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -15,49 +17,21 @@ namespace DataIntegrationTests
// http://ef.readthedocs.io/en/latest/miscellaneous/testing.html // http://ef.readthedocs.io/en/latest/miscellaneous/testing.html
public class Tests 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] [Fact]
public void Add_orders_to_database()
public void Add_order_to_data_model()
{ {
// All contexts that share the same service provider will share the same database // All contexts that share the same service provider will share the same database
//Using InMemory DB //Using InMemory DB
var options = CreateNewContextOptionsForInMemoryDB();
//var options = DbContextUtil.CreateNewContextOptionsForInMemoryDB();
//Using Sql LocalDB //Using Sql LocalDB
//var options = CreateNewContextOptionsForSqlDB();
var options = DbContextUtil.CreateNewContextOptionsForSqlDB();
// Run the test against one instance of the context // Run the test against one instance of the context
using (var context = new OrderingDbContext(options)) using (var context = new OrderingDbContext(options))
{ {
IOrderRepository orderRepository = new OrderRepository(context);
//Create generic Address ValueObject //Create generic Address ValueObject
Address sampleAddress = new Address("15703 NE 61st Ct.", Address sampleAddress = new Address("15703 NE 61st Ct.",
@ -72,10 +46,20 @@ namespace DataIntegrationTests
); );
//Create sample Orders //Create sample Orders
Order order1 = new Order(Guid.NewGuid(), sampleAddress, sampleAddress); Order order1 = new Order(Guid.NewGuid(), sampleAddress, sampleAddress);
context.Orders.Add(order1);
context.SaveChanges();
Assert.True(true);
//Add a few OrderItems
order1.AddNewOrderItem(Guid.NewGuid(), 2, 25, 30);
order1.AddNewOrderItem(Guid.NewGuid(), 1, 58, 0);
order1.AddNewOrderItem(Guid.NewGuid(), 1, 60, 0);
order1.AddNewOrderItem(Guid.NewGuid(), 3, 12, 0);
order1.AddNewOrderItem(Guid.NewGuid(), 5, 3, 0);
orderRepository.Add(order1);
//With no Async Repository
//context.Orders.Add(order1);
//context.SaveChanges();
} }
//// Use a separate instance of the context to verify correct data was saved to database //// Use a separate instance of the context to verify correct data was saved to database
@ -87,7 +71,7 @@ namespace DataIntegrationTests
.ToList(); .ToList();
//Could be using .Load() if you don't want to create a List //Could be using .Load() if you don't want to create a List
//SAMPLE
//OTHER SAMPLE
//var company = context.Companies //var company = context.Companies
// .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Car) // .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Car)
// .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Country) // .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Country)


Loading…
Cancel
Save