diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index 264a8d6b5..7dba59ba1 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -47,7 +47,7 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "UnitTest", "test\Services\U EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Identity", "Identity", "{24CD3B53-141E-4A07-9B0D-796641E1CF78}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Identity.API", "src\Services\Identity\eShopOnContainers.Identity\Identity.API.xproj", "{A579E108-5445-403D-A407-339AC4D1611B}" +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Identity.API", "src\Services\Identity\Identity.API\Identity.API.xproj", "{A579E108-5445-403D-A407-339AC4D1611B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Services/Basket/Basket.API/Controllers/BasketController.cs b/src/Services/Basket/Basket.API/Controllers/BasketController.cs index e613774b1..548eb2bbf 100644 --- a/src/Services/Basket/Basket.API/Controllers/BasketController.cs +++ b/src/Services/Basket/Basket.API/Controllers/BasketController.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Authorization; namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers { - //NOTE: Right now this is a very chunky API, as the app evolves it is possible we would + //TODO NOTE: Right now this is a very chunky API, as the app evolves it is possible we would //want to make the actions more fine graned, add basket item as an action for example. //If this is the case we should also investigate changing the serialization format used for Redis, //using a HashSet instead of a simple string. diff --git a/src/Services/Basket/Basket.API/Dockerfile b/src/Services/Basket/Basket.API/Dockerfile index 90c726b0e..bd3bad87b 100644 --- a/src/Services/Basket/Basket.API/Dockerfile +++ b/src/Services/Basket/Basket.API/Dockerfile @@ -1,4 +1,4 @@ -FROM microsoft/aspnetcore:1.0.1 +FROM microsoft/aspnetcore:1.1 ENTRYPOINT ["dotnet", "Basket.API.dll"] ARG source=. WORKDIR /app diff --git a/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs b/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs index 5363bf352..19d8e7055 100644 --- a/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs +++ b/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs @@ -18,7 +18,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model private ConnectionMultiplexer _redis; - public RedisBasketRepository(IOptions options, ILoggerFactory loggerFactory) + public RedisBasketRepository(IOptionsSnapshot options, ILoggerFactory loggerFactory) { _settings = options.Value; _logger = loggerFactory.CreateLogger(); diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index c9b73b556..35ac63f1e 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -44,7 +44,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API //that cost to startup instead of having the first request pay the //penalty. services.AddSingleton((sp) => { - var config = sp.GetRequiredService>().Value; + var config = sp.GetRequiredService>().Value; var ips = Dns.GetHostAddressesAsync(config.ConnectionString).Result; return ConnectionMultiplexer.Connect(ips.First().ToString()); }); diff --git a/src/Services/Basket/Basket.API/project.json b/src/Services/Basket/Basket.API/project.json index bff91d429..4ca72f510 100644 --- a/src/Services/Basket/Basket.API/project.json +++ b/src/Services/Basket/Basket.API/project.json @@ -1,20 +1,20 @@ { "dependencies": { "Microsoft.NETCore.App": { - "version": "1.0.0", + "version": "1.1.0", "type": "platform" }, - "System.Threading": "4.0.11", - "Microsoft.AspNetCore.Mvc": "1.0.0", - "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", - "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", - "Microsoft.Extensions.Configuration.Json": "1.0.0", - "Microsoft.Extensions.Logging": "1.0.0", - "Microsoft.Extensions.Logging.Console": "1.0.0", - "Microsoft.Extensions.Logging.Debug": "1.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", + "System.Threading": "4.3.0", + "Microsoft.AspNetCore.Mvc": "1.1.0", + "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0", + "Microsoft.AspNetCore.Server.Kestrel": "1.1.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0", + "Microsoft.Extensions.Configuration.FileExtensions": "1.1.0", + "Microsoft.Extensions.Configuration.Json": "1.1.0", + "Microsoft.Extensions.Logging": "1.1.0", + "Microsoft.Extensions.Logging.Console": "1.1.0", + "Microsoft.Extensions.Logging.Debug": "1.1.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0", "StackExchange.Redis": "1.1.608", "Newtonsoft.Json": "9.0.1", "IdentityServer4.AccessTokenValidation": "1.0.1-rc3", @@ -24,10 +24,11 @@ "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final" }, "frameworks": { - "netcoreapp1.0": { + "netcoreapp1.1": { "imports": [ - "dotnet5.6", - "portable-net45+win8" + "netstandard1.6.1", + "dnxcore50", + "portable-net451+win8" ] } }, diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index d2fc39d95..0d8ccc88a 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -1,27 +1,27 @@ - +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; +using Microsoft.eShopOnContainers.Services.Catalog.API.Model; +using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel; +using Microsoft.Extensions.Options; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers { - using Extensions.Options; - using Microsoft.AspNetCore.Mvc; - using Microsoft.EntityFrameworkCore; - using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; - using Model; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - using ViewModel; - [Route("api/v1/[controller]")] public class CatalogController : ControllerBase { private readonly CatalogContext _context; - private readonly IOptions _settings; + private readonly IOptionsSnapshot _settings; - public CatalogController(CatalogContext context, IOptions settings) + public CatalogController(CatalogContext context, IOptionsSnapshot settings) { _context = context; _settings = settings; + + ((DbContext)context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } // GET api/v1/[controller]/items/[?pageSize=3&pageIndex=10] @@ -30,7 +30,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers public async Task Items(int pageSize = 10, int pageIndex = 0) { var totalItems = await _context.CatalogItems - .LongCountAsync(); + .LongCountAsync(); var itemsOnPage = await _context.CatalogItems .OrderBy(c=>c.Name) diff --git a/src/Services/Catalog/Catalog.API/Dockerfile b/src/Services/Catalog/Catalog.API/Dockerfile index 8bbc926f9..b2148694a 100644 --- a/src/Services/Catalog/Catalog.API/Dockerfile +++ b/src/Services/Catalog/Catalog.API/Dockerfile @@ -1,4 +1,4 @@ -FROM microsoft/aspnetcore:1.0.1 +FROM microsoft/aspnetcore:1.1 WORKDIR /app EXPOSE 80 COPY . /app diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 70369056e..38bea3ee3 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -38,8 +38,6 @@ public void ConfigureServices(IServiceCollection services) { - services.AddSingleton(Configuration); - services.AddDbContext(c => { c.UseSqlServer(Configuration["ConnectionString"]); diff --git a/src/Services/Catalog/Catalog.API/project.json b/src/Services/Catalog/Catalog.API/project.json index 6705792b9..67d3b95cd 100644 --- a/src/Services/Catalog/Catalog.API/project.json +++ b/src/Services/Catalog/Catalog.API/project.json @@ -1,49 +1,41 @@ { + "buildOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true, + "debugType": "portable" + }, "dependencies": { "Microsoft.NETCore.App": { - "version": "1.0.1", + "version": "1.1.0", "type": "platform" }, - "Microsoft.AspNetCore.Mvc": "1.0.1", - "Microsoft.AspNetCore.Diagnostics": "1.0.0", - "Microsoft.AspNetCore.Diagnostics.Abstractions": "1.0.0", - "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", - "Microsoft.AspNetCore.Server.Kestrel": "1.0.1", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", - "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0", - "Microsoft.Extensions.Configuration.Json": "1.0.0", - "Microsoft.Extensions.Logging": "1.0.0", - "Microsoft.Extensions.Logging.Console": "1.0.0", - "Microsoft.Extensions.Logging.Debug": "1.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", - "Microsoft.EntityFrameworkCore": "1.0.1", - "Microsoft.EntityFrameworkCore.Relational": "1.0.1", - "Microsoft.EntityFrameworkCore.SqlServer": "1.0.1", - "Microsoft.EntityFrameworkCore.Design": "1.0.0-preview2-final", + "Microsoft.AspNetCore.Mvc": "1.1.0", + "Microsoft.AspNetCore.Diagnostics": "1.1.0", + "Microsoft.AspNetCore.Diagnostics.Abstractions": "1.1.0", + "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0", + "Microsoft.AspNetCore.Server.Kestrel": "1.1.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0", + "Microsoft.Extensions.Configuration.FileExtensions": "1.1.0", + "Microsoft.Extensions.Configuration.UserSecrets": "1.1.0", + "Microsoft.Extensions.Configuration.Json": "1.1.0", + "Microsoft.Extensions.Logging": "1.1.0", + "Microsoft.Extensions.Logging.Console": "1.1.0", + "Microsoft.Extensions.Logging.Debug": "1.1.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0", + "Microsoft.EntityFrameworkCore": "1.1.0", + "Microsoft.EntityFrameworkCore.Relational": "1.1.0", + "Microsoft.EntityFrameworkCore.SqlServer": "1.1.0", + "Microsoft.EntityFrameworkCore.Design": "1.1.0", "Swashbuckle": "6.0.0-beta902" }, - "tools": { - "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final" - }, "frameworks": { - "netcoreapp1.0": { + "netcoreapp1.1": { "imports": [ "dotnet5.6", "portable-net45+win8" ] } }, - "buildOptions": { - "emitEntryPoint": true, - "preserveCompilationContext": true, - "debugType": "portable" - }, - "runtimeOptions": { - "configProperties": { - "System.GC.Server": true - } - }, "publishOptions": { "include": [ "wwwroot", @@ -56,6 +48,14 @@ "Dockerfile" ] }, + "runtimeOptions": { + "configProperties": { + "System.GC.Server": true + } + }, "scripts": {}, + "tools": { + "Microsoft.EntityFrameworkCore.Tools": "1.1.0-preview4-final" + }, "userSecretsId": "aspnet-Catalog.API-20161122013618" } \ No newline at end of file diff --git a/src/Services/Identity/Identity.API/Controllers/HomeController.cs b/src/Services/Identity/Identity.API/Controllers/HomeController.cs index 9fbc8b190..f21ab65e0 100644 --- a/src/Services/Identity/Identity.API/Controllers/HomeController.cs +++ b/src/Services/Identity/Identity.API/Controllers/HomeController.cs @@ -15,10 +15,10 @@ namespace IdentityServer4.Quickstart.UI.Controllers public class HomeController : Controller { private readonly IIdentityServerInteractionService _interaction; - private readonly IOptions _settings; + private readonly IOptionsSnapshot _settings; private readonly IRedirectService _redirectSvc; - public HomeController(IIdentityServerInteractionService interaction, IOptions settings,IRedirectService redirectSvc) + public HomeController(IIdentityServerInteractionService interaction, IOptionsSnapshot settings,IRedirectService redirectSvc) { _interaction = interaction; _settings = settings; diff --git a/src/Services/Identity/Identity.API/Dockerfile b/src/Services/Identity/Identity.API/Dockerfile index 38dda0129..5eb4acdb6 100644 --- a/src/Services/Identity/Identity.API/Dockerfile +++ b/src/Services/Identity/Identity.API/Dockerfile @@ -1,4 +1,4 @@ -FROM microsoft/aspnetcore:1.0.1 +FROM microsoft/aspnetcore:1.1 ENTRYPOINT ["dotnet", "Identity.API.dll"] ARG source=. WORKDIR /app diff --git a/src/Services/Identity/Identity.API/project.json b/src/Services/Identity/Identity.API/project.json index a63963893..fa9795162 100644 --- a/src/Services/Identity/Identity.API/project.json +++ b/src/Services/Identity/Identity.API/project.json @@ -3,59 +3,55 @@ "dependencies": { "Microsoft.NETCore.App": { - "version": "1.0.1", + "version": "1.1.0", "type": "platform" }, - "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0", - "Microsoft.AspNetCore.Diagnostics": "1.0.0", - "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0", - "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0", - "Microsoft.AspNetCore.Mvc": "1.0.1", - "Microsoft.AspNetCore.Razor.Tools": { - "version": "1.0.0-preview2-final", - "type": "build" - }, - "Microsoft.AspNetCore.Routing": "1.0.1", - "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", - "Microsoft.AspNetCore.Server.Kestrel": "1.0.1", - "Microsoft.AspNetCore.StaticFiles": "1.0.0", - "Microsoft.EntityFrameworkCore.SqlServer": "1.0.1", + "Microsoft.AspNetCore.Authentication.Cookies": "1.1.0", + "Microsoft.AspNetCore.Diagnostics": "1.1.0", + "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.1.0", + "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.1.0", + "Microsoft.AspNetCore.Mvc": "1.1.0", + "Microsoft.AspNetCore.Routing": "1.1.0", + "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0", + "Microsoft.AspNetCore.Server.Kestrel": "1.1.0", + "Microsoft.AspNetCore.StaticFiles": "1.1.0", + "Microsoft.EntityFrameworkCore.SqlServer": "1.1.0", "Microsoft.EntityFrameworkCore.SqlServer.Design": { - "version": "1.0.1", + "version": "1.1.0", "type": "build" }, "Microsoft.EntityFrameworkCore.Tools": { - "version": "1.0.0-preview2-final", + "version": "1.1.0-preview4-final", "type": "build" }, - "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", - "Microsoft.Extensions.Configuration.Json": "1.0.0", - "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0", - "Microsoft.Extensions.Logging": "1.0.0", - "Microsoft.Extensions.Logging.Console": "1.0.0", - "Microsoft.Extensions.Logging.Debug": "1.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", - "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0", + "Microsoft.Extensions.Configuration.Json": "1.1.0", + "Microsoft.Extensions.Configuration.UserSecrets": "1.1.0", + "Microsoft.Extensions.Logging": "1.1.0", + "Microsoft.Extensions.Logging.Console": "1.1.0", + "Microsoft.Extensions.Logging.Debug": "1.1.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0", + "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.1.0", "Microsoft.VisualStudio.Web.CodeGeneration.Tools": { - "version": "1.0.0-preview2-final", + "version": "1.1.0-preview4-final", "type": "build" }, "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": { - "version": "1.0.0-preview2-final", + "version": "1.1.0-preview4-final", "type": "build" }, "IdentityServer4.AspNetIdentity": "1.0.0-rc3", - "IdentityServer4.EntityFramework": "1.0.0-rc3" + "IdentityServer4.EntityFramework": "1.0.0-rc3", }, "tools": { "BundlerMinifier.Core": "2.0.238", - "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final", - "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final", - "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final", - "Microsoft.Extensions.SecretManager.Tools": "1.0.0-preview2-final", + "Microsoft.AspNetCore.Razor.Tools": "1.1.0-preview4-final", + "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.1.0-preview4-final", + "Microsoft.EntityFrameworkCore.Tools": "1.1.0-preview4-final", + "Microsoft.Extensions.SecretManager.Tools": "1.1.0-preview4-final", "Microsoft.VisualStudio.Web.CodeGeneration.Tools": { - "version": "1.0.0-preview2-final", + "version": "1.1.0-preview4-final", "imports": [ "portable-net45+win8" ] diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs index cb112bbd9..21f016a0f 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs @@ -31,7 +31,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands public int CardTypeId { get; set; } - public string BuyerFullName { get; set; } + public string BuyerIdentityGuid { get; set; } public IEnumerable OrderItems => _orderItems; diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs index 483ab9be9..3e721cadc 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs @@ -12,6 +12,7 @@ private readonly IBuyerRepository _buyerRepository; private readonly IOrderRepository _orderRepository; + // Using DI to inject infrastructure persistence Repositories public CreateOrderCommandHandler(IBuyerRepository buyerRepository, IOrderRepository orderRepository) { if (buyerRepository == null) @@ -30,13 +31,16 @@ public async Task Handle(CreateOrderCommand message) { - //find buyer/payment or add a new one buyer/payment + // Add/Update the Buyer AggregateRoot + // DDD patterns comment: Add child entities and value-objects through the Order Aggregate-Root + // methods and constructor so validations, invariants and business logic + // make sure that consistency is preserved across the whole aggregate - var buyer = await _buyerRepository.FindAsync(message.BuyerFullName); + var buyer = await _buyerRepository.FindAsync(message.BuyerIdentityGuid); if (buyer == null) { - buyer = new Buyer(message.BuyerFullName); + buyer = new Buyer(message.BuyerIdentityGuid); } var payment = buyer.AddPaymentMethod(message.CardTypeId, @@ -51,7 +55,10 @@ await _buyerRepository.UnitOfWork .SaveChangesAsync(); - //create order for buyer and payment method + // Create the Order AggregateRoot + // DDD patterns comment: Add child entities and value-objects through the Order Aggregate-Root + // methods and constructor so validations, invariants and business logic + // make sure that consistency is preserved across the whole aggregate var order = new Order(buyer.Id, payment.Id, new Address(message.Street, message.City, message.State, message.Country, message.ZipCode)); @@ -66,6 +73,7 @@ .SaveChangesAsync(); return result > 0; + } } } diff --git a/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs b/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs index 5d0540adb..043747826 100644 --- a/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs +++ b/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs @@ -13,9 +13,9 @@ { private string _connectionString = string.Empty; - public OrderQueries(IConfiguration configuration) + public OrderQueries(string constr) { - _connectionString = configuration["ConnectionString"]; + _connectionString = constr; } @@ -28,8 +28,9 @@ var result = await connection.QueryAsync( @"select o.[Id] as ordernumber,o.OrderDate as date, os.Name as status, oi.ProductName as productname, oi.Units as units, oi.UnitPrice as unitprice, oi.PictureUrl as pictureurl, - o.Street as street, o.City as city, o.Country as country, o.State as state, o.ZipCode as zipcode + a.Street as street, a.City as city, a.Country as country, a.State as state, a.ZipCode as zipcode FROM ordering.Orders o + INNER JOIN ordering.Address a ON o.AddressId = a.Id LEFT JOIN ordering.Orderitems oi ON o.Id = oi.orderid LEFT JOIN ordering.orderstatus os on o.OrderStatusId = os.Id WHERE o.Id=@id" diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs index 2226c95e4..ec2886e2c 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers { [Route("api/v1/[controller]")] - [Authorize] + //[Authorize] public class OrdersController : Controller { private readonly IMediator _mediator; @@ -42,17 +42,17 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers [Route("new")] [HttpPost] - public async Task AddOrder([FromBody]CreateOrderCommand createOrderCommand) + public async Task CreateOrder([FromBody]CreateOrderCommand createOrderCommand) { if (createOrderCommand.CardTypeId == 0) { createOrderCommand.CardTypeId = 1; } - createOrderCommand.BuyerFullName = _identityService.GetUserIdentity(); + createOrderCommand.BuyerIdentityGuid = _identityService.GetUserIdentity(); - var added = await _mediator.SendAsync(createOrderCommand); - if (added) + var result = await _mediator.SendAsync(createOrderCommand); + if (result) { return Ok(); } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs index 0f56720f2..b8bc98442 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs @@ -10,9 +10,19 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof public class ApplicationModule :Autofac.Module { + + public string QueriesConnectionString { get; } + + public ApplicationModule(string qconstr) + { + QueriesConnectionString = qconstr; + + } + protected override void Load(ContainerBuilder builder) { - builder.RegisterType() + + builder.Register(c => new OrderQueries(QueriesConnectionString)) .As() .InstancePerLifetimeScope(); diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170127103457_Initial.Designer.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170208181933_Initial.Designer.cs similarity index 88% rename from src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170127103457_Initial.Designer.cs rename to src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170208181933_Initial.Designer.cs index cf7e5fdfb..a6bd1fe1d 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170127103457_Initial.Designer.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170208181933_Initial.Designer.cs @@ -8,7 +8,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; namespace Ordering.API.Migrations { [DbContext(typeof(OrderingContext))] - [Migration("20170127103457_Initial")] + [Migration("20170208181933_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -29,13 +29,13 @@ namespace Ordering.API.Migrations .HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); - b.Property("FullName") + b.Property("IdentityGuid") .IsRequired() .HasMaxLength(200); b.HasKey("Id"); - b.HasIndex("FullName") + b.HasIndex("IdentityGuid") .IsUnique(); b.ToTable("buyers","ordering"); @@ -90,6 +90,26 @@ namespace Ordering.API.Migrations b.ToTable("paymentmethods","ordering"); }); + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("City"); + + b.Property("Country"); + + b.Property("State"); + + b.Property("Street"); + + b.Property("ZipCode"); + + b.HasKey("Id"); + + b.ToTable("address","ordering"); + }); + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b => { b.Property("Id") @@ -98,13 +118,9 @@ namespace Ordering.API.Migrations .HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); - b.Property("BuyerId"); - - b.Property("City") - .IsRequired(); + b.Property("AddressId"); - b.Property("Country") - .IsRequired(); + b.Property("BuyerId"); b.Property("OrderDate"); @@ -112,17 +128,10 @@ namespace Ordering.API.Migrations b.Property("PaymentMethodId"); - b.Property("State") - .IsRequired(); - - b.Property("Street") - .IsRequired(); - - b.Property("ZipCode") - .IsRequired(); - b.HasKey("Id"); + b.HasIndex("AddressId"); + b.HasIndex("BuyerId"); b.HasIndex("OrderStatusId"); @@ -190,6 +199,10 @@ namespace Ordering.API.Migrations modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b => { + b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Address", "Address") + .WithMany() + .HasForeignKey("AddressId"); + b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer", "Buyer") .WithMany() .HasForeignKey("BuyerId") diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170127103457_Initial.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170208181933_Initial.cs similarity index 84% rename from src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170127103457_Initial.cs rename to src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170208181933_Initial.cs index 86a9403c4..5346cef60 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170127103457_Initial.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170208181933_Initial.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Metadata; namespace Ordering.API.Migrations { @@ -36,7 +37,7 @@ namespace Ordering.API.Migrations columns: table => new { Id = table.Column(nullable: false), - FullName = table.Column(maxLength: 200, nullable: false) + IdentityGuid = table.Column(maxLength: 200, nullable: false) }, constraints: table => { @@ -56,6 +57,24 @@ namespace Ordering.API.Migrations table.PrimaryKey("PK_cardtypes", x => x.Id); }); + migrationBuilder.CreateTable( + name: "address", + schema: "ordering", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), + City = table.Column(nullable: true), + Country = table.Column(nullable: true), + State = table.Column(nullable: true), + Street = table.Column(nullable: true), + ZipCode = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_address", x => x.Id); + }); + migrationBuilder.CreateTable( name: "orderstatus", schema: "ordering", @@ -107,19 +126,22 @@ namespace Ordering.API.Migrations columns: table => new { Id = table.Column(nullable: false), + AddressId = table.Column(nullable: true), BuyerId = table.Column(nullable: false), - City = table.Column(nullable: false), - Country = table.Column(nullable: false), OrderDate = table.Column(nullable: false), OrderStatusId = table.Column(nullable: false), - PaymentMethodId = table.Column(nullable: false), - State = table.Column(nullable: false), - Street = table.Column(nullable: false), - ZipCode = table.Column(nullable: false) + PaymentMethodId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_orders", x => x.Id); + table.ForeignKey( + name: "FK_orders_address_AddressId", + column: x => x.AddressId, + principalSchema: "ordering", + principalTable: "address", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); table.ForeignKey( name: "FK_orders_buyers_BuyerId", column: x => x.BuyerId, @@ -170,10 +192,10 @@ namespace Ordering.API.Migrations }); migrationBuilder.CreateIndex( - name: "IX_buyers_FullName", + name: "IX_buyers_IdentityGuid", schema: "ordering", table: "buyers", - column: "FullName", + column: "IdentityGuid", unique: true); migrationBuilder.CreateIndex( @@ -188,6 +210,12 @@ namespace Ordering.API.Migrations table: "paymentmethods", column: "CardTypeId"); + migrationBuilder.CreateIndex( + name: "IX_orders_AddressId", + schema: "ordering", + table: "orders", + column: "AddressId"); + migrationBuilder.CreateIndex( name: "IX_orders_BuyerId", schema: "ordering", @@ -223,6 +251,10 @@ namespace Ordering.API.Migrations name: "orders", schema: "ordering"); + migrationBuilder.DropTable( + name: "address", + schema: "ordering"); + migrationBuilder.DropTable( name: "orderstatus", schema: "ordering"); diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/OrderingContextModelSnapshot.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/OrderingContextModelSnapshot.cs index 81402440a..5c7b6d25b 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/OrderingContextModelSnapshot.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/OrderingContextModelSnapshot.cs @@ -28,13 +28,13 @@ namespace Ordering.API.Migrations .HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); - b.Property("FullName") + b.Property("IdentityGuid") .IsRequired() .HasMaxLength(200); b.HasKey("Id"); - b.HasIndex("FullName") + b.HasIndex("IdentityGuid") .IsUnique(); b.ToTable("buyers","ordering"); @@ -89,6 +89,26 @@ namespace Ordering.API.Migrations b.ToTable("paymentmethods","ordering"); }); + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("City"); + + b.Property("Country"); + + b.Property("State"); + + b.Property("Street"); + + b.Property("ZipCode"); + + b.HasKey("Id"); + + b.ToTable("address","ordering"); + }); + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b => { b.Property("Id") @@ -97,13 +117,9 @@ namespace Ordering.API.Migrations .HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); - b.Property("BuyerId"); - - b.Property("City") - .IsRequired(); + b.Property("AddressId"); - b.Property("Country") - .IsRequired(); + b.Property("BuyerId"); b.Property("OrderDate"); @@ -111,17 +127,10 @@ namespace Ordering.API.Migrations b.Property("PaymentMethodId"); - b.Property("State") - .IsRequired(); - - b.Property("Street") - .IsRequired(); - - b.Property("ZipCode") - .IsRequired(); - b.HasKey("Id"); + b.HasIndex("AddressId"); + b.HasIndex("BuyerId"); b.HasIndex("OrderStatusId"); @@ -189,6 +198,10 @@ namespace Ordering.API.Migrations modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b => { + b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Address", "Address") + .WithMany() + .HasForeignKey("AddressId"); + b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer", "Buyer") .WithMany() .HasForeignKey("BuyerId") diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index fe7046b5a..b0e4a67f2 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -81,7 +81,6 @@ // Add application services. services.AddSingleton(); - services.AddSingleton(this.Configuration); services.AddTransient(); services.AddOptions(); @@ -92,7 +91,7 @@ container.Populate(services); container.RegisterModule(new MediatorModule()); - container.RegisterModule(new ApplicationModule()); + container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"] )); return new AutofacServiceProvider(container.Build()); } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs index ce09978c2..f0aba8b9c 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs @@ -8,11 +8,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.B public class Buyer : Entity, IAggregateRoot { - public string FullName { get; private set; } + public string IdentityGuid { get; private set; } - private HashSet _paymentMethods; + private List _paymentMethods; - public IEnumerable PaymentMethods => _paymentMethods?.ToList().AsEnumerable(); + public IEnumerable PaymentMethods => _paymentMethods?.AsReadOnly(); protected Buyer() { } @@ -23,9 +23,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.B throw new ArgumentNullException(nameof(identity)); } - FullName = identity; + IdentityGuid = identity; - _paymentMethods = new HashSet(); + _paymentMethods = new List(); } public PaymentMethod AddPaymentMethod(int cardTypeId, string alias, string cardNumber, string securityNumber, string cardHolderName, DateTime expiration) diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/IBuyerRepository.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/IBuyerRepository.cs index 6436a0aa4..ccd0efe39 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/IBuyerRepository.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/IBuyerRepository.cs @@ -7,7 +7,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.B //This is just the RepositoryContracts or Interface defined at the Domain Layer //as requisite for the Buyer Aggregate public interface IBuyerRepository - :IRepository + :IAggregateRepository { Buyer Add(Buyer buyer); diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Address.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Address.cs index b3760480c..961d83d3a 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Address.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Address.cs @@ -1,18 +1,21 @@ -using System; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; +using System; +using System.Collections.Generic; namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate { public class Address + :ValueObject { - public String Street { get; } + public String Street { get; private set; } - public String City { get; } + public String City { get; private set; } - public String State { get; } + public String State { get; private set; } - public String Country { get; } + public String Country { get; private set; } - public String ZipCode { get; } + public String ZipCode { get; private set; } public Address(string street, string city, string state, string country, string zipcode) { @@ -22,5 +25,14 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O Country = country; ZipCode = zipcode; } + + protected override IEnumerable GetAtomicValues() + { + yield return Street; + yield return City; + yield return State; + yield return Country; + yield return ZipCode; + } } } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs index 9db371698..c16caa4a3 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs @@ -5,7 +5,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O //This is just the RepositoryContracts or Interface defined at the Domain Layer //as requisite for the Order Aggregate public interface IOrderRepository - :IRepository + :IAggregateRepository { Order Add(Order order); } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs index 692ce59cd..3c856f09a 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs @@ -9,44 +9,51 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O public class Order : Entity { - private string _street; - private string _city; - private string _state; - private string _country; - private string _zipCode; + // DDD Patterns comment + // Using private fields, allowed since EF Core 1.1, is a much better encapsulation + // aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections) private DateTime _orderDate; + public Address Address { get; private set; } + public Buyer Buyer { get; private set; } - int _buyerId; + private int _buyerId; public OrderStatus OrderStatus { get; private set; } - int _orderStatusId; + private int _orderStatusId; + + // DDD Patterns comment + // Using a private collection field, better for DDD Aggregate's encapsulation + // so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection, + // but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour. + private readonly List _orderItems; - HashSet _orderItems; - public IEnumerable OrderItems => _orderItems.ToList().AsEnumerable(); + public IEnumerable OrderItems => _orderItems.AsReadOnly(); + // Using List<>.AsReadOnly() + // This will create a read only wrapper around the private list so is protected against "external updates". + // It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance) + //https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx public PaymentMethod PaymentMethod { get; private set; } - int _paymentMethodId; + private int _paymentMethodId; protected Order() { } public Order(int buyerId, int paymentMethodId, Address address) { - + _orderItems = new List(); _buyerId = buyerId; _paymentMethodId = paymentMethodId; _orderStatusId = OrderStatus.InProcess.Id; _orderDate = DateTime.UtcNow; - _street = address.Street; - _city = address.City; - _state = address.State; - _country = address.Country; - _zipCode = address.ZipCode; - _orderItems = new HashSet(); + Address = address; } - + // DDD Patterns comment + // This Order AggregateRoot's method "AddOrderitem()" should be the only way to add Items to the Order, + // so any behavior (discounts, etc.) and validations are controlled by the AggregateRoot + // in order to maintain consistency between the whole Aggregate. public void AddOrderItem(int productId, string productName, decimal unitPrice, decimal discount, string pictureUrl, int units = 1) { var existingOrderForProduct = _orderItems.Where(o => o.ProductId == productId) diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs index 0b5fe1f72..22c7cc93c 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs @@ -6,16 +6,18 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O public class OrderItem : Entity { - private string _productName; - private string _pictureUrl; - private int _orderId; + // DDD Patterns comment + // Using private fields, allowed since EF Core 1.1, is a much better encapsulation + // aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections) + private string _productName; + private string _pictureUrl; + private int _orderId; private decimal _unitPrice; private decimal _discount; - private int _units; + private int _units; public int ProductId { get; private set; } - protected OrderItem() { } public OrderItem(int productId, string productName, decimal unitPrice, decimal discount, string PictureUrl, int units = 1) diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/IRepository.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/IAggregateRepository.cs similarity index 75% rename from src/Services/Ordering/Ordering.Domain/SeedWork/IRepository.cs rename to src/Services/Ordering/Ordering.Domain/SeedWork/IAggregateRepository.cs index f5035f88b..c2983d290 100644 --- a/src/Services/Ordering/Ordering.Domain/SeedWork/IRepository.cs +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/IAggregateRepository.cs @@ -1,6 +1,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork { - public interface IRepository + public interface IAggregateRepository { IUnitOfWork UnitOfWork { get; } } diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs new file mode 100644 index 000000000..40e587683 --- /dev/null +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork +{ + public abstract class ValueObject + { + protected static bool EqualOperator(ValueObject left, ValueObject right) + { + if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null)) + { + return false; + } + return ReferenceEquals(left, null) || left.Equals(right); + } + + + protected static bool NotEqualOperator(ValueObject left, ValueObject right) + { + return !(EqualOperator(left, right)); + } + + + protected abstract IEnumerable GetAtomicValues(); + + + public override bool Equals(object obj) + { + if (obj == null || obj.GetType() != GetType()) + { + return false; + } + ValueObject other = (ValueObject)obj; + IEnumerator thisValues = GetAtomicValues().GetEnumerator(); + IEnumerator otherValues = other.GetAtomicValues().GetEnumerator(); + while (thisValues.MoveNext() && otherValues.MoveNext()) + { + if (ReferenceEquals(thisValues.Current, null) ^ ReferenceEquals(otherValues.Current, null)) + { + return false; + } + if (thisValues.Current != null && !thisValues.Current.Equals(otherValues.Current)) + { + return false; + } + } + return !thisValues.MoveNext() && !otherValues.MoveNext(); + } + + + public override int GetHashCode() + { + return GetAtomicValues() + .Select(x => x != null ? x.GetHashCode() : 0) + .Aggregate((x, y) => x ^ y); + } + + public ValueObject GetCopy() + { + return this.MemberwiseClone() as ValueObject; + } + } +} diff --git a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs index d2ed4efed..34815b524 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs @@ -30,6 +30,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.Entity
(ConfigureAddress); modelBuilder.Entity(ConfigureBuyer); modelBuilder.Entity(ConfigurePayment); modelBuilder.Entity(ConfigureOrder); @@ -38,6 +39,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure modelBuilder.Entity(ConfigureOrderStatus); } + void ConfigureAddress(EntityTypeBuilder
addressConfiguration) + { + addressConfiguration.ToTable("address", DEFAULT_SCHEMA); + + addressConfiguration.Property("Id") + .IsRequired(); + + addressConfiguration.HasKey("Id"); + } + void ConfigureBuyer(EntityTypeBuilder buyerConfiguration) { buyerConfiguration.ToTable("buyers", DEFAULT_SCHEMA); @@ -47,11 +58,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure buyerConfiguration.Property(b => b.Id) .ForSqlServerUseSequenceHiLo("buyerseq", DEFAULT_SCHEMA); - buyerConfiguration.Property(b=>b.FullName) + buyerConfiguration.Property(b=>b.IdentityGuid) .HasMaxLength(200) .IsRequired(); - buyerConfiguration.HasIndex("FullName") + buyerConfiguration.HasIndex("IdentityGuid") .IsUnique(true); buyerConfiguration.HasMany(b => b.PaymentMethods) @@ -109,17 +120,13 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure .ForSqlServerUseSequenceHiLo("orderseq", DEFAULT_SCHEMA); orderConfiguration.Property("OrderDate").IsRequired(); - orderConfiguration.Property("Street").IsRequired(); - orderConfiguration.Property("State").IsRequired(); - orderConfiguration.Property("City").IsRequired(); - orderConfiguration.Property("ZipCode").IsRequired(); - orderConfiguration.Property("Country").IsRequired(); orderConfiguration.Property("BuyerId").IsRequired(); orderConfiguration.Property("OrderStatusId").IsRequired(); orderConfiguration.Property("PaymentMethodId").IsRequired(); var navigation = orderConfiguration.Metadata.FindNavigation(nameof(Order.OrderItems)); - + // DDD Patterns comment: + //Set as Field (New since EF 1.1) to access the OrderItem collection property through its field navigation.SetPropertyAccessMode(PropertyAccessMode.Field); orderConfiguration.HasOne(o => o.PaymentMethod) diff --git a/src/Services/Ordering/Ordering.Infrastructure/Repositories/BuyerRepository.cs b/src/Services/Ordering/Ordering.Infrastructure/Repositories/BuyerRepository.cs index e7572af97..8a2856de8 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Repositories/BuyerRepository.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/Repositories/BuyerRepository.cs @@ -49,7 +49,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositor { var buyer = await _context.Buyers .Include(b => b.PaymentMethods) - .Where(b => b.FullName == identity) + .Where(b => b.IdentityGuid == identity) .SingleOrDefaultAsync(); return buyer; diff --git a/src/Web/WebMVC/Dockerfile b/src/Web/WebMVC/Dockerfile index 8e105bd5e..7ae55f27e 100644 --- a/src/Web/WebMVC/Dockerfile +++ b/src/Web/WebMVC/Dockerfile @@ -1,4 +1,4 @@ -FROM microsoft/aspnetcore:1.0.1 +FROM microsoft/aspnetcore:1.1 ENTRYPOINT ["dotnet", "WebMVC.dll"] ARG source=. WORKDIR /app diff --git a/src/Web/WebMVC/Services/BasketService.cs b/src/Web/WebMVC/Services/BasketService.cs index 1d5678ffe..6ddbf0485 100644 --- a/src/Web/WebMVC/Services/BasketService.cs +++ b/src/Web/WebMVC/Services/BasketService.cs @@ -14,12 +14,12 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services { public class BasketService : IBasketService { - private readonly IOptions _settings; + private readonly IOptionsSnapshot _settings; private HttpClient _apiClient; private readonly string _remoteServiceBaseUrl; private IHttpContextAccessor _httpContextAccesor; - public BasketService(IOptions settings, IHttpContextAccessor httpContextAccesor) + public BasketService(IOptionsSnapshot settings, IHttpContextAccessor httpContextAccesor) { _settings = settings; _remoteServiceBaseUrl = _settings.Value.BasketUrl; diff --git a/src/Web/WebMVC/Services/CatalogService.cs b/src/Web/WebMVC/Services/CatalogService.cs index 9885ed4bf..b830c9542 100644 --- a/src/Web/WebMVC/Services/CatalogService.cs +++ b/src/Web/WebMVC/Services/CatalogService.cs @@ -15,11 +15,11 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services { public class CatalogService : ICatalogService { - private readonly IOptions _settings; + private readonly IOptionsSnapshot _settings; private HttpClient _apiClient; private readonly string _remoteServiceBaseUrl; - public CatalogService(IOptions settings, ILoggerFactory loggerFactory) { + public CatalogService(IOptionsSnapshot settings, ILoggerFactory loggerFactory) { _settings = settings; _remoteServiceBaseUrl = $"{_settings.Value.CatalogUrl}/api/v1/catalog/"; diff --git a/src/Web/WebMVC/Services/OrderingService.cs b/src/Web/WebMVC/Services/OrderingService.cs index 098f5eb45..652ee18b0 100644 --- a/src/Web/WebMVC/Services/OrderingService.cs +++ b/src/Web/WebMVC/Services/OrderingService.cs @@ -15,10 +15,10 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services { private HttpClient _apiClient; private readonly string _remoteServiceBaseUrl; - private readonly IOptions _settings; + private readonly IOptionsSnapshot _settings; private readonly IHttpContextAccessor _httpContextAccesor; - public OrderingService(IOptions settings, IHttpContextAccessor httpContextAccesor) + public OrderingService(IOptionsSnapshot settings, IHttpContextAccessor httpContextAccesor) { _remoteServiceBaseUrl = $"{settings.Value.OrderingUrl}/api/v1/orders"; _settings = settings; diff --git a/src/Web/WebMVC/Views/Order/Index.cshtml b/src/Web/WebMVC/Views/Order/Index.cshtml index 347f0fd9d..9ca57df7c 100644 --- a/src/Web/WebMVC/Views/Order/Index.cshtml +++ b/src/Web/WebMVC/Views/Order/Index.cshtml @@ -25,7 +25,7 @@
@Html.DisplayFor(modelItem => item.Date)
$ @Html.DisplayFor(modelItem => item.Total)
@Html.DisplayFor(modelItem => item.Status)
-
+
Detail
diff --git a/src/Web/WebMVC/project.json b/src/Web/WebMVC/project.json index f248f234a..cc45e7095 100644 --- a/src/Web/WebMVC/project.json +++ b/src/Web/WebMVC/project.json @@ -2,27 +2,27 @@ "userSecretsId": "aspnet-Microsoft.eShopOnContainers-946ae052-8305-4a99-965b-ec8636ddbae3", "dependencies": { "Microsoft.NETCore.App": { - "version": "1.0.0", + "version": "1.1.0", "type": "platform" }, - "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0", - "Microsoft.AspNetCore.Diagnostics": "1.0.0", - "Microsoft.AspNetCore.Mvc": "1.0.0", + "Microsoft.AspNetCore.Authentication.Cookies": "1.1.0", + "Microsoft.AspNetCore.Diagnostics": "1.1.0", + "Microsoft.AspNetCore.Mvc": "1.1.1", "Microsoft.AspNetCore.Razor.Tools": { "version": "1.0.0-preview2-final", "type": "build" }, - "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", - "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", - "Microsoft.AspNetCore.StaticFiles": "1.0.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", - "Microsoft.Extensions.Configuration.Json": "1.0.0", - "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0", - "Microsoft.Extensions.Logging": "1.0.0", - "Microsoft.Extensions.Logging.Console": "1.0.0", - "Microsoft.Extensions.Logging.Debug": "1.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", - "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0", + "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0", + "Microsoft.AspNetCore.Server.Kestrel": "1.1.0", + "Microsoft.AspNetCore.StaticFiles": "1.1.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0", + "Microsoft.Extensions.Configuration.Json": "1.1.0", + "Microsoft.Extensions.Configuration.UserSecrets": "1.1.0", + "Microsoft.Extensions.Logging": "1.1.0", + "Microsoft.Extensions.Logging.Console": "1.1.0", + "Microsoft.Extensions.Logging.Debug": "1.1.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0", + "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.1.0", "Microsoft.VisualStudio.Web.CodeGeneration.Tools": { "version": "1.0.0-preview2-final", "type": "build" @@ -32,10 +32,11 @@ "type": "build" }, "Newtonsoft.Json": "9.0.1", - "System.IdentityModel.Tokens.Jwt": "5.0.0", - "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.0", - "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0", - "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0" + "System.IdentityModel.Tokens.Jwt": "5.1.0", + "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.1.0", + "Microsoft.AspNetCore.Authentication.JwtBearer": "1.1.0", + "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.1.0", + "Microsoft.Extensions.Options": "1.1.0" }, "tools": { "BundlerMinifier.Core": "2.0.238", diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html index 22b25182f..f6c547279 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html @@ -16,7 +16,7 @@
{{order.date | date:'short'}}
$ {{order.total}}
{{order.status}}
-
+
Detail
diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Dockerfile b/src/Web/WebSPA/eShopOnContainers.WebSPA/Dockerfile index c945aa42a..b55f7527c 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Dockerfile +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Dockerfile @@ -1,4 +1,4 @@ -FROM microsoft/aspnetcore:1.0.1 +FROM microsoft/aspnetcore:1.1 ENTRYPOINT ["dotnet", "eShopOnContainers.WebSPA.dll"] ARG source=. WORKDIR /app diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Server/Controllers/HomeController.cs b/src/Web/WebSPA/eShopOnContainers.WebSPA/Server/Controllers/HomeController.cs index ee980e334..7e78cd41a 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Server/Controllers/HomeController.cs +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Server/Controllers/HomeController.cs @@ -11,9 +11,9 @@ namespace eShopConContainers.WebSPA.Server.Controllers public class HomeController : Controller { private readonly IHostingEnvironment _env; - private readonly IOptions _settings; + private readonly IOptionsSnapshot _settings; - public HomeController(IHostingEnvironment env, IOptions settings) + public HomeController(IHostingEnvironment env, IOptionsSnapshot settings) { _env = env; _settings = settings; diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/project.json b/src/Web/WebSPA/eShopOnContainers.WebSPA/project.json index 57b813c07..1df90ea9a 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/project.json +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/project.json @@ -2,16 +2,16 @@ "userSecretsId": "aspnetcorespa-c23d27a4-eb88-4b18-9b77-2a93f3b15119", "dependencies": { "Microsoft.NETCore.App": { - "version": "1.0.0", + "version": "1.1.0", "type": "platform" }, - "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0", - "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0", - "Microsoft.AspNetCore.Diagnostics": "1.0.0", - "Microsoft.AspNetCore.Mvc": "1.0.1", - "Microsoft.AspNetCore.Cors": "1.0.0", - "Microsoft.AspNetCore.Antiforgery": "1.0.1", - "Microsoft.AspNetCore.Authorization": "1.0.0", + "Microsoft.Extensions.Configuration.UserSecrets": "1.1.0", + "Microsoft.AspNetCore.Authentication.Cookies": "1.1.0", + "Microsoft.AspNetCore.Diagnostics": "1.1.0", + "Microsoft.AspNetCore.Mvc": "1.1.1", + "Microsoft.AspNetCore.Cors": "1.1.0", + "Microsoft.AspNetCore.Antiforgery": "1.1.0", + "Microsoft.AspNetCore.Authorization": "1.1.0", "Newtonsoft.Json": "9.0.1", "Webpack": "3.0.0", "Microsoft.AspNetCore.AngularServices": "1.0.0-beta-000014", @@ -19,15 +19,15 @@ "version": "1.0.0-preview2-final", "type": "build" }, - "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", - "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", - "Microsoft.AspNetCore.StaticFiles": "1.0.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", - "Microsoft.Extensions.Configuration.Json": "1.0.0", - "Microsoft.Extensions.Logging": "1.0.0", - "Microsoft.Extensions.Logging.Console": "1.0.0", - "Microsoft.Extensions.Logging.Debug": "1.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", + "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0", + "Microsoft.AspNetCore.Server.Kestrel": "1.1.0", + "Microsoft.AspNetCore.StaticFiles": "1.1.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0", + "Microsoft.Extensions.Configuration.Json": "1.1.0", + "Microsoft.Extensions.Logging": "1.1.0", + "Microsoft.Extensions.Logging.Console": "1.1.0", + "Microsoft.Extensions.Logging.Debug": "1.1.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0", "Microsoft.VisualStudio.Web.CodeGeneration.Tools": { "version": "1.0.0-preview2-final", "type": "build" @@ -36,7 +36,8 @@ "version": "1.0.0-preview2-final", "type": "build" }, - "Microsoft.AspNetCore.Http.Abstractions": "1.0.0" + "Microsoft.AspNetCore.Http.Abstractions": "1.1.0", + "Microsoft.Extensions.Options": "1.1.0" }, "tools": { "Microsoft.DotNet.Watcher.Tools": { diff --git a/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs b/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs index af26cbe49..0d219d594 100644 --- a/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs +++ b/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs @@ -25,7 +25,7 @@ namespace UnitTest.Ordering.Application public async Task Handle_returns_true_when_order_is_persisted_succesfully() { // Arrange - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().BuyerFullName)) + _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().BuyerIdentityGuid)) .Returns(Task.FromResult(FakeBuyer())); _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) @@ -48,7 +48,7 @@ namespace UnitTest.Ordering.Application [Fact] public async Task Handle_return_false_if_order_is_not_persisted() { - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().BuyerFullName)) + _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().BuyerIdentityGuid)) .Returns(Task.FromResult(FakeBuyer())); _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) @@ -80,7 +80,7 @@ namespace UnitTest.Ordering.Application { return new CreateOrderCommand { - BuyerFullName = "1234", + BuyerIdentityGuid = "1234", CardNumber = "1234", CardExpiration = DateTime.Now.AddYears(1), CardSecurityNumber = "123",