diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrderingController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrderingController.cs index a439925d4..c5064b812 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrderingController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrderingController.cs @@ -15,19 +15,16 @@ 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) + OrderingQueries orderingQueries + ) { //Injected objects from the IoC container _orderRepository = orderRepository; _queries = orderingQueries; - - _context = context; } @@ -42,28 +39,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers // GET api/ordering/orders/xxxGUIDxxxx [HttpGet("orders/{orderId:Guid}")] - public async Task GetOrderByGuid(Guid orderId) + public async Task 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(); - - // 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); } diff --git a/src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.Designer.cs b/src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.Designer.cs new file mode 100644 index 000000000..c0386ae73 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.Designer.cs @@ -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("Id") + .ValueGeneratedOnAdd(); + + b.Property("City"); + + b.Property("Country"); + + b.Property("CountryCode"); + + b.Property("Latitude"); + + b.Property("Longitude"); + + b.Property("State"); + + b.Property("StateCode"); + + b.Property("Street"); + + b.Property("ZipCode"); + + b.HasKey("Id"); + + b.ToTable("Address"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BillingAddressId"); + + b.Property("BuyerId"); + + b.Property("OrderDate"); + + b.Property("SequenceNumber") + .ValueGeneratedOnAdd() + .HasDefaultValueSql("NEXT VALUE FOR shared.OrderSequences"); + + b.Property("ShippingAddressId"); + + b.Property("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"); + }); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.cs b/src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.cs new file mode 100644 index 000000000..e719fd4c2 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.cs @@ -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( + name: "OrderSequences", + schema: "shared", + startValue: 1001L); + + migrationBuilder.AddColumn( + 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"); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.Designer.cs b/src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.Designer.cs new file mode 100644 index 000000000..e6af61b39 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.Designer.cs @@ -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("Id") + .ValueGeneratedOnAdd(); + + b.Property("City"); + + b.Property("Country"); + + b.Property("CountryCode"); + + b.Property("Latitude"); + + b.Property("Longitude"); + + b.Property("State"); + + b.Property("StateCode"); + + b.Property("Street"); + + b.Property("ZipCode"); + + b.HasKey("Id"); + + b.ToTable("Address"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BillingAddressId"); + + b.Property("BuyerId"); + + b.Property("OrderDate"); + + b.Property("SequenceNumber") + .ValueGeneratedOnAdd() + .HasDefaultValueSql("NEXT VALUE FOR shared.OrderSequences"); + + b.Property("ShippingAddressId"); + + b.Property("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("Id") + .ValueGeneratedOnAdd(); + + b.Property("Discount"); + + b.Property("FulfillmentRemaining"); + + b.Property("OrderId"); + + b.Property("ProductId"); + + b.Property("Quantity"); + + b.Property("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); + }); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.cs b/src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.cs new file mode 100644 index 000000000..1f005d177 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.cs @@ -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(nullable: false), + Discount = table.Column(nullable: false), + FulfillmentRemaining = table.Column(nullable: false), + OrderId = table.Column(nullable: false), + ProductId = table.Column(nullable: false), + Quantity = table.Column(nullable: false), + UnitPrice = table.Column(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"); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Migrations/OrderingContextModelSnapshot.cs b/src/Services/Ordering/Ordering.API/Migrations/OrderingContextModelSnapshot.cs index 739e05927..3ad13c891 100644 --- a/src/Services/Ordering/Ordering.API/Migrations/OrderingContextModelSnapshot.cs +++ b/src/Services/Ordering/Ordering.API/Migrations/OrderingContextModelSnapshot.cs @@ -14,6 +14,7 @@ namespace Ordering.API.Migrations { 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 => @@ -55,6 +56,10 @@ namespace Ordering.API.Migrations b.Property("OrderDate"); + b.Property("SequenceNumber") + .ValueGeneratedOnAdd() + .HasDefaultValueSql("NEXT VALUE FOR shared.OrderSequences"); + b.Property("ShippingAddressId"); b.Property("Status"); @@ -68,6 +73,30 @@ namespace Ordering.API.Migrations b.ToTable("Orders"); }); + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Discount"); + + b.Property("FulfillmentRemaining"); + + b.Property("OrderId"); + + b.Property("ProductId"); + + b.Property("Quantity"); + + b.Property("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") @@ -78,6 +107,14 @@ namespace Ordering.API.Migrations .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); + }); } } } diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index b29d95e94..d65553ec0 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -39,7 +39,12 @@ 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(options => options.UseSqlServer(connection)); + services.AddDbContext(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(); services.AddTransient(); diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/Order.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/Order.cs index e71e922ad..4744c3cfa 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/Order.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/Order.cs @@ -20,6 +20,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel this.ShippingAddress = shippingAddress; this.BillingAddress = billingAddress; this.OrderDate = orderDate; + + this.Status = OrderStatus.New; } //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 - //List _orderItems; - //public virtual List orderItems - //{ - // get - // { - // if (_orderItems == null) - // _orderItems = new List(); + List _orderItems; + public virtual List OrderItems + { + get + { + if (_orderItems == null) + _orderItems = new List(); - // 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; } @@ -54,5 +66,47 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel 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; + } } } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/OrderItem.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/OrderItem.cs index 6290c1e81..aec28b4c2 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/OrderItem.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/OrderItem.cs @@ -7,29 +7,25 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel { 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 decimal Discount { get; set; } + public int FulfillmentRemaining { get; set; } 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); } } } diff --git a/src/Services/Ordering/Ordering.SqlData/Queries/OrderingQueries.cs b/src/Services/Ordering/Ordering.SqlData/Queries/OrderingQueries.cs index de3a44f56..5504f4b20 100644 --- a/src/Services/Ordering/Ordering.SqlData/Queries/OrderingQueries.cs +++ b/src/Services/Ordering/Ordering.SqlData/Queries/OrderingQueries.cs @@ -24,7 +24,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries var orders = await _dbContext.Orders .Include(o => o.ShippingAddress) .Include(o => o.BillingAddress) - //.Include(o => o.Items) + .Include(o => o.OrderItems) .ToListAsync(); // 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 { id = o.Id, + orderNumber = o.OrderNumber, + buyerId = o.BuyerId, orderDate = o.OrderDate, + status = o.Status, shippingAddress = o.ShippingAddress, 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; } + + public async Task 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(); + + // 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; + } } } diff --git a/src/Services/Ordering/Ordering.SqlData/UnitOfWork/DBContextUtil.cs b/src/Services/Ordering/Ordering.SqlData/UnitOfWork/DBContextUtil.cs new file mode 100644 index 000000000..bb726c59b --- /dev/null +++ b/src/Services/Ordering/Ordering.SqlData/UnitOfWork/DBContextUtil.cs @@ -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 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(); + builder.UseInMemoryDatabase() + .UseInternalServiceProvider(serviceProvider); + + return builder.Options; + } + + public static DbContextOptions CreateNewContextOptionsForSqlDB() + { + // Create a new options instance telling the context to use a Sql database + var builder = new DbContextOptionsBuilder(); + + //SQL LOCALDB + builder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;"); + + return builder.Options; + } + } +} diff --git a/src/Services/Ordering/Ordering.SqlData/UnitOfWork/OrderingDbContext.cs b/src/Services/Ordering/Ordering.SqlData/UnitOfWork/OrderingDbContext.cs index 8cfb231a6..63142ee91 100644 --- a/src/Services/Ordering/Ordering.SqlData/UnitOfWork/OrderingDbContext.cs +++ b/src/Services/Ordering/Ordering.SqlData/UnitOfWork/OrderingDbContext.cs @@ -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("OrderSequences", schema: "shared") + .StartsAt(1001) + .IncrementsBy(1); + + modelBuilder.Entity() + .Property(o => o.SequenceNumber) + .HasDefaultValueSql("NEXT VALUE FOR shared.OrderSequences"); + + } } } diff --git a/src/Services/Ordering/Ordering.SqlData/project.json b/src/Services/Ordering/Ordering.SqlData/project.json index fd7c6dee5..8ede54bb4 100644 --- a/src/Services/Ordering/Ordering.SqlData/project.json +++ b/src/Services/Ordering/Ordering.SqlData/project.json @@ -5,12 +5,15 @@ "Microsoft.EntityFrameworkCore": "1.0.0", "Microsoft.EntityFrameworkCore.SqlServer": "1.0.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": { "netstandard1.6": { - "imports": "dnxcore50" + "imports": [ "dnxcore50", "portable-net451+win8" ] } + } } diff --git a/test/Services/Ordering.Test/DataIntegrationTests.cs b/test/Services/Ordering.Test/DataIntegrationTests.cs index b2edcf2d4..160a7ed7b 100644 --- a/test/Services/Ordering.Test/DataIntegrationTests.cs +++ b/test/Services/Ordering.Test/DataIntegrationTests.cs @@ -6,6 +6,8 @@ using System.Threading.Tasks; using Xunit; using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork; 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.Extensions.DependencyInjection; @@ -15,49 +17,21 @@ namespace DataIntegrationTests // http://ef.readthedocs.io/en/latest/miscellaneous/testing.html public class Tests { - private static DbContextOptions 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(); - builder.UseInMemoryDatabase() - .UseInternalServiceProvider(serviceProvider); - - return builder.Options; - } - - private static DbContextOptions CreateNewContextOptionsForSqlDB() - { - // Create a new options instance telling the context to use a Sql database - var builder = new DbContextOptionsBuilder(); - - //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() + public void Add_order_to_data_model() { // All contexts that share the same service provider will share the same database //Using InMemory DB - var options = CreateNewContextOptionsForInMemoryDB(); + //var options = DbContextUtil.CreateNewContextOptionsForInMemoryDB(); //Using Sql LocalDB - //var options = CreateNewContextOptionsForSqlDB(); - + var options = DbContextUtil.CreateNewContextOptionsForSqlDB(); // Run the test against one instance of the context using (var context = new OrderingDbContext(options)) { + IOrderRepository orderRepository = new OrderRepository(context); //Create generic Address ValueObject Address sampleAddress = new Address("15703 NE 61st Ct.", @@ -72,10 +46,20 @@ namespace DataIntegrationTests ); //Create sample Orders 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 @@ -87,7 +71,7 @@ namespace DataIntegrationTests .ToList(); //Could be using .Load() if you don't want to create a List - //SAMPLE + //OTHER SAMPLE //var company = context.Companies // .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Car) // .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Country)