Browse Source

Working Ordering microservice on Docker with Docker-compose deployment

pull/49/merge
Cesar De la Torre 8 years ago
parent
commit
28b4ccb317
54 changed files with 1733 additions and 728 deletions
  1. +10
    -0
      eShopOnContainers.sln
  2. +5
    -0
      src/Console/eShopConsole/.dockerignore
  3. +392
    -0
      src/Console/eShopConsole/DockerTask.ps1
  4. +10
    -0
      src/Console/eShopConsole/Dockerfile
  5. +20
    -0
      src/Console/eShopConsole/Dockerfile.debug
  6. +81
    -0
      src/Console/eShopConsole/Program.cs
  7. +19
    -0
      src/Console/eShopConsole/Properties/AssemblyInfo.cs
  8. +17
    -0
      src/Console/eShopConsole/Properties/Docker.props
  9. +75
    -0
      src/Console/eShopConsole/Properties/Docker.targets
  10. +8
    -0
      src/Console/eShopConsole/Properties/launchSettings.json
  11. +12
    -0
      src/Console/eShopConsole/docker-compose.debug.yml
  12. +8
    -0
      src/Console/eShopConsole/docker-compose.yml
  13. +22
    -0
      src/Console/eShopConsole/eShopConsole.xproj
  14. +31
    -0
      src/Console/eShopConsole/project.json
  15. +5
    -0
      src/Services/Ordering/Ordering.API/.dockerignore
  16. +23
    -0
      src/Services/Ordering/Ordering.API/Controllers/EnvironmentInfoController.cs
  17. +53
    -19
      src/Services/Ordering/Ordering.API/Controllers/OrderingController.cs
  18. +489
    -0
      src/Services/Ordering/Ordering.API/DockerTask.ps1
  19. +7
    -9
      src/Services/Ordering/Ordering.API/Dockerfile
  20. +24
    -0
      src/Services/Ordering/Ordering.API/Dockerfile.debug
  21. +0
    -37
      src/Services/Ordering/Ordering.API/Migrations/20160909202620_MyFirstMigration.Designer.cs
  22. +0
    -32
      src/Services/Ordering/Ordering.API/Migrations/20160909202620_MyFirstMigration.cs
  23. +0
    -84
      src/Services/Ordering/Ordering.API/Migrations/20160909223213_Migration2.Designer.cs
  24. +0
    -98
      src/Services/Ordering/Ordering.API/Migrations/20160909223213_Migration2.cs
  25. +0
    -84
      src/Services/Ordering/Ordering.API/Migrations/20160909233852_Migration3.Designer.cs
  26. +0
    -19
      src/Services/Ordering/Ordering.API/Migrations/20160909233852_Migration3.cs
  27. +0
    -89
      src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.Designer.cs
  28. +0
    -37
      src/Services/Ordering/Ordering.API/Migrations/20160913052800_Migration4.cs
  29. +0
    -46
      src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.cs
  30. +2
    -2
      src/Services/Ordering/Ordering.API/Migrations/20160913204939_Migration1.Designer.cs
  31. +123
    -0
      src/Services/Ordering/Ordering.API/Migrations/20160913204939_Migration1.cs
  32. +1
    -1
      src/Services/Ordering/Ordering.API/Migrations/OrderingDbContextModelSnapshot.cs
  33. +3
    -0
      src/Services/Ordering/Ordering.API/Ordering.API.xproj
  34. +17
    -0
      src/Services/Ordering/Ordering.API/Properties/Docker.props
  35. +75
    -0
      src/Services/Ordering/Ordering.API/Properties/Docker.targets
  36. +6
    -2
      src/Services/Ordering/Ordering.API/Properties/launchSettings.json
  37. +19
    -6
      src/Services/Ordering/Ordering.API/Startup.cs
  38. +14
    -0
      src/Services/Ordering/Ordering.API/docker-compose.debug.yml
  39. +10
    -0
      src/Services/Ordering/Ordering.API/docker-compose.yml
  40. +14
    -11
      src/Services/Ordering/Ordering.API/project.json
  41. +1
    -1
      src/Services/Ordering/Ordering.Domain/AggregatesModel/Buyer/Buyer.cs
  42. +1
    -1
      src/Services/Ordering/Ordering.Domain/AggregatesModel/Order/Order.cs
  43. +2
    -2
      src/Services/Ordering/Ordering.Domain/Contracts/IBuyerRepository.cs
  44. +8
    -6
      src/Services/Ordering/Ordering.Domain/Contracts/IOrderRepository.cs
  45. +6
    -0
      src/Services/Ordering/Ordering.Domain/SeedWork/IAggregateRoot.cs
  46. +3
    -25
      src/Services/Ordering/Ordering.Domain/SeedWork/IRepository.cs
  47. +2
    -3
      src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs
  48. +24
    -0
      src/Services/Ordering/Ordering.SqlData/Queries/IOrderdingQueries.cs
  49. +8
    -1
      src/Services/Ordering/Ordering.SqlData/Queries/OrderingQueries.cs
  50. +28
    -17
      src/Services/Ordering/Ordering.SqlData/Repositories/OrderRepository.cs
  51. +0
    -82
      src/Services/Ordering/Ordering.SqlData/SeedWork/Repository.cs
  52. +12
    -2
      src/Services/Ordering/Ordering.SqlData/UnitOfWork/DBContextUtil.cs
  53. +35
    -2
      src/Services/Ordering/Ordering.SqlData/UnitOfWork/OrderingDbContext.cs
  54. +8
    -10
      test/Services/Ordering.Test/DataIntegrationTests.cs

+ 10
- 0
eShopOnContainers.sln View File

@ -51,6 +51,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{EF
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ordering.Test", "test\Services\Ordering.Test\Ordering.Test.xproj", "{A0AFC432-3846-4B4E-BD8E-3C8C896F4967}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Console App", "Console App", "{48FC45C5-223F-4B59-AC77-6CBB1C561E85}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "eShopConsole", "src\Console\eShopConsole\eShopConsole.xproj", "{C10C7B69-CE4F-4167-928E-33B7FA1DFFC7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -101,6 +105,10 @@ Global
{A0AFC432-3846-4B4E-BD8E-3C8C896F4967}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0AFC432-3846-4B4E-BD8E-3C8C896F4967}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0AFC432-3846-4B4E-BD8E-3C8C896F4967}.Release|Any CPU.Build.0 = Release|Any CPU
{C10C7B69-CE4F-4167-928E-33B7FA1DFFC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C10C7B69-CE4F-4167-928E-33B7FA1DFFC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C10C7B69-CE4F-4167-928E-33B7FA1DFFC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C10C7B69-CE4F-4167-928E-33B7FA1DFFC7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -124,5 +132,7 @@ Global
{5DA6EF13-C7E0-4EC4-8599-A61C6AC2628D} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B}
{EF0337F2-ED00-4643-89FD-EE10863F1870} = {A857AD10-40FF-4303-BEC2-FF1C58D5735E}
{A0AFC432-3846-4B4E-BD8E-3C8C896F4967} = {EF0337F2-ED00-4643-89FD-EE10863F1870}
{48FC45C5-223F-4B59-AC77-6CBB1C561E85} = {932D8224-11F6-4D07-B109-DA28AD288A63}
{C10C7B69-CE4F-4167-928E-33B7FA1DFFC7} = {48FC45C5-223F-4B59-AC77-6CBB1C561E85}
EndGlobalSection
EndGlobal

+ 5
- 0
src/Console/eShopConsole/.dockerignore View File

@ -0,0 +1,5 @@
# Docker support files
docker-compose.yml
docker-compose.debug.yml
Dockerfile
Dockerfile.debug

+ 392
- 0
src/Console/eShopConsole/DockerTask.ps1 View File

@ -0,0 +1,392 @@
<#
.SYNOPSIS
Deploys an ASP .NET Core Web Application into a docker container running in a specified Docker machine.
.DESCRIPTION
The following script will execute a set of Docker commands against the designated dockermachine.
.PARAMETER Build
Builds the containers using docker-compose build.
.PARAMETER Clean
Clears out any running containers (docker-compose kill, docker-compose rm -f).
.PARAMETER Exec
Executes a command in the container using docker exec.
.PARAMETER Refresh
Kills the running command in the container, publishes the project and restarts executing the command.
.PARAMETER Run
Removes any conflicting containers running on the same port, then instances the containers using docker-compose up.
.PARAMETER Environment
Specifies the configuration under which the project will be built and run (Debug or Release).
.PARAMETER Machine
Specifies the docker machine name to connect to. This is optional and if left blank or not provided it will use the currently configured docker host, or if no host is set, will use the Docker for Windows beta.
.PARAMETER ProjectFolder
Specifies the project folder, defaults to the parent of the directory containing this script.
.PARAMETER ProjectName
Specifies the project name used by docker-compose, defaults to the name of $ProjectFolder.
.PARAMETER NoCache
Specifies the build argument --no-cache.
.PARAMETER RemoteDebugging
Specifies if remote debugging is needed, defaults to $False.
.PARAMETER ClrDebugVersion
Specifies the version of the debugger, defaults to 'VS2015U2'.
.PARAMETER Command
Specifies the command to run in the container.
.INPUTS
None. You cannot pipe inputs to DockerTask.
.EXAMPLE
Compiles the project and builds the docker image using the currently configured docker host, or when no host is set, used for the Docker for Windows beta. To see the container running, use the -Run parameter
C:\PS> .\DockerTask.ps1 -Build -Environment Release
.EXAMPLE
Will use 'docker-compose up' on the project, using the docker-machine instance named 'default', and opens a browser once the container responds. Assumes -Build was previously run. For the Docker for Windows Beta, remove the -Machine parameter or pass '' as the value.
C:\PS> .\DockerTask.ps1 -Run -Environment Release -Machine 'default'
.LINK
http://aka.ms/DockerToolsForVS
#>
Param(
[Parameter(ParameterSetName = "Build", Position = 0, Mandatory = $True)]
[switch]$Build,
[Parameter(ParameterSetName = "Clean", Position = 0, Mandatory = $True)]
[switch]$Clean,
[Parameter(ParameterSetName = "Run", Position = 0, Mandatory = $True)]
[switch]$Run,
[Parameter(ParameterSetName = "Exec", Position = 0, Mandatory = $True)]
[switch]$Exec,
[Parameter(ParameterSetName = "Refresh", Position = 0, Mandatory = $True)]
[switch]$Refresh,
[Parameter(ParameterSetName = "ValidateVolumeMapping", Position = 0, Mandatory = $True)]
[switch]$ValidateVolumeMapping,
[parameter(ParameterSetName = "Clean", Position = 1, Mandatory = $True)]
[parameter(ParameterSetName = "Build", Position = 1, Mandatory = $True)]
[parameter(ParameterSetName = "Run", Position = 1, Mandatory = $True)]
[parameter(ParameterSetName = "Refresh", Position = 1, Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[String]$Environment,
[parameter(ParameterSetName = "Clean", Position = 2, Mandatory = $False)]
[parameter(ParameterSetName = "Build", Position = 2, Mandatory = $False)]
[parameter(ParameterSetName = "Run", Position = 2, Mandatory = $False)]
[parameter(ParameterSetName = "Exec", Position = 1, Mandatory = $False)]
[parameter(ParameterSetName = "Refresh", Position = 2, Mandatory = $False)]
[parameter(ParameterSetName = "ValidateVolumeMapping", Position = 1, Mandatory = $False)]
[String]$Machine,
[parameter(ParameterSetName = "Clean", Position = 3, Mandatory = $False)]
[parameter(ParameterSetName = "Build", Position = 3, Mandatory = $False)]
[parameter(ParameterSetName = "Run", Position = 3, Mandatory = $False)]
[parameter(ParameterSetName = "Exec", Position = 2, Mandatory = $False)]
[parameter(ParameterSetName = "Refresh", Position = 3, Mandatory = $False)]
[ValidateNotNullOrEmpty()]
[String]$ProjectFolder = (Split-Path -Path $MyInvocation.MyCommand.Definition),
[parameter(ParameterSetName = "Clean", Position = 4, Mandatory = $False)]
[parameter(ParameterSetName = "Build", Position = 4, Mandatory = $False)]
[parameter(ParameterSetName = "Run", Position = 4, Mandatory = $False)]
[parameter(ParameterSetName = "Exec", Position = 3, Mandatory = $False)]
[parameter(ParameterSetName = "Refresh", Position = 4, Mandatory = $False)]
[ValidateNotNullOrEmpty()]
[String]$ProjectName = (Split-Path -Path (Resolve-Path $ProjectFolder) -Leaf).ToLowerInvariant(),
[parameter(ParameterSetName = "Build", Position = 5, Mandatory = $False)]
[switch]$NoCache,
[parameter(ParameterSetName = "Run", Position = 6, Mandatory = $False)]
[bool]$RemoteDebugging = $False,
[parameter(ParameterSetName = "Build", Position = 6, Mandatory = $False)]
[String]$ClrDebugVersion = "VS2015U2",
[parameter(ParameterSetName = "Exec", Position = 4, Mandatory = $True)]
[parameter(ParameterSetName = "Refresh", Position = 5, Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[String]$Command
)
$ErrorActionPreference = "Stop"
# Turns VERBOSE output ON
$VerbosePreference = "Continue"
# Docker Working Directory for validating volume mapping. Should be in sync with Dockerfile and Docker.props.
$DockerWorkingDirectory = "/app/"
# Path for the launch URL to be opened
$launchURLPath = ""
# The project name can only contain alphanumeric charecters, replace everything else with empty string
$ProjectName = $ProjectName -replace "[^a-zA-Z0-9]", ""
# The name of the image created by the compose file
$ImageName = "username/eshopconsole"
# Calculate the name of the container created by the compose file
$ContainerName = "${ProjectName}_eshopconsole"
# .net core runtime ID for the container (used to publish the app correctly)
$RuntimeID = "debian.8-x64"
# .net core framework (used to publish the app correctly)
$Framework = "netcoreapp1.0"
# Kills all containers using an image, removes all containers using an image, and removes the image.
function Clean () {
$composeFilePath = GetComposeFilePath($ProjectFolder)
# Call compose-down to clean up the containers
$shellCommand = "docker-compose -f '$composeFilePath' -p $ProjectName down"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression "cmd /c $shellCommand `"2>&1`""
if ($LastExitCode -ne 0) {
Write-Error "Failed to clean up the containers"
}
# If $ImageName exists remove it
$ImageNameRegEx = "\b$ImageName\b"
docker images | select-string -pattern $ImageNameRegEx | foreach {
$imageName = $_.Line.split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)[0];
$tag = $_.Line.split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)[1];
$shellCommand = "docker rmi -f ${imageName}:$tag"
Write-Verbose "Executing: $shellCommand";
Invoke-Expression "cmd /c $shellCommand `"2>&1`""
}
# Remove any dangling images (from previous builds)
$shellCommand = "docker images -q --filter 'dangling=true'"
Write-Verbose "Executing: $shellCommand"
$danglingImages = $(Invoke-Expression "cmd /c $shellCommand `"2>&1`"")
if (-not [String]::IsNullOrWhiteSpace($danglingImages)) {
$shellCommand = "docker rmi -f $danglingImages"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression "cmd /c $shellCommand `"2>&1`""
}
# If the folder for publishing exists, delete it
if (Test-Path $pubPath) {
Remove-Item $pubPath -Force -Recurse
}
}
# Runs docker build.
function Build () {
# Publish the project
PublishProject
$composeFilePath = GetComposeFilePath($pubPath)
$buildArgs = ""
if ($NoCache)
{
$buildArgs = "--no-cache"
}
# Call docker-compose on the published project to build the images
$shellCommand = "docker-compose -f '$composeFilePath' -p $ProjectName build $buildArgs"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression "cmd /c $shellCommand `"2>&1`""
if ($LastExitCode -ne 0) {
Write-Error "Failed to build the image"
}
}
function GetContainerId () {
$containerId = (docker ps -f "name=$ContainerName" -q -n=1)
if ([System.String]::IsNullOrWhiteSpace($containerId)) {
Write-Error "Could not find a container named $ContainerName"
}
$containerId
}
# Validates volume mapping
function ValidateVolumeMapping () {
# Volume mapping enables shared folder mounting between host and docker container
# If there are no files in the working directory, most likely volume mapping is misconfigured.
$containerId = GetContainerId
Write-Host "Validating volume mapping in the container $containerId"
$shellCommand = "docker exec -i $containerId /bin/bash -c 'ls $DockerWorkingDirectory'"
if (!$(Invoke-Expression $shellCommand)) {
Write-Error "Unable to validate volume mapping. For troubleshooting, follow instructions from http://aka.ms/DockerToolsTroubleshooting"
}
}
# Runs docker run
function Run () {
$composeFilePath = GetComposeFilePath($pubPath)
$shellCommand = "docker-compose -f '$composeFilePath' -p $ProjectName up -d"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression "cmd /c $shellCommand `"2>&1`""
if ($LastExitCode -ne 0) {
Write-Error "Failed to start the container(s)"
}
}
# Runs docker run
function Exec () {
$containerId = GetContainerId
$shellCommand = "docker exec -i $containerId $Command"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression $shellCommand
if ($LastExitCode -ne 0) {
Write-Error "Failed to exec command $Command in the container"
}
}
function Refresh () {
# Find the container
$containerId = GetContainerId
# Kill any existing process
$shellCommand = "docker exec -i $containerId /bin/bash -c 'if PID=`$(pidof -x $Command); then kill `$PID; fi'"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression $shellCommand
# Publish the project
PublishProject
# Restart the process
$shellCommand = "docker exec -i $containerId $Command"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression $shellCommand
if ($LastExitCode -ne 0) {
Write-Error "Failed to exec command $Command in the container"
}
}
# Publishes the project
function PublishProject () {
$oldPath = $Env:Path
try {
# Need to add $ProjectFolder\node_modules\.bin and the External Tools folder from the Web Tools to Path before calling publish
$newPath = (Join-Path $ProjectFolder ".\node_modules\.bin") + ";$oldPath"
# Find where VS is installed
$vsPath = $null
if (Test-Path HKLM:\Software\WOW6432Node\Microsoft\VisualStudio\14.0\) {
$vsPath = (Get-ItemProperty -Path HKLM:\Software\WOW6432Node\Microsoft\VisualStudio\14.0\ -Name ShellFolder).ShellFolder
} elseif (Test-Path HKLM:\Software\Microsoft\VisualStudio\14.0\) {
$vsPath = (Get-ItemProperty -Path HKLM:\Software\Microsoft\VisualStudio\14.0\ -Name ShellFolder).ShellFolder
}
# Find where the Web Tools are installed
if ($vsPath -ne $null) {
$webExternalPath = $null
# Check for the Web Tools in VS
if (Test-Path (Join-Path $vsPath "Web")) {
$webExternalPath = Join-Path $vsPath (Join-Path "Web" "External")
# or the Web Exress edition
} elseif (Test-Path (Join-Path $vsPath "WebExpress")) {
$webExternalPath = Join-Path $vsPath (Join-Path "WebExpress" "External")
}
# If the Web Tools were found, add the externals from the Web Tools to Path
if ($webExternalPath -ne $null) {
$newPath = "$newPath;$webExternalPath;$webExternalPath\git"
}
}
# Set Path to our new path
$Env:Path = $newPath
# Publish the project
dotnet publish -f $Framework -r $RuntimeID -c $Environment -o $pubPath $ProjectFolder
if ($? -eq $False) {
Write-Error "Failed to publish the project"
}
}
finally {
# Restore path to its old value
$Env:Path = $oldPath
}
}
function GetComposeFilePath([string]$folder) {
$composeFileName = "docker-compose.yml"
if ($Environment -ne "Release") {
$composeFileName = "docker-compose.$($Environment.ToLower()).yml"
}
$composeFilePath = Join-Path $folder $composeFileName
if (Test-Path $composeFilePath) {
return $composeFilePath
} else {
Write-Error -Message "$Environment is not a valid parameter. File '$composeFilePath' does not exist." -Category InvalidArgument
}
}
# Need the full path of the project for mapping
$ProjectFolder = Resolve-Path $ProjectFolder
if (![System.String]::IsNullOrWhiteSpace($Machine)) {
$users = Split-Path $env:USERPROFILE -Parent
# Set the environment variables for the docker machine to connect to
$shellCommand = "docker-machine env $Machine --shell powershell"
Write-Verbose "Executing: $shellCommand | Invoke-Expression"
Invoke-Expression $shellCommand | Invoke-Expression
if ($LastExitCode -ne 0) {
Write-Error "Failed to set docker environment variables"
}
# Get the driver name of the docker machine
$DriverName = (docker-machine inspect $Machine | Out-String | ConvertFrom-Json)."DriverName"
# If the driver is virtualbox, need to check that the project location can be volume mapped
if ($DriverName -eq "virtualbox") {
if (!$ProjectFolder.StartsWith($users, [StringComparison]::InvariantCultureIgnoreCase)) {
$message = "VirtualBox by default shares C:\Users as c/Users. If the project is not under c:\Users, please manually add it to the shared folders on VirtualBox. "`
+ "Follow instructions from https://www.virtualbox.org/manual/ch04.html#sharedfolders"
Write-Warning -Message $message
}
elseif (!$ProjectFolder.StartsWith($users, [StringComparison]::InvariantCulture)) {
# If the project is under C:\Users, fix the casing if necessary. Path in Linux is case sensitive and the default shared folder c/Users
# on VirtualBox can only be accessed if the project folder starts with the correct casing C:\Users as in $env:USERPROFILE
$ProjectFolder = $users + $ProjectFolder.Substring($users.Length)
}
}
}
# Our working directory in bin
$dockerBinFolder = Join-Path $ProjectFolder (Join-Path "bin" "Docker")
# The folder to publish the app to
$pubPath = Join-Path (Join-Path $dockerBinFolder $Environment) "app"
Write-Verbose "Setting: `$env:CLRDBG_VERSION = `"$ClrDebugVersion`""
$env:CLRDBG_VERSION = "$ClrDebugVersion"
if ($RemoteDebugging) {
Write-Verbose "Setting: `$env:REMOTE_DEBUGGING = 1"
$env:REMOTE_DEBUGGING = 1
}
else {
Write-Verbose "Setting: `$env:REMOTE_DEBUGGING = 0"
$env:REMOTE_DEBUGGING = 0
}
# Call the correct functions for the parameters that were used
if ($Clean) {
Clean
}
if ($Build) {
Build
}
if ($Run) {
Run
}
if ($Exec) {
Exec
}
if ($Refresh) {
Refresh
}
if ($ValidateVolumeMapping) {
ValidateVolumeMapping
}

+ 10
- 0
src/Console/eShopConsole/Dockerfile View File

@ -0,0 +1,10 @@
FROM microsoft/dotnet:1.0.0-core
# Set the Working Directory
WORKDIR /app
# Copy the app
COPY . /app
# Start the app
ENTRYPOINT dotnet eShopConsole.dll

+ 20
- 0
src/Console/eShopConsole/Dockerfile.debug View File

@ -0,0 +1,20 @@
FROM microsoft/dotnet:1.0.0-preview2-sdk
ENV NUGET_XMLDOC_MODE skip
# Install debugging components
ARG CLRDBG_VERSION=VS2015U2
WORKDIR /clrdbg
RUN curl -SL https://raw.githubusercontent.com/Microsoft/MIEngine/getclrdbg-release/scripts/GetClrDbg.sh --output GetClrDbg.sh \
&& chmod 700 GetClrDbg.sh \
&& ./GetClrDbg.sh $CLRDBG_VERSION \
&& rm GetClrDbg.sh
# Set the Working Directory
WORKDIR /app
# Copy the app
COPY . /app
# If we are launching through a remote debugger wait for it, otherwise start the app
ENTRYPOINT ["/bin/bash", "-c", "if [ \"$REMOTE_DEBUGGING\" -eq 0 ]; then dotnet eShopConsole.dll; else sleep infinity; fi"]

+ 81
- 0
src/Console/eShopConsole/Program.cs View File

@ -0,0 +1,81 @@
using System;
using System.Linq;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Contracts;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Repositories;
using Microsoft.EntityFrameworkCore;
namespace eShopConsole
{
public class Program
{
public static void Main(string[] args)
{
// All contexts that share the same service provider will share the same database
//Using InMemory DB
//var options = DbContextUtil.CreateNewContextOptionsForInMemoryDB();
//Using Sql Server
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.",
"Redmond",
"Washington",
"WA",
"United States",
"US",
"98052",
47.661492,
-122.131309
);
//Create sample Orders
Order order1 = new Order(Guid.NewGuid(), sampleAddress, sampleAddress);
//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);
orderRepository.UnitOfWork.CommitAsync();
//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
using (var context = new OrderingDbContext(options))
{
var orders = context.Orders
.Include(o => o.ShippingAddress)
.Include(o => o.BillingAddress)
.ToList();
//Could be using .Load() if you don't want to create a List
//OTHER SAMPLE
//var company = context.Companies
// .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Car)
// .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Country)
// .FirstOrDefault(co => co.companyID == companyID);
//Assert when running test with a clean In-Memory DB
//Assert.Equal(1, context.Orders.Count());
string cityName = orders.First<Order>().ShippingAddress.City;
Console.WriteLine(cityName);
}
}
}
}

+ 19
- 0
src/Console/eShopConsole/Properties/AssemblyInfo.cs View File

@ -0,0 +1,19 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("eShopConsole")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c10c7b69-ce4f-4167-928e-33b7fa1dffc7")]

+ 17
- 0
src/Console/eShopConsole/Properties/Docker.props View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- Use this property to manually turn building the docker image as part of the project's build on and off,
otherwise the target SetDockerProps will set it based on $(ActiveDebugProfile) -->
<!--<DockerBuild Condition=" '$(DockerBuild)'=='' ">True</DockerBuild> -->
<!-- Use this property to change the docker host that is used by this project. Leave the value blank for the Docker for Windows beta or change to your docker-machine's name
if using a host registered in docker-machine. (Note: you need to restart VS after changing this property) -->
<DockerMachineName Condition=" '$(DockerMachineName)'=='' "></DockerMachineName>
<!-- Use these properties to configure the process that will be started by the debugger in the container -->
<DockerDebugStartProcess>dotnet</DockerDebugStartProcess>
<DockerDebugStartArgs>/app/eShopConsole.dll</DockerDebugStartArgs>
<DockerDebugWorkingDirectory>/app/</DockerDebugWorkingDirectory>
</PropertyGroup>
</Project>

+ 75
- 0
src/Console/eShopConsole/Properties/Docker.targets View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<NoDockerCache>False</NoDockerCache>
</PropertyGroup>
<!-- These properties coordinate the targets used for calling docker commands -->
<PropertyGroup>
<DockerBuildDependsOn>
SetDockerProps;
CoreDockerBuild;
</DockerBuildDependsOn>
<DockerCleanDependsOn>
SetDockerProps;
CoreDockerClean;
</DockerCleanDependsOn>
<DockerBeforeRebuildDependsOn>
SetDockerProps;
CoreDockerBeforeRebuild;
</DockerBeforeRebuildDependsOn>
</PropertyGroup>
<!-- This target dynamically sets the DockerBuild property based on the ActiveDebugProfile so that docker commands will only be invoked when targeting docker -->
<Target Name="SetDockerProps">
<PropertyGroup>
<DockerBuild Condition=" '$(DockerBuild)'=='' And ('$(ActiveDebugProfile)' == 'Docker' Or '$(ActiveDebugProfile)' == '')">True</DockerBuild>
</PropertyGroup>
</Target>
<!-- This target takes care of reporting failures from calling the powershell script in CoreDockerBuild -->
<Target Name="CoreDockerBuildFailed">
<Error File="$(MSBuildProjectDirectory)\DockerTask.ps1" Text="Error Running: $(DockerBuildCommand). See the output window for details."></Error>
</Target>
<!-- These targets take care of buiding the docker image as part of the project's build -->
<Target Name="DockerBuild" AfterTargets="Build" DependsOnTargets="$(DockerBuildDependsOn)" />
<Target Name="CoreDockerBuild" Condition="'$(DockerBuild)'=='True'">
<PropertyGroup>
<DockerBuildCommand>powershell -NonInteractive -ExecutionPolicy RemoteSigned .\DockerTask.ps1 -Build -Environment $(Configuration) -Machine '$(DockerMachineName)' -ClrDebugVersion VS2015U2</DockerBuildCommand>
<DockerBuildCommand Condition="'$(NoDockerCache)'=='True'">$(DockerBuildCommand) -NoCache</DockerBuildCommand>
</PropertyGroup>
<Message Importance="high" Text="$(DockerBuildCommand)" />
<Exec
WorkingDirectory="$(MSBuildProjectDirectory)"
Command="$(DockerBuildCommand)" />
<OnError ExecuteTargets="CoreDockerBuildFailed"/>
</Target>
<!-- This target takes care of reporting failures from calling the powershell script in CoreDockerClean -->
<Target Name="CoreDockerCleanFailed">
<Error File="$(MSBuildProjectDirectory)\DockerTask.ps1" Text="Error Running: $(DockerCleanCommand). See the output window for details."></Error>
</Target>
<!-- These targets take care of buiding the docker image as part of the project's clean -->
<Target Name="DockerClean" AfterTargets="Clean" DependsOnTargets="$(DockerCleanDependsOn)" />
<Target Name="CoreDockerClean" Condition="'$(DockerBuild)'=='True'">
<PropertyGroup>
<DockerCleanCommand>powershell -NonInteractive -ExecutionPolicy RemoteSigned .\DockerTask.ps1 -Clean -Environment $(Configuration) -Machine '$(DockerMachineName)'</DockerCleanCommand>
</PropertyGroup>
<Message Importance="high" Text="$(DockerCleanCommand)" />
<Exec
WorkingDirectory="$(MSBuildProjectDirectory)"
Command="$(DockerCleanCommand)" />
<OnError ExecuteTargets="CoreDockerCleanFailed"/>
</Target>
<!-- These targets take care of buiding the docker image as part of the project's rebuild -->
<Target Name="DockerBeforeRebuild" BeforeTargets="BeforeRebuild" DependsOnTargets="$(DockerBeforeRebuildDependsOn)" />
<Target Name="CoreDockerBeforeRebuild" Condition="'$(DockerBuild)'=='True'">
<!-- DockerBuild will be called later, just need to change it to not used the cached images -->
<PropertyGroup>
<NoDockerCache>True</NoDockerCache>
</PropertyGroup>
</Target>
</Project>

+ 8
- 0
src/Console/eShopConsole/Properties/launchSettings.json View File

@ -0,0 +1,8 @@
{
"profiles": {
"Docker": {
"executablePath": "%WINDIR%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"commandLineArgs": "-ExecutionPolicy RemoteSigned .\\DockerTask.ps1 -Run -Environment $(Configuration) -Machine '$(DockerMachineName)'"
}
}
}

+ 12
- 0
src/Console/eShopConsole/docker-compose.debug.yml View File

@ -0,0 +1,12 @@
version: '2'
services:
eshopconsole:
image: username/eshopconsole:Debug
build:
context: .
dockerfile: Dockerfile.debug
environment:
- REMOTE_DEBUGGING=${REMOTE_DEBUGGING}
volumes:
- .:/app

+ 8
- 0
src/Console/eShopConsole/docker-compose.yml View File

@ -0,0 +1,8 @@
version: '2'
services:
eshopconsole:
image: username/eshopconsole
build:
context: .
dockerfile: Dockerfile

+ 22
- 0
src/Console/eShopConsole/eShopConsole.xproj View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>c10c7b69-ce4f-4167-928e-33b7fa1dffc7</ProjectGuid>
<RootNamespace>eShopConsole</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DockerToolsMinVersion>0.21</DockerToolsMinVersion>
</PropertyGroup>
<Import Project="Properties\Docker.props" />
<Import Project="Properties\Docker.targets" />
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

+ 31
- 0
src/Console/eShopConsole/project.json View File

@ -0,0 +1,31 @@
{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true,
"debugType": "portable"
},
"dependencies": {
"Microsoft.EntityFrameworkCore": "1.0.0",
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
},
"Ordering.API": "1.0.0-*",
"Ordering.Domain": "1.0.0-*",
"Ordering.SqlData": "1.0.0-*"
},
"frameworks": {
"netcoreapp1.0": {
"imports": "dnxcore50"
}
},
"publishOptions": {
"include": [
"docker-compose.yml",
"docker-compose.debug.yml",
"Dockerfile.debug",
"Dockerfile",
".dockerignore"
]
}
}

+ 5
- 0
src/Services/Ordering/Ordering.API/.dockerignore View File

@ -0,0 +1,5 @@
# Docker support files
docker-compose.yml
docker-compose.debug.yml
Dockerfile
Dockerfile.debug

+ 23
- 0
src/Services/Ordering/Ordering.API/Controllers/EnvironmentInfoController.cs View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
{
[Route("api/[controller]")]
public class EnvironmentInfoController : Controller
{
// GET api/environmentInfo/machinename
[HttpGet("machinename")]
public dynamic GetMachineName()
{
return new
{
Node = Environment.MachineName
};
}
}
}

+ 53
- 19
src/Services/Ordering/Ordering.API/Controllers/OrderingController.cs View File

@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Contracts;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
@ -16,15 +16,19 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
public class OrderingController : Controller
{
private IOrderRepository _orderRepository;
private OrderingQueries _queries;
private IOrderdingQueries _queries;
//private OrderingDbContext _context;
public OrderingController(IOrderRepository orderRepository,
OrderingQueries orderingQueries
IOrderdingQueries orderingQueries //,
//OrderingDbContext context
)
{
//Injected objects from the IoC container
_orderRepository = orderRepository;
_queries = orderingQueries;
//_context = context;
}
@ -45,6 +49,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
return Ok(response);
}
//(CDLTLL) - Using parameters
//Alternate method if using several parameters instead of part of the URL
// GET api/ordering/orders/?orderId=xxxGUIDxxx&otherParam=value
//[HttpGet("orders")]
@ -55,34 +60,63 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
[HttpPut("orders/create")]
public async Task<int> Post([FromBody]Order order)
{
return await _orderRepository.Add(order);
//_context.Orders.Add(order);
//return await _context.SaveChangesAsync();
_orderRepository.Add(order);
return await _orderRepository.UnitOfWork.CommitAsync();
}
// PUT api/ordering/orders/xxxOrderGUIDxxxx/update
[HttpPut("orders/{orderId:Guid}/update")]
public async Task<int> UpdateOrder(Guid orderID, [FromBody] Order orderToUpdate)
{
return await _orderRepository.Add(orderToUpdate);
//_context.Orders.Update(orderToUpdate);
//return await _context.SaveChangesAsync();
_orderRepository.Update(orderToUpdate);
return await _orderRepository.UnitOfWork.CommitAsync();
}
// DELETE api/ordering/orders/xxxOrderGUIDxxxx
[HttpDelete("orders/{orderId:Guid}/delete")]
public async Task<int> Delete(Guid id)
[HttpDelete("orders/{orderId:Guid}/remove")]
public async Task<int> Remove(Guid id)
{
return await _orderRepository.Remove(id);
await _orderRepository.Remove(id);
return await _orderRepository.UnitOfWork.CommitAsync();
}
//Order orderToDelete = _context.Orders
// .Where(o => o.Id == id)
// .SingleOrDefault();
//_context.Orders.Remove(orderToDelete);
//return await _context.SaveChangesAsync();
// GET api/ordering/orders/add_test_data_and_get_all
[HttpGet("orders/add_test_data_and_get_all")]
public async Task<IActionResult> AddTestDataAndGetAllOrders()
{
//TEST ADDING ORDERS *********************************
//Create generic Address ValueObject
Address sampleAddress = new Address("15703 NE 61st Ct.",
"Redmond",
"Washington",
"WA",
"United States",
"US",
"98052",
47.661492,
-122.131309
);
//Create sample Orders
Order order1 = new Order(Guid.NewGuid(), sampleAddress, sampleAddress);
//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);
int numRecs = await _orderRepository.UnitOfWork.CommitAsync();
//_context.Orders.Add(order1);
//_context.SaveChanges();
//*****************************************************
dynamic response = await _queries.GetAllOrdersIncludingValueObjectsAndChildEntities();
return Ok(response);
}
}


+ 489
- 0
src/Services/Ordering/Ordering.API/DockerTask.ps1 View File

@ -0,0 +1,489 @@
<#
.SYNOPSIS
Deploys an ASP .NET Core Web Application into a docker container running in a specified Docker machine.
.DESCRIPTION
The following script will execute a set of Docker commands against the designated dockermachine.
.PARAMETER Build
Builds the containers using docker-compose build.
.PARAMETER Clean
Clears out any running containers (docker-compose kill, docker-compose rm -f).
.PARAMETER Exec
Executes a command in the container using docker exec.
.PARAMETER GetUrl
Gets the url for the site to open.
.PARAMETER WaitForUrl
Waits for url to respond.
.PARAMETER Refresh
Kills the running command in the container, publishes the project and restarts executing the command.
.PARAMETER Run
Removes any conflicting containers running on the same port, then instances the containers using docker-compose up.
.PARAMETER Environment
Specifies the configuration under which the project will be built and run (Debug or Release).
.PARAMETER Machine
Specifies the docker machine name to connect to. This is optional and if left blank or not provided it will use the currently configured docker host, or if no host is set, will use the Docker for Windows beta.
.PARAMETER ProjectFolder
Specifies the project folder, defaults to the parent of the directory containing this script.
.PARAMETER ProjectName
Specifies the project name used by docker-compose, defaults to the name of $ProjectFolder.
.PARAMETER NoCache
Specifies the build argument --no-cache.
.PARAMETER OpenSite
Specifies whether to launch the site once the docker container is running, defaults to $True.
.PARAMETER RemoteDebugging
Specifies if remote debugging is needed, defaults to $False.
.PARAMETER ClrDebugVersion
Specifies the version of the debugger, defaults to 'VS2015U2'.
.PARAMETER Command
Specifies the command to run in the container.
.INPUTS
None. You cannot pipe inputs to DockerTask.
.EXAMPLE
Compiles the project and builds the docker image using the currently configured docker host, or when no host is set, used for the Docker for Windows beta. To see the container running, use the -Run parameter
C:\PS> .\DockerTask.ps1 -Build -Environment Release
.EXAMPLE
Will use 'docker-compose up' on the project, using the docker-machine instance named 'default', and opens a browser once the container responds. Assumes -Build was previously run. For the Docker for Windows Beta, remove the -Machine parameter or pass '' as the value.
C:\PS> .\DockerTask.ps1 -Run -Environment Release -Machine 'default'
.LINK
http://aka.ms/DockerToolsForVS
#>
Param(
[Parameter(ParameterSetName = "Build", Position = 0, Mandatory = $True)]
[switch]$Build,
[Parameter(ParameterSetName = "Clean", Position = 0, Mandatory = $True)]
[switch]$Clean,
[Parameter(ParameterSetName = "Run", Position = 0, Mandatory = $True)]
[switch]$Run,
[Parameter(ParameterSetName = "Exec", Position = 0, Mandatory = $True)]
[switch]$Exec,
[Parameter(ParameterSetName = "GetUrl", Position = 0, Mandatory = $True)]
[switch]$GetUrl,
[Parameter(ParameterSetName = "WaitForUrl", Position = 0, Mandatory = $True)]
[switch]$WaitForUrl,
[Parameter(ParameterSetName = "Refresh", Position = 0, Mandatory = $True)]
[switch]$Refresh,
[Parameter(ParameterSetName = "ValidateVolumeMapping", Position = 0, Mandatory = $True)]
[switch]$ValidateVolumeMapping,
[parameter(ParameterSetName = "Clean", Position = 1, Mandatory = $True)]
[parameter(ParameterSetName = "Build", Position = 1, Mandatory = $True)]
[parameter(ParameterSetName = "Run", Position = 1, Mandatory = $True)]
[parameter(ParameterSetName = "Refresh", Position = 1, Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[String]$Environment,
[parameter(ParameterSetName = "Clean", Position = 2, Mandatory = $False)]
[parameter(ParameterSetName = "Build", Position = 2, Mandatory = $False)]
[parameter(ParameterSetName = "Run", Position = 2, Mandatory = $False)]
[parameter(ParameterSetName = "Exec", Position = 1, Mandatory = $False)]
[Parameter(ParameterSetName = "GetUrl", Position = 1, Mandatory = $False)]
[Parameter(ParameterSetName = "WaitForUrl", Position = 1, Mandatory = $False)]
[parameter(ParameterSetName = "Refresh", Position = 2, Mandatory = $False)]
[parameter(ParameterSetName = "ValidateVolumeMapping", Position = 1, Mandatory = $False)]
[String]$Machine,
[parameter(ParameterSetName = "Clean", Position = 3, Mandatory = $False)]
[parameter(ParameterSetName = "Build", Position = 3, Mandatory = $False)]
[parameter(ParameterSetName = "Run", Position = 3, Mandatory = $False)]
[parameter(ParameterSetName = "Exec", Position = 2, Mandatory = $False)]
[parameter(ParameterSetName = "Refresh", Position = 3, Mandatory = $False)]
[ValidateNotNullOrEmpty()]
[String]$ProjectFolder = (Split-Path -Path $MyInvocation.MyCommand.Definition),
[parameter(ParameterSetName = "Clean", Position = 4, Mandatory = $False)]
[parameter(ParameterSetName = "Build", Position = 4, Mandatory = $False)]
[parameter(ParameterSetName = "Run", Position = 4, Mandatory = $False)]
[parameter(ParameterSetName = "Exec", Position = 3, Mandatory = $False)]
[parameter(ParameterSetName = "Refresh", Position = 4, Mandatory = $False)]
[ValidateNotNullOrEmpty()]
[String]$ProjectName = (Split-Path -Path (Resolve-Path $ProjectFolder) -Leaf).ToLowerInvariant(),
[parameter(ParameterSetName = "Build", Position = 5, Mandatory = $False)]
[switch]$NoCache,
[parameter(ParameterSetName = "Run", Position = 5, Mandatory = $False)]
[bool]$OpenSite = $True,
[parameter(ParameterSetName = "Run", Position = 6, Mandatory = $False)]
[bool]$RemoteDebugging = $False,
[parameter(ParameterSetName = "Build", Position = 6, Mandatory = $False)]
[String]$ClrDebugVersion = "VS2015U2",
[parameter(ParameterSetName = "Exec", Position = 4, Mandatory = $True)]
[parameter(ParameterSetName = "Refresh", Position = 5, Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[String]$Command
)
$ErrorActionPreference = "Stop"
# Turns VERBOSE output ON
$VerbosePreference = "Continue"
# Docker Working Directory for validating volume mapping. Should be in sync with Dockerfile and Docker.props.
$DockerWorkingDirectory = "/app/"
# Path for the launch URL to be opened
$launchURLPath = "api/environmentInfo/machinename"
# The project name can only contain alphanumeric charecters, replace everything else with empty string
$ProjectName = $ProjectName -replace "[^a-zA-Z0-9]", ""
# The name of the image created by the compose file
$ImageName = "username/microsoft.eshoponcontainers.services.ordering.api"
# Calculate the name of the container created by the compose file
$ContainerName = "${ProjectName}_microsoft.eshoponcontainers.services.ordering.api"
# .net core runtime ID for the container (used to publish the app correctly)
$RuntimeID = "debian.8-x64"
# .net core framework (used to publish the app correctly)
$Framework = "netcoreapp1.0"
# Kills all containers using an image, removes all containers using an image, and removes the image.
function Clean () {
$composeFilePath = GetComposeFilePath($ProjectFolder)
# Call compose-down to clean up the containers
$shellCommand = "docker-compose -f '$composeFilePath' -p $ProjectName down"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression "cmd /c $shellCommand `"2>&1`""
if ($LastExitCode -ne 0) {
Write-Error "Failed to clean up the containers"
}
# If $ImageName exists remove it
$ImageNameRegEx = "\b$ImageName\b"
docker images | select-string -pattern $ImageNameRegEx | foreach {
$imageName = $_.Line.split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)[0];
$tag = $_.Line.split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)[1];
$shellCommand = "docker rmi -f ${imageName}:$tag"
Write-Verbose "Executing: $shellCommand";
Invoke-Expression "cmd /c $shellCommand `"2>&1`""
}
# Remove any dangling images (from previous builds)
$shellCommand = "docker images -q --filter 'dangling=true'"
Write-Verbose "Executing: $shellCommand"
$danglingImages = $(Invoke-Expression "cmd /c $shellCommand `"2>&1`"")
if (-not [String]::IsNullOrWhiteSpace($danglingImages)) {
$shellCommand = "docker rmi -f $danglingImages"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression "cmd /c $shellCommand `"2>&1`""
}
# If the folder for publishing exists, delete it
if (Test-Path $pubPath) {
Remove-Item $pubPath -Force -Recurse
}
}
# Runs docker build.
function Build () {
# Publish the project
PublishProject
$composeFilePath = GetComposeFilePath($pubPath)
$buildArgs = ""
if ($NoCache)
{
$buildArgs = "--no-cache"
}
# Call docker-compose on the published project to build the images
$shellCommand = "docker-compose -f '$composeFilePath' -p $ProjectName build $buildArgs"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression "cmd /c $shellCommand `"2>&1`""
if ($LastExitCode -ne 0) {
Write-Error "Failed to build the image"
}
}
function GetContainerId () {
$containerId = (docker ps -f "name=$ContainerName" -q -n=1)
if ([System.String]::IsNullOrWhiteSpace($containerId)) {
Write-Error "Could not find a container named $ContainerName"
}
$containerId
}
# Validates volume mapping
function ValidateVolumeMapping () {
# Volume mapping enables shared folder mounting between host and docker container
# If there are no files in the working directory, most likely volume mapping is misconfigured.
$containerId = GetContainerId
Write-Host "Validating volume mapping in the container $containerId"
$shellCommand = "docker exec -i $containerId /bin/bash -c 'ls $DockerWorkingDirectory'"
if (!$(Invoke-Expression $shellCommand)) {
Write-Error "Unable to validate volume mapping. For troubleshooting, follow instructions from http://aka.ms/DockerToolsTroubleshooting"
}
}
# Runs docker run
function Run () {
$composeFilePath = GetComposeFilePath($pubPath)
$conflictingContainerIds = $(docker ps | select-string -pattern ":80->" | foreach { Write-Output $_.Line.split()[0] })
if ($conflictingContainerIds) {
$conflictingContainerIds = $conflictingContainerIds -Join ' '
Write-Host "Stopping conflicting containers using port 80"
$stopCommand = "docker stop $conflictingContainerIds"
Write-Verbose "Executing: $stopCommand"
Invoke-Expression "cmd /c $stopCommand `"2>&1`""
if ($LastExitCode -ne 0) {
Write-Error "Failed to stop the container(s)"
}
}
$shellCommand = "docker-compose -f '$composeFilePath' -p $ProjectName up -d"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression "cmd /c $shellCommand `"2>&1`""
if ($LastExitCode -ne 0) {
Write-Error "Failed to start the container(s)"
}
if ($OpenSite) {
OpenSite
}
}
# Runs docker run
function Exec () {
$containerId = GetContainerId
$shellCommand = "docker exec -i $containerId $Command"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression $shellCommand
if ($LastExitCode -ne 0) {
Write-Error "Failed to exec command $Command in the container"
}
}
# Opens the remote site
function OpenSite () {
# If we're going to debug, the server won't start immediately; don't need to wait for it.
if (-not $RemoteDebugging)
{
$uri = GetUrl
WaitForUrl $uri
# Open the site.
Start-Process $uri
}
else {
# Give the container 10 seconds to get ready
Start-Sleep 10
}
}
# Gets the Url of the remote container
function GetUrl () {
if ([System.String]::IsNullOrWhiteSpace($Machine)) {
$launchURL = [System.UriBuilder]"http://localhost"
}
else {
$launchURL = [System.UriBuilder]"http://$(docker-machine ip $Machine)"
}
$launchURL.Path = $launchURLPath
return $launchURL.Uri.AbsoluteUri
}
# Checks if the URL is responding
function WaitForUrl ([string]$uri) {
Write-Host "Opening site $uri " -NoNewline
$status = 0
$count = 0
#Check if the site is available
while ($status -ne 200 -and $count -lt 120) {
try {
Write-Host "Trying to connect to $uri ($count/120)"
$response = Invoke-WebRequest -Uri $uri -Headers @{"Cache-Control"="no-cache";"Pragma"="no-cache"} -UseBasicParsing -Verbose:$false
$status = [int]$response.StatusCode
}
catch [System.Net.WebException] { }
if($status -ne 200) {
# Wait Time max. 2 minutes (120 sec.)
Start-Sleep 1
$count += 1
}
}
Write-Host
if($status -ne 200) {
# Check if bad volume mapping is the reason why we were not able to connect
ValidateVolumeMapping
}
}
function Refresh () {
# Find the container
$containerId = GetContainerId
# Kill any existing process
$shellCommand = "docker exec -i $containerId /bin/bash -c 'if PID=`$(pidof -x $Command); then kill `$PID; fi'"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression $shellCommand
# Publish the project
PublishProject
# Restart the process
$shellCommand = "docker exec -i $containerId $Command"
Write-Verbose "Executing: $shellCommand"
Invoke-Expression $shellCommand
if ($LastExitCode -ne 0) {
Write-Error "Failed to exec command $Command in the container"
}
}
# Publishes the project
function PublishProject () {
$oldPath = $Env:Path
try {
# Need to add $ProjectFolder\node_modules\.bin and the External Tools folder from the Web Tools to Path before calling publish
$newPath = (Join-Path $ProjectFolder ".\node_modules\.bin") + ";$oldPath"
# Find where VS is installed
$vsPath = $null
if (Test-Path HKLM:\Software\WOW6432Node\Microsoft\VisualStudio\14.0\) {
$vsPath = (Get-ItemProperty -Path HKLM:\Software\WOW6432Node\Microsoft\VisualStudio\14.0\ -Name ShellFolder).ShellFolder
} elseif (Test-Path HKLM:\Software\Microsoft\VisualStudio\14.0\) {
$vsPath = (Get-ItemProperty -Path HKLM:\Software\Microsoft\VisualStudio\14.0\ -Name ShellFolder).ShellFolder
}
# Find where the Web Tools are installed
if ($vsPath -ne $null) {
$webExternalPath = $null
# Check for the Web Tools in VS
if (Test-Path (Join-Path $vsPath "Web")) {
$webExternalPath = Join-Path $vsPath (Join-Path "Web" "External")
# or the Web Exress edition
} elseif (Test-Path (Join-Path $vsPath "WebExpress")) {
$webExternalPath = Join-Path $vsPath (Join-Path "WebExpress" "External")
}
# If the Web Tools were found, add the externals from the Web Tools to Path
if ($webExternalPath -ne $null) {
$newPath = "$newPath;$webExternalPath;$webExternalPath\git"
}
}
# Set Path to our new path
$Env:Path = $newPath
# Publish the project
dotnet publish -f $Framework -r $RuntimeID -c $Environment -o $pubPath $ProjectFolder
if ($? -eq $False) {
Write-Error "Failed to publish the project"
}
}
finally {
# Restore path to its old value
$Env:Path = $oldPath
}
}
function GetComposeFilePath([string]$folder) {
$composeFileName = "docker-compose.yml"
if ($Environment -ne "Release") {
$composeFileName = "docker-compose.$($Environment.ToLower()).yml"
}
$composeFilePath = Join-Path $folder $composeFileName
if (Test-Path $composeFilePath) {
return $composeFilePath
} else {
Write-Error -Message "$Environment is not a valid parameter. File '$composeFilePath' does not exist." -Category InvalidArgument
}
}
# Need the full path of the project for mapping
$ProjectFolder = Resolve-Path $ProjectFolder
if (![System.String]::IsNullOrWhiteSpace($Machine)) {
$users = Split-Path $env:USERPROFILE -Parent
# Set the environment variables for the docker machine to connect to
$shellCommand = "docker-machine env $Machine --shell powershell"
Write-Verbose "Executing: $shellCommand | Invoke-Expression"
Invoke-Expression $shellCommand | Invoke-Expression
if ($LastExitCode -ne 0) {
Write-Error "Failed to set docker environment variables"
}
# Get the driver name of the docker machine
$DriverName = (docker-machine inspect $Machine | Out-String | ConvertFrom-Json)."DriverName"
# If the driver is virtualbox, need to check that the project location can be volume mapped
if ($DriverName -eq "virtualbox") {
if (!$ProjectFolder.StartsWith($users, [StringComparison]::InvariantCultureIgnoreCase)) {
$message = "VirtualBox by default shares C:\Users as c/Users. If the project is not under c:\Users, please manually add it to the shared folders on VirtualBox. "`
+ "Follow instructions from https://www.virtualbox.org/manual/ch04.html#sharedfolders"
Write-Warning -Message $message
}
elseif (!$ProjectFolder.StartsWith($users, [StringComparison]::InvariantCulture)) {
# If the project is under C:\Users, fix the casing if necessary. Path in Linux is case sensitive and the default shared folder c/Users
# on VirtualBox can only be accessed if the project folder starts with the correct casing C:\Users as in $env:USERPROFILE
$ProjectFolder = $users + $ProjectFolder.Substring($users.Length)
}
}
}
# Our working directory in bin
$dockerBinFolder = Join-Path $ProjectFolder (Join-Path "bin" "Docker")
# The folder to publish the app to
$pubPath = Join-Path (Join-Path $dockerBinFolder $Environment) "app"
Write-Verbose "Setting: `$env:CLRDBG_VERSION = `"$ClrDebugVersion`""
$env:CLRDBG_VERSION = "$ClrDebugVersion"
if ($RemoteDebugging) {
Write-Verbose "Setting: `$env:REMOTE_DEBUGGING = 1"
$env:REMOTE_DEBUGGING = 1
}
else {
Write-Verbose "Setting: `$env:REMOTE_DEBUGGING = 0"
$env:REMOTE_DEBUGGING = 0
}
# Call the correct functions for the parameters that were used
if ($Clean) {
Clean
}
if ($Build) {
Build
}
if ($Run) {
Run
}
if ($Exec) {
Exec
}
if ($GetUrl) {
GetUrl
}
if ($WaitForUrl) {
WaitForUrl (GetUrl)
}
if ($Refresh) {
Refresh
}
if ($ValidateVolumeMapping) {
ValidateVolumeMapping
}

+ 7
- 9
src/Services/Ordering/Ordering.API/Dockerfile View File

@ -6,27 +6,25 @@
# Set the Working Directory
WORKDIR /app
# Configure the listening port to 88
ENV ASPNETCORE_URLS http://*:88
# Configure the listening port to 80
ENV ASPNETCORE_URLS http://*:80
# Open port exposed by Docker
EXPOSE 88/tcp
EXPOSE 80/tcp
# Copy the app
COPY . /app
#################
# Restore NuGet packages
RUN ["dotnet", "restore"]
#RUN ["dotnet", "restore"]
# Build the .NET Core app
RUN ["dotnet", "build"]
#RUN ["dotnet", "build"]
# Entrypoint
ENTRYPOINT ["dotnet", "run"]
#ENTRYPOINT ["dotnet", "run"]
#################
# Entry point through the copied assembly
#ENTRYPOINT dotnet Ordering.API.dll
ENTRYPOINT dotnet Ordering.API.dll

+ 24
- 0
src/Services/Ordering/Ordering.API/Dockerfile.debug View File

@ -0,0 +1,24 @@
FROM microsoft/dotnet:1.0.0-preview2-sdk
ENV NUGET_XMLDOC_MODE skip
# Install debugging components
ARG CLRDBG_VERSION=VS2015U2
WORKDIR /clrdbg
RUN curl -SL https://raw.githubusercontent.com/Microsoft/MIEngine/getclrdbg-release/scripts/GetClrDbg.sh --output GetClrDbg.sh \
&& chmod 700 GetClrDbg.sh \
&& ./GetClrDbg.sh $CLRDBG_VERSION \
&& rm GetClrDbg.sh
# Set the Working Directory
WORKDIR /app
# Configure the listening port to 80
ENV ASPNETCORE_URLS http://*:80
EXPOSE 80
# Copy the app
COPY . /app
# If we are launching through a remote debugger wait for it, otherwise start the app
ENTRYPOINT ["/bin/bash", "-c", "if [ \"$REMOTE_DEBUGGING\" -eq 0 ]; then dotnet Ordering.API.dll; else sleep infinity; fi"]

+ 0
- 37
src/Services/Ordering/Ordering.API/Migrations/20160909202620_MyFirstMigration.Designer.cs View File

@ -1,37 +0,0 @@
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("20160909202620_MyFirstMigration")]
partial class MyFirstMigration
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.0.0-rtm-21431")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid>("BuyerId");
b.Property<DateTime>("OrderDate");
b.Property<int>("Status");
b.HasKey("Id");
b.ToTable("Orders");
});
}
}
}

+ 0
- 32
src/Services/Ordering/Ordering.API/Migrations/20160909202620_MyFirstMigration.cs View File

@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ordering.API.Migrations
{
public partial class MyFirstMigration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Orders",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
BuyerId = table.Column<Guid>(nullable: false),
OrderDate = table.Column<DateTime>(nullable: false),
Status = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Orders", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Orders");
}
}
}

+ 0
- 84
src/Services/Ordering/Ordering.API/Migrations/20160909223213_Migration2.Designer.cs View File

@ -1,84 +0,0 @@
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("20160909223213_Migration2")]
partial class Migration2
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.0.0-rtm-21431")
.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<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");
});
}
}
}

+ 0
- 98
src/Services/Ordering/Ordering.API/Migrations/20160909223213_Migration2.cs View File

@ -1,98 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ordering.API.Migrations
{
public partial class Migration2 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Address",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
City = table.Column<string>(nullable: true),
Country = table.Column<string>(nullable: true),
CountryCode = table.Column<string>(nullable: true),
Latitude = table.Column<double>(nullable: false),
Longitude = table.Column<double>(nullable: false),
State = table.Column<string>(nullable: true),
StateCode = table.Column<string>(nullable: true),
Street = table.Column<string>(nullable: true),
ZipCode = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Address", x => x.Id);
});
migrationBuilder.AddColumn<Guid>(
name: "BillingAddressId",
table: "Orders",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "ShippingAddressId",
table: "Orders",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Orders_BillingAddressId",
table: "Orders",
column: "BillingAddressId");
migrationBuilder.CreateIndex(
name: "IX_Orders_ShippingAddressId",
table: "Orders",
column: "ShippingAddressId");
migrationBuilder.AddForeignKey(
name: "FK_Orders_Address_BillingAddressId",
table: "Orders",
column: "BillingAddressId",
principalTable: "Address",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_Orders_Address_ShippingAddressId",
table: "Orders",
column: "ShippingAddressId",
principalTable: "Address",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Orders_Address_BillingAddressId",
table: "Orders");
migrationBuilder.DropForeignKey(
name: "FK_Orders_Address_ShippingAddressId",
table: "Orders");
migrationBuilder.DropIndex(
name: "IX_Orders_BillingAddressId",
table: "Orders");
migrationBuilder.DropIndex(
name: "IX_Orders_ShippingAddressId",
table: "Orders");
migrationBuilder.DropColumn(
name: "BillingAddressId",
table: "Orders");
migrationBuilder.DropColumn(
name: "ShippingAddressId",
table: "Orders");
migrationBuilder.DropTable(
name: "Address");
}
}
}

+ 0
- 84
src/Services/Ordering/Ordering.API/Migrations/20160909233852_Migration3.Designer.cs View File

@ -1,84 +0,0 @@
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("20160909233852_Migration3")]
partial class Migration3
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.0.0-rtm-21431")
.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<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");
});
}
}
}

+ 0
- 19
src/Services/Ordering/Ordering.API/Migrations/20160909233852_Migration3.cs View File

@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ordering.API.Migrations
{
public partial class Migration3 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

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

@ -1,89 +0,0 @@
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");
});
}
}
}

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

@ -1,37 +0,0 @@
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");
}
}
}

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

@ -1,46 +0,0 @@
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");
}
}
}

src/Services/Ordering/Ordering.API/Migrations/20160913061710_Migration5.Designer.cs → src/Services/Ordering/Ordering.API/Migrations/20160913204939_Migration1.Designer.cs View File

@ -8,8 +8,8 @@ using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
namespace Ordering.API.Migrations
{
[DbContext(typeof(OrderingDbContext))]
[Migration("20160913061710_Migration5")]
partial class Migration5
[Migration("20160913204939_Migration1")]
partial class Migration1
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{

+ 123
- 0
src/Services/Ordering/Ordering.API/Migrations/20160913204939_Migration1.cs View File

@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ordering.API.Migrations
{
public partial class Migration1 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "shared");
migrationBuilder.CreateSequence<int>(
name: "OrderSequences",
schema: "shared",
startValue: 1001L);
migrationBuilder.CreateTable(
name: "Address",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
City = table.Column<string>(nullable: true),
Country = table.Column<string>(nullable: true),
CountryCode = table.Column<string>(nullable: true),
Latitude = table.Column<double>(nullable: false),
Longitude = table.Column<double>(nullable: false),
State = table.Column<string>(nullable: true),
StateCode = table.Column<string>(nullable: true),
Street = table.Column<string>(nullable: true),
ZipCode = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Address", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Orders",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
BillingAddressId = table.Column<Guid>(nullable: true),
BuyerId = table.Column<Guid>(nullable: false),
OrderDate = table.Column<DateTime>(nullable: false),
SequenceNumber = table.Column<int>(nullable: false, defaultValueSql: "NEXT VALUE FOR shared.OrderSequences"),
ShippingAddressId = table.Column<Guid>(nullable: true),
Status = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Orders", x => x.Id);
table.ForeignKey(
name: "FK_Orders_Address_BillingAddressId",
column: x => x.BillingAddressId,
principalTable: "Address",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Orders_Address_ShippingAddressId",
column: x => x.ShippingAddressId,
principalTable: "Address",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
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_Orders_BillingAddressId",
table: "Orders",
column: "BillingAddressId");
migrationBuilder.CreateIndex(
name: "IX_Orders_ShippingAddressId",
table: "Orders",
column: "ShippingAddressId");
migrationBuilder.CreateIndex(
name: "IX_OrderItem_OrderId",
table: "OrderItem",
column: "OrderId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropSequence(
name: "OrderSequences",
schema: "shared");
migrationBuilder.DropTable(
name: "OrderItem");
migrationBuilder.DropTable(
name: "Orders");
migrationBuilder.DropTable(
name: "Address");
}
}
}

src/Services/Ordering/Ordering.API/Migrations/OrderingContextModelSnapshot.cs → src/Services/Ordering/Ordering.API/Migrations/OrderingDbContextModelSnapshot.cs View File

@ -8,7 +8,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
namespace Ordering.API.Migrations
{
[DbContext(typeof(OrderingDbContext))]
partial class OrderingContextModelSnapshot : ModelSnapshot
partial class OrderingDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{

+ 3
- 0
src/Services/Ordering/Ordering.API/Ordering.API.xproj View File

@ -14,6 +14,9 @@
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DockerToolsMinVersion>0.21</DockerToolsMinVersion>
</PropertyGroup>
<Import Project="Properties\Docker.props" />
<Import Project="Properties\Docker.targets" />
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

+ 17
- 0
src/Services/Ordering/Ordering.API/Properties/Docker.props View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- Use this property to manually turn building the docker image as part of the project's build on and off,
otherwise the target SetDockerProps will set it based on $(ActiveDebugProfile) -->
<!--<DockerBuild Condition=" '$(DockerBuild)'=='' ">True</DockerBuild> -->
<!-- Use this property to change the docker host that is used by this project. Leave the value blank for the Docker for Windows beta or change to your docker-machine's name
if using a host registered in docker-machine. (Note: you need to restart VS after changing this property) -->
<DockerMachineName Condition=" '$(DockerMachineName)'=='' "></DockerMachineName>
<!-- Use these properties to configure the process that will be started by the debugger in the container -->
<DockerDebugStartProcess>dotnet</DockerDebugStartProcess>
<DockerDebugStartArgs>/app/Ordering.API.dll</DockerDebugStartArgs>
<DockerDebugWorkingDirectory>/app/</DockerDebugWorkingDirectory>
</PropertyGroup>
</Project>

+ 75
- 0
src/Services/Ordering/Ordering.API/Properties/Docker.targets View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<NoDockerCache>False</NoDockerCache>
</PropertyGroup>
<!-- These properties coordinate the targets used for calling docker commands -->
<PropertyGroup>
<DockerBuildDependsOn>
SetDockerProps;
CoreDockerBuild;
</DockerBuildDependsOn>
<DockerCleanDependsOn>
SetDockerProps;
CoreDockerClean;
</DockerCleanDependsOn>
<DockerBeforeRebuildDependsOn>
SetDockerProps;
CoreDockerBeforeRebuild;
</DockerBeforeRebuildDependsOn>
</PropertyGroup>
<!-- This target dynamically sets the DockerBuild property based on the ActiveDebugProfile so that docker commands will only be invoked when targeting docker -->
<Target Name="SetDockerProps">
<PropertyGroup>
<DockerBuild Condition=" '$(DockerBuild)'=='' And ('$(ActiveDebugProfile)' == 'Docker' Or '$(ActiveDebugProfile)' == '')">True</DockerBuild>
</PropertyGroup>
</Target>
<!-- This target takes care of reporting failures from calling the powershell script in CoreDockerBuild -->
<Target Name="CoreDockerBuildFailed">
<Error File="$(MSBuildProjectDirectory)\DockerTask.ps1" Text="Error Running: $(DockerBuildCommand). See the output window for details."></Error>
</Target>
<!-- These targets take care of buiding the docker image as part of the project's build -->
<Target Name="DockerBuild" AfterTargets="Build" DependsOnTargets="$(DockerBuildDependsOn)" />
<Target Name="CoreDockerBuild" Condition="'$(DockerBuild)'=='True'">
<PropertyGroup>
<DockerBuildCommand>powershell -NonInteractive -ExecutionPolicy RemoteSigned .\DockerTask.ps1 -Build -Environment $(Configuration) -Machine '$(DockerMachineName)' -ClrDebugVersion VS2015U2</DockerBuildCommand>
<DockerBuildCommand Condition="'$(NoDockerCache)'=='True'">$(DockerBuildCommand) -NoCache</DockerBuildCommand>
</PropertyGroup>
<Message Importance="high" Text="$(DockerBuildCommand)" />
<Exec
WorkingDirectory="$(MSBuildProjectDirectory)"
Command="$(DockerBuildCommand)" />
<OnError ExecuteTargets="CoreDockerBuildFailed"/>
</Target>
<!-- This target takes care of reporting failures from calling the powershell script in CoreDockerClean -->
<Target Name="CoreDockerCleanFailed">
<Error File="$(MSBuildProjectDirectory)\DockerTask.ps1" Text="Error Running: $(DockerCleanCommand). See the output window for details."></Error>
</Target>
<!-- These targets take care of buiding the docker image as part of the project's clean -->
<Target Name="DockerClean" AfterTargets="Clean" DependsOnTargets="$(DockerCleanDependsOn)" />
<Target Name="CoreDockerClean" Condition="'$(DockerBuild)'=='True'">
<PropertyGroup>
<DockerCleanCommand>powershell -NonInteractive -ExecutionPolicy RemoteSigned .\DockerTask.ps1 -Clean -Environment $(Configuration) -Machine '$(DockerMachineName)'</DockerCleanCommand>
</PropertyGroup>
<Message Importance="high" Text="$(DockerCleanCommand)" />
<Exec
WorkingDirectory="$(MSBuildProjectDirectory)"
Command="$(DockerCleanCommand)" />
<OnError ExecuteTargets="CoreDockerCleanFailed"/>
</Target>
<!-- These targets take care of buiding the docker image as part of the project's rebuild -->
<Target Name="DockerBeforeRebuild" BeforeTargets="BeforeRebuild" DependsOnTargets="$(DockerBeforeRebuildDependsOn)" />
<Target Name="CoreDockerBeforeRebuild" Condition="'$(DockerBuild)'=='True'">
<!-- DockerBuild will be called later, just need to change it to not used the cached images -->
<PropertyGroup>
<NoDockerCache>True</NoDockerCache>
</PropertyGroup>
</Target>
</Project>

+ 6
- 2
src/Services/Ordering/Ordering.API/Properties/launchSettings.json View File

@ -11,7 +11,7 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/ordering/orders",
"launchUrl": "api/environmentInfo/machinename",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@ -19,10 +19,14 @@
"Microsoft.eShopOnContainers.Services.Ordering.API": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "http://localhost:5000/api/values",
"launchUrl": "http://localhost:5000/api/environmentInfo/machinename",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"executablePath": "%WINDIR%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"commandLineArgs": "-ExecutionPolicy RemoteSigned .\\DockerTask.ps1 -Run -Environment $(Configuration) -Machine '$(DockerMachineName)'"
}
}
}

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

@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Contracts;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Repositories;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries;
@ -38,17 +38,27 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API
services.AddMvc();
//Add EF Core Context (UnitOfWork)
var connection = @"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;";
services.AddDbContext<OrderingDbContext>(options => options.UseSqlServer(connection)
.UseSqlServer(connection, b => b.MigrationsAssembly("Ordering.API"))
//SQL LocalDB
// var connString = @"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;";
//SQL SERVER on-premises
//(Integrated Security)
//var connString = @"Server=CESARDLBOOKVHD;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;";
//(SQL Server Authentication)
var connString = @"Server=CESARDLBOOKVHD;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;";
//(TBD) connString = config.GetConnectionString("SqlStandardAuthentication.OrderingDb");
services.AddDbContext<OrderingDbContext>(options => options.UseSqlServer(connString)
.UseSqlServer(connString, 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<OrderingQueries, OrderingQueries>();
services.AddTransient<IOrderdingQueries, OrderingQueries>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -57,6 +67,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
//if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseMvc();
}
}


+ 14
- 0
src/Services/Ordering/Ordering.API/docker-compose.debug.yml View File

@ -0,0 +1,14 @@
version: '2'
services:
microsoft.eshoponcontainers.services.ordering.api:
image: username/microsoft.eshoponcontainers.services.ordering.api:Debug
build:
context: .
dockerfile: Dockerfile.debug
environment:
- REMOTE_DEBUGGING=${REMOTE_DEBUGGING}
ports:
- "80:80"
volumes:
- .:/app

+ 10
- 0
src/Services/Ordering/Ordering.API/docker-compose.yml View File

@ -0,0 +1,10 @@
version: '2'
services:
microsoft.eshoponcontainers.services.ordering.api:
image: username/microsoft.eshoponcontainers.services.ordering.api
build:
context: .
dockerfile: Dockerfile
ports:
- "80:80"

+ 14
- 11
src/Services/Ordering/Ordering.API/project.json View File

@ -18,15 +18,14 @@
"Microsoft.EntityFrameworkCore": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0",
"Ordering.Domain": "1.0.0-*",
"Ordering.SqlData": "1.0.0-*",
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
"Ordering.SqlData": "1.0.0-*"
"Microsoft.AspNetCore.Diagnostics": "1.0.0"
},
"tools": {
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
@ -35,29 +34,33 @@
]
}
},
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
"preserveCompilationContext": true,
"debugType": "portable"
},
"runtimeOptions": {
"configProperties": {
"System.GC.Server": true
}
},
"publishOptions": {
"include": [
"wwwroot",
"Views",
"Areas/**/Views",
"appsettings.json",
"web.config"
"web.config",
"docker-compose.yml",
"docker-compose.debug.yml",
"Dockerfile.debug",
"Dockerfile",
".dockerignore"
]
},
"scripts": {
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
"postpublish": [
"dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%"
]
}
}
}

+ 1
- 1
src/Services/Ordering/Ordering.Domain/AggregatesModel/Buyer/Buyer.cs View File

@ -3,7 +3,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
{
public class Buyer : AggregateRoot
public class Buyer : Entity, IAggregateRoot
{
public Buyer(Guid buyerId, string name, string lastName, string email, Address address, string phoneNumber)
{


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

@ -7,7 +7,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
{
public class Order : AggregateRoot
public class Order : Entity, IAggregateRoot
{
public Order(Guid buyerId, Address shippingAddress, Address billingAddress)
: this(buyerId, shippingAddress, billingAddress, DateTime.UtcNow)


src/Services/Ordering/Ordering.Domain/RepositoryContracts/IBuyerRepository.cs → src/Services/Ordering/Ordering.Domain/Contracts/IBuyerRepository.cs View File

@ -6,9 +6,9 @@ using System.Threading.Tasks;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Contracts
{
public interface IBuyerRepository : IRepository<Order>
public interface IBuyerRepository : IRepository
{
//TBD - To define Specific Actions Not In Base Repo
}

src/Services/Ordering/Ordering.Domain/RepositoryContracts/IOrderRepository.cs → src/Services/Ordering/Ordering.Domain/Contracts/IOrderRepository.cs View File

@ -6,13 +6,15 @@ using System.Threading.Tasks;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Contracts
{
public interface IOrderRepository : IRepository<Order>
public interface IOrderRepository : IRepository
{
//TBD - To define Specific Actions Not In Base Repo
Task<int> Remove(Guid id);
void Add(Order order);
void Update(Order order);
Task Remove(Guid id);
Task<Order> FindAsync(Guid id);
}
}

+ 6
- 0
src/Services/Ordering/Ordering.Domain/SeedWork/IAggregateRoot.cs View File

@ -0,0 +1,6 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
{
public interface IAggregateRoot { }
}

+ 3
- 25
src/Services/Ordering/Ordering.Domain/SeedWork/IRepository.cs View File

@ -1,29 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
{
/// <summary>
/// Base interface to implement a "Repository Pattern", for
/// more information about this pattern see http://martinfowler.com/eaaCatalog/repository.html
/// or http://blogs.msdn.com/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx
/// </summary>
/// <remarks>
/// Indeed, DbSet is already a generic repository and for Data-Driven apps
/// you might not need custom Repositories.
/// But using this interface allows us to ensure PI (Persistence Ignorance) principle
/// from the Domain and Application code
/// </remarks>
/// <typeparam name="TEntity">Type of entity for this repository </typeparam>
public interface IRepository<TEntity> : IDisposable
where TEntity : AggregateRoot //1:1 relationship between Repository and AggregateRoot
public interface IRepository
{
Task<int> Add(TEntity item);
Task<int> Remove(TEntity item);
Task<int> Update(TEntity item);
Task<TEntity> Get(Guid id);
IUnitOfWork UnitOfWork { get; }
}
}

src/Services/Ordering/Ordering.Domain/SeedWork/AggregateRoot.cs → src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs View File

@ -1,11 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
{
public class AggregateRoot : Entity
public interface IUnitOfWork : IDisposable
{
Task<int> CommitAsync();
}
}

+ 24
- 0
src/Services/Ordering/Ordering.SqlData/Queries/IOrderdingQueries.cs View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries
{
//The OrderingQueries Contracts/Interfaces could be moved to a third assembly
//We're not putting this contract in the Domain layer assembly because
//queries/joins are just Application's needs and should not be limited
//to the Domain Model restrictions (Aggregates and Repositories restrictions).
//
//In this case we're using the same EF Context but another good approach
//is also to simply use SQL sentences for the queries with any Micro-ORM (like Dapper) or even just ADO.NET
//
//The point is that Queries are IDEMPOTENT and don't need to commit to DDD Domain restrictions
//so could be implemented in a completely orthogonal way in regards the Domain Layer (à la CQRS)
public interface IOrderdingQueries
{
Task<dynamic> GetAllOrdersIncludingValueObjectsAndChildEntities();
Task<dynamic> GetOrderById(Guid orderId);
}
}

+ 8
- 1
src/Services/Ordering/Ordering.SqlData/Queries/OrderingQueries.cs View File

@ -7,10 +7,17 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries;
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries
{
public class OrderingQueries
//In this case, for the Application queries, we're using the same EF Context but another good approach
//is also to simply use SQL sentences for the queries with any Micro-ORM (like Dapper) or even just ADO.NET
//
//The point is that Queries are IDEMPOTENT and don't need to commit to DDD Domain restrictions
//so could be implemented in a completely orthogonal way in regards the Domain Layer (à la CQRS)
public class OrderingQueries : IOrderdingQueries
{
private OrderingDbContext _dbContext;


+ 28
- 17
src/Services/Ordering/Ordering.SqlData/Repositories/OrderRepository.cs View File

@ -3,37 +3,48 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.SeedWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Contracts;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
using Microsoft.EntityFrameworkCore;
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.Repositories
{
//1:1 relationship between Repository and Aggregate (i.e. OrderRepository and Order)
public class OrderRepository
: Repository<Order>, IOrderRepository
public class OrderRepository : IOrderRepository
{
public OrderRepository(OrderingDbContext unitOfWork)
: base(unitOfWork) { }
private readonly OrderingDbContext _context;
//TBD - To define Specific Actions Not In Base Repository class
public IUnitOfWork UnitOfWork => _context;
public async Task<int> Remove(Guid orderId)
public OrderRepository(OrderingDbContext orderingDbContext)
{
if (orderId == null)
return 0;
Order orderToDelete = await this.Get(orderId);
_context = orderingDbContext;
}
public void Add(Order order)
{
_context.Orders.Add(order);
}
//attach item if not exist
_unitOfWork.Attach(orderToDelete);
public void Update(Order order)
{
_context.Orders.Update(order);
}
//set as "removed"
_unitOfWork.Remove(orderToDelete);
public async Task Remove(Guid orderId)
{
var orderToRemove = await _context.Orders.Where(o => o.Id == orderId).SingleOrDefaultAsync();
_context.Orders.Remove(orderToRemove);
}
return await _unitOfWork.SaveChangesAsync();
public async Task<Order> FindAsync(Guid id)
{
if (id != Guid.Empty)
return await _context.Set<Order>().FirstOrDefaultAsync(o => o.Id == id);
else
return null;
}
}


+ 0
- 82
src/Services/Ordering/Ordering.SqlData/SeedWork/Repository.cs View File

@ -1,82 +0,0 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.SeedWork
{
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : AggregateRoot //1:1 relationship between Repository and AggregateRoot
{
protected readonly DbContext _unitOfWork;
//DbContext injected thru DI from ASP.NET Core bootstrap
public Repository(DbContext unitOfWork)
{
if (unitOfWork == null)
throw new ArgumentNullException("unitOfWork");
_unitOfWork = unitOfWork;
}
public DbContext UnitOfWork
{
get
{
return _unitOfWork;
}
}
public virtual async Task<int> Add(TEntity item)
{
if (item == null)
return 0;
_unitOfWork.Set<TEntity>().Add(item); // add new item in this set
return await _unitOfWork.SaveChangesAsync();
}
public virtual async Task<int> Remove(TEntity item)
{
if (item == null)
return 0;
//attach item if not exist
_unitOfWork.Set<TEntity>().Attach(item);
//set as "removed"
_unitOfWork.Set<TEntity>().Remove(item);
return await _unitOfWork.SaveChangesAsync();
}
public virtual async Task<int> Update(TEntity item)
{
if (item == null)
return 0;
_unitOfWork.Set<TEntity>().Update(item);
return await _unitOfWork.SaveChangesAsync();
}
public virtual async Task<TEntity> Get(Guid id)
{
if (id != Guid.Empty)
return await _unitOfWork.Set<TEntity>().FirstOrDefaultAsync(o => o.Id == id);
else
return null;
}
public void Dispose()
{
if (_unitOfWork != null)
_unitOfWork.Dispose();
}
}
}

+ 12
- 2
src/Services/Ordering/Ordering.SqlData/UnitOfWork/DBContextUtil.cs View File

@ -26,13 +26,23 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork
return builder.Options;
}
public static DbContextOptions<OrderingDbContext> CreateNewContextOptionsForSqlDB()
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
//var connString = @"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;";
//SQL SERVER on-premises
//(Integrated Security) var connString = @"Server=CESARDLBOOKVHD;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;";
//(SQL Server Authentication)
var connString = @"Server=CESARDLBOOKVHD;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;";
//SQL LOCALDB
builder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;");
builder.UseSqlServer(connString);
return builder.Options;
}


+ 35
- 2
src/Services/Ordering/Ordering.SqlData/UnitOfWork/OrderingDbContext.cs View File

@ -4,10 +4,11 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork
{
public class OrderingDbContext : DbContext
public class OrderingDbContext : DbContext, IUnitOfWork
{
public OrderingDbContext(DbContextOptions<OrderingDbContext> options)
: base(options)
@ -21,8 +22,19 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork
//and injected through DI later on. The following config is used when running Tests or similar contexts
if (!optionsBuilder.IsConfigured)
{
//SQL LocalDB
//var connString = @"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;";
//SQL SERVER on-premises
//(Integrated Security)
//var connString = @"Server=CESARDLBOOKVHD;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;";
//(SQL Server Authentication)
var connString = @"Server=CESARDLBOOKVHD;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;";
//SQL LOCALDB
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;");
optionsBuilder.UseSqlServer(connString);
}
@ -43,5 +55,26 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork
.HasDefaultValueSql("NEXT VALUE FOR shared.OrderSequences");
}
public async Task<int> CommitAsync()
{
int changes = 0;
try
{
//(CDLTLL) TBD
//RemoveOrphanedChilds();
changes = await base.SaveChangesAsync();
}
catch (Exception ex)
{
//(CDLTLL) TBD
//RejectChanges();
throw ex;
}
return changes;
}
}
}

+ 8
- 10
test/Services/Ordering.Test/DataIntegrationTests.cs View File

@ -1,15 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Contracts;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace DataIntegrationTests
{
@ -18,15 +14,15 @@ namespace DataIntegrationTests
public class Tests
{
[Fact]
public void Add_order_to_data_model()
public async void Add_order_to_data_model()
{
// All contexts that share the same service provider will share the same database
//Using InMemory DB
//var options = DbContextUtil.CreateNewContextOptionsForInMemoryDB();
//Using Sql LocalDB
var options = DbContextUtil.CreateNewContextOptionsForSqlDB();
//Using Sql Server
var options = DbContextUtil.CreateNewContextOptionsForSqlDb();
// Run the test against one instance of the context
using (var context = new OrderingDbContext(options))
@ -55,6 +51,7 @@ namespace DataIntegrationTests
order1.AddNewOrderItem(Guid.NewGuid(), 5, 3, 0);
orderRepository.Add(order1);
int numChanges = await orderRepository.UnitOfWork.CommitAsync();
//With no Async Repository
//context.Orders.Add(order1);
@ -69,7 +66,7 @@ namespace DataIntegrationTests
.Include(o => o.ShippingAddress)
.Include(o => o.BillingAddress)
.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
//OTHER SAMPLE
//var company = context.Companies
@ -80,7 +77,8 @@ namespace DataIntegrationTests
//Assert when running test with a clean In-Memory DB
//Assert.Equal(1, context.Orders.Count());
Assert.Equal("Redmond", orders.First<Order>().ShippingAddress.City);
string cityName = orders.First<Order>().ShippingAddress.City;
Assert.Equal("Redmond", cityName);
}
}


Loading…
Cancel
Save