Browse Source

Merge branch 'dev'

# Conflicts:
#	src/Services/Basket/Basket.API/Model/IBasketRepository.cs
#	src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs
#	src/Services/Catalog/Catalog.API/Startup.cs
#	src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs
#	src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs
#	test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs
pull/119/merge
dsanz 7 years ago
parent
commit
35cc2f9d7b
270 changed files with 29931 additions and 697 deletions
  1. +29
    -0
      _docker/rabbitmq/Dockerfile.nanowin
  2. +1
    -0
      _docker/rabbitmq/enabled_plugins
  3. +1
    -0
      _docker/rabbitmq/rabbitmq.config
  4. +30
    -0
      _docker/redis/Dockerfile.nanowin
  5. +5
    -0
      docker-compose-external.yml
  6. +79
    -0
      docker-compose-windows.override.yml
  7. +80
    -0
      docker-compose-windows.prod.yml
  8. +80
    -0
      docker-compose-windows.yml
  9. +18
    -2
      docker-compose.override.yml
  10. +12
    -2
      docker-compose.prod.yml
  11. +15
    -0
      docker-compose.vs.debug.yml
  12. +10
    -0
      docker-compose.vs.release.yml
  13. +13
    -0
      docker-compose.yml
  14. +380
    -0
      eShopOnContainers-ServicesAndWebApps.sln
  15. +222
    -10
      eShopOnContainers.sln
  16. +14
    -0
      src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs
  17. +18
    -0
      src/BuildingBlocks/EventBus/EventBus/Abstractions/IIntegrationEventHandler.cs
  18. +17
    -0
      src/BuildingBlocks/EventBus/EventBus/EventBus.csproj
  19. +18
    -0
      src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs
  20. +171
    -0
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
  21. +19
    -0
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj
  22. +13
    -0
      src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs
  23. +48
    -0
      src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogContext.cs
  24. +27
    -0
      src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj
  25. +28
    -0
      src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs
  26. +15
    -0
      src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs
  27. +53
    -0
      src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs
  28. +68
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckMiddleware.cs
  29. +38
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckStartupFilter.cs
  30. +36
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostBuilderExtension.cs
  31. +38
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostExtensions.cs
  32. +16
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/Microsoft.AspNetCore.HealthChecks.csproj
  33. +45
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.Data/HealthCheckBuilderDataExtensions.cs
  34. +19
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.Data/Microsoft.Extensions.HealthChecks.Data.csproj
  35. +13
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CheckStatus.cs
  36. +110
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/AddCheck.cs
  37. +55
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs
  38. +21
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/SystemChecks.cs
  39. +109
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs
  40. +83
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CompositeHealthCheckResult.cs
  41. +76
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheck.cs
  42. +40
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs
  43. +18
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckExtensions.cs
  44. +54
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResult.cs
  45. +12
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResults.cs
  46. +54
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckService.cs
  47. +22
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceCollectionExtensions.cs
  48. +38
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceExtensions.cs
  49. +16
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheck.cs
  50. +14
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckResult.cs
  51. +13
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckService.cs
  52. +92
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Internal/UrlChecker.cs
  53. +20
    -0
      src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Microsoft.Extensions.HealthChecks.csproj
  54. +45
    -0
      src/BuildingBlocks/HealthChecks/src/common/Guard.cs
  55. +18
    -10
      src/Services/Basket/Basket.API/Basket.API.csproj
  56. +2
    -0
      src/Services/Basket/Basket.API/BasketSettings.cs
  57. +8
    -0
      src/Services/Basket/Basket.API/Dockerfile.nanowin
  58. +18
    -0
      src/Services/Basket/Basket.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs
  59. +24
    -0
      src/Services/Basket/Basket.API/Infrastructure/Exceptions/BasketDomainException.cs
  60. +67
    -0
      src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs
  61. +46
    -0
      src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs
  62. +26
    -0
      src/Services/Basket/Basket.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs
  63. +1
    -0
      src/Services/Basket/Basket.API/Model/BasketItem.cs
  64. +1
    -0
      src/Services/Basket/Basket.API/Model/IBasketRepository.cs
  65. +34
    -4
      src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs
  66. +1
    -0
      src/Services/Basket/Basket.API/Program.cs
  67. +42
    -14
      src/Services/Basket/Basket.API/Startup.cs
  68. +29
    -17
      src/Services/Catalog/Catalog.API/Catalog.API.csproj
  69. +121
    -13
      src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs
  70. +8
    -0
      src/Services/Catalog/Catalog.API/Dockerfile.nanowin
  71. +18
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs
  72. +32
    -2
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs
  73. +4
    -4
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs
  74. +0
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103152832_Initial.Designer.cs
  75. +0
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103152832_Initial.cs
  76. +0
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103153420_UpdateTableNames.Designer.cs
  77. +0
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103153420_UpdateTableNames.cs
  78. +122
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170314083211_AddEventTable.Designer.cs
  79. +34
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170314083211_AddEventTable.cs
  80. +121
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316012921_RefactoringToIntegrationEventLog.Designer.cs
  81. +53
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316012921_RefactoringToIntegrationEventLog.cs
  82. +122
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316120022_RefactoringEventBusNamespaces.Designer.cs
  83. +19
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316120022_RefactoringEventBusNamespaces.cs
  84. +99
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.Designer.cs
  85. +34
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.cs
  86. +10
    -10
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/CatalogContextModelSnapshot.cs
  87. +24
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/Exceptions/CatalogDomainException.cs
  88. +63
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs
  89. +43
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.Designer.cs
  90. +34
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.cs
  91. +42
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextModelSnapshot.cs
  92. +3
    -0
      src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/AnyFutureIntegrationEventHandler.cs.txt
  93. +26
    -0
      src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs
  94. +1
    -0
      src/Services/Catalog/Catalog.API/Program.cs
  95. +44
    -14
      src/Services/Catalog/Catalog.API/Startup.cs
  96. +1
    -0
      src/Services/Catalog/Catalog.API/settings.cs
  97. +8
    -0
      src/Services/Identity/Identity.API/Dockerfile.nanowin
  98. +6
    -0
      src/Services/Identity/Identity.API/Identity.API.csproj
  99. +1
    -0
      src/Services/Identity/Identity.API/Program.cs
  100. +8
    -1
      src/Services/Identity/Identity.API/Startup.cs

+ 29
- 0
_docker/rabbitmq/Dockerfile.nanowin View File

@ -0,0 +1,29 @@
#https://github.com/spring2/dockerfiles/tree/master/rabbitmq
FROM microsoft/windowsservercore
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
ENV chocolateyUseWindowsCompression false
RUN iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1')); \
choco install -y curl;
RUN choco install -y erlang
ENV ERLANG_SERVICE_MANAGER_PATH="C:\Program Files\erl8.2\erts-8.2\bin"
RUN choco install -y rabbitmq
ENV RABBITMQ_SERVER="C:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.5"
ENV RABBITMQ_CONFIG_FILE="c:\rabbitmq"
COPY rabbitmq.config C:/
COPY rabbitmq.config C:/Users/ContainerAdministrator/AppData/Roaming/RabbitMQ/
COPY enabled_plugins C:/Users/ContainerAdministrator/AppData/Roaming/RabbitMQ/
EXPOSE 4369
EXPOSE 5672
EXPOSE 5671
EXPOSE 15672
WORKDIR C:/Program\ Files/RabbitMQ\ Server/rabbitmq_server-3.6.5/sbin
CMD .\rabbitmq-server.bat

+ 1
- 0
_docker/rabbitmq/enabled_plugins View File

@ -0,0 +1 @@
[rabbitmq_amqp1_0,rabbitmq_management].

+ 1
- 0
_docker/rabbitmq/rabbitmq.config View File

@ -0,0 +1 @@
[{rabbit, [{loopback_users, []}]}].

+ 30
- 0
_docker/redis/Dockerfile.nanowin View File

@ -0,0 +1,30 @@
# The MSI installs a service which is hard to override, so let's use a zip file.
FROM microsoft/windowsservercore
MAINTAINER alexellis2@gmail.com
SHELL ["powershell"]
RUN $ErrorActionPreference = 'Stop'; \
wget https://github.com/MSOpenTech/redis/releases/download/win-3.2.100/Redis-x64-3.2.100.zip -OutFile Redis-x64-3.2.100.zip ; \
Expand-Archive Redis-x64-3.2.100.zip -dest 'C:\\Program Files\\Redis\\' ; \
Remove-Item Redis-x64-3.2.100.zip -Force
RUN setx PATH '%PATH%;C:\\Program Files\\Redis\\'
WORKDIR 'C:\\Program Files\\Redis\\'
RUN Get-Content redis.windows.conf | Where { $_ -notmatch 'bind 127.0.0.1' } | Set-Content redis.openport.conf ; \
Get-Content redis.openport.conf | Where { $_ -notmatch 'protected-mode yes' } | Set-Content redis.unprotected.conf ; \
Add-Content redis.unprotected.conf 'protected-mode no' ; \
Add-Content redis.unprotected.conf 'bind 0.0.0.0' ; \
Get-Content redis.unprotected.conf
EXPOSE 6379
RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord
# Define our command to be run when launching the container
CMD .\\redis-server.exe .\\redis.unprotected.conf --port 6379 ; \
Write-Host Redis Started... ; \
while ($true) { Start-Sleep -Seconds 3600 }

+ 5
- 0
docker-compose-external.yml View File

@ -8,3 +8,8 @@ services:
image: redis
ports:
- "6379:6379"
rabbitmq:
image: rabbitmq
ports:
- "5672:5672"

+ 79
- 0
docker-compose-windows.override.yml View File

@ -0,0 +1,79 @@
version: '2.1'
# The default docker-compose.override file can use the "localhost" as the external name for testing web apps within the same dev machine.
# The ESHOP_EXTERNAL_DNS_NAME_OR_IP environment variable is taken, by default, from the ".env" file defined like:
# ESHOP_EXTERNAL_DNS_NAME_OR_IP=localhost
# but values present in the environment vars at runtime will always override those defined inside the .env file
# An external IP or DNS name has to be used (instead localhost and the 10.0.75.1 IP) when testing the Web apps and the Xamarin apps from remote machines/devices using the same WiFi, for instance.
services:
basket.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5103
- ConnectionString=basket.data
- identityUrl=http://identity.api:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq
ports:
- "5103:5103"
catalog.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5101
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word
- ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq
ports:
- "5101:5101"
identity.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5105
- SpaClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5104
- ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word
- MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your local dev-machine firewall at range 5100-5105.
ports:
- "5105:5105"
ordering.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5102
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
- identityUrl=http://identity.api:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq
ports:
- "5102:5102"
webspa:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5104
- CatalogUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101
- OrderingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102
- IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
ports:
- "5104:5104"
webmvc:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5100
- CatalogUrl=http://catalog.api:5101
- OrderingUrl=http://ordering.api:5102
- BasketUrl=http://basket.api:5103
- IdentityUrl=http://10.0.75.1:5105 #Local: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser.
#Remote: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.
ports:
- "5100:5100"
sql.data:
environment:
- SA_PASSWORD=Pass@word
- ACCEPT_EULA=Y
ports:
- "5433:1433"

+ 80
- 0
docker-compose-windows.prod.yml View File

@ -0,0 +1,80 @@
version: '2.1'
# The Production docker-compose file has to have the external/real IPs or DNS names for the services
# The ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP environment variable is taken, by default, from the ".env" file defined like:
# ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP=192.168.88.248
# but values present in the environment vars at runtime will always override those defined inside the .env file
# An external IP or DNS name has to be used when testing the Web apps and the Xamarin apps from remote machines/devices using the same WiFi, for instance.
#
# Set ASPNETCORE_ENVIRONMENT=Development to get errors while testing.
#
# You need to start it with the following CLI command:
# docker-compose -f docker-compose-windows.yml -f docker-compose-windows.prod.yml up -d
services:
basket.api:
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5103
- ConnectionString=basket.data
- identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
ports:
- "5103:5103"
catalog.api:
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5101
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word
- ExternalCatalogBaseUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
ports:
- "5101:5101"
identity.api:
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5105
- SpaClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5104
- ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word
- MvcClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your host's firewall at range 5100-5105.
ports:
- "5105:5105"
ordering.api:
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5102
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
- identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
ports:
- "5102:5102"
webspa:
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5104
- CatalogUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101
- OrderingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- BasketUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5103
ports:
- "5104:5104"
webmvc:
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5100
- CatalogUrl=http://catalog.api:5101
- OrderingUrl=http://ordering.api:5102
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
- BasketUrl=http://basket.api:5103
ports:
- "5100:5100"
sql.data:
environment:
- SA_PASSWORD=Pass@word
- ACCEPT_EULA=Y
ports:
- "5433:1433"

+ 80
- 0
docker-compose-windows.yml View File

@ -0,0 +1,80 @@
version: '2.1'
services:
basket.api:
image: eshop/basket.api
build:
context: ./src/Services/Basket/Basket.API
dockerfile: Dockerfile.nanowin
depends_on:
- basket.data
- identity.api
catalog.api:
image: eshop/catalog.api
build:
context: ./src/Services/Catalog/Catalog.API
dockerfile: Dockerfile.nanowin
depends_on:
- sql.data
identity.api:
image: eshop/identity.api
build:
context: ./src/Services/Identity/Identity.API
dockerfile: Dockerfile.nanowin
depends_on:
- sql.data
ordering.api:
image: eshop/ordering.api
build:
context: ./src/Services/Ordering/Ordering.API
dockerfile: Dockerfile.nanowin
depends_on:
- sql.data
webspa:
image: eshop/webspa
build:
context: ./src/Web/WebSPA
dockerfile: Dockerfile.nanowin
depends_on:
- identity.api
- basket.api
webmvc:
image: eshop/webmvc
build:
context: ./src/Web/WebMVC
dockerfile: Dockerfile.nanowin
depends_on:
- catalog.api
- ordering.api
- identity.api
- basket.api
sql.data:
image: microsoft/mssql-server-windows
basket.data:
image: redis
build:
context: ./_docker/redis
dockerfile: Dockerfile.nanowin
ports:
- "6379:6379"
rabbitmq:
image: rabbitmq
build:
context: ./_docker/rabbitmq
dockerfile: Dockerfile.nanowin
ports:
- "5672:5672"
networks:
default:
external:
name: nat

+ 18
- 2
docker-compose.override.yml View File

@ -14,6 +14,7 @@ services:
- ASPNETCORE_URLS=http://0.0.0.0:5103
- ConnectionString=basket.data
- identityUrl=http://identity.api:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq
ports:
- "5103:5103"
@ -22,7 +23,8 @@ services:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5101
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word
- ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq
ports:
- "5101:5101"
@ -42,6 +44,7 @@ services:
- ASPNETCORE_URLS=http://0.0.0.0:5102
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
- identityUrl=http://identity.api:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq
ports:
- "5102:5102"
@ -73,4 +76,17 @@ services:
- SA_PASSWORD=Pass@word
- ACCEPT_EULA=Y
ports:
- "5433:1433"
- "5433:1433"
webstatus:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:5107
- CatalogUrl=http://catalog.api:5101/hc
- OrderingUrl=http://ordering.api:5102/hc
- BasketUrl=http://basket.api:5103/hc
- IdentityUrl=http://10.0.75.1:5105/hc
- mvc=http://webmvc:5100/hc
- spa=http://webspa:5104/hc
ports:
- "5107:5107"

+ 12
- 2
docker-compose.prod.yml View File

@ -8,7 +8,7 @@ version: '2'
#
# Set ASPNETCORE_ENVIRONMENT=Development to get errors while testing.
#
# You need to start it with the following CLI command:
# You need to start it with the following CLI command:
# docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
services:
@ -77,4 +77,14 @@ services:
- SA_PASSWORD=Pass@word
- ACCEPT_EULA=Y
ports:
- "5433:1433"
- "5433:1433"
webstatus:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- CatalogUrl=http://catalog.api:5101/hc
- OrderingUrl=http://ordering.api:5102/hc
- BasketUrl=http://basket.api:5103/hc
- IdentityUrl=http://10.0.75.1:5105/hc
ports:
- "5107:5107"

+ 15
- 0
docker-compose.vs.debug.yml View File

@ -90,3 +90,18 @@ services:
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
webstatus:
image: eshop/webstatus:dev
build:
args:
source: ${DOCKER_BUILD_SOURCE}
environment:
- DOTNET_USE_POLLING_FILE_WATCHER=1
volumes:
- ./src/Web/WebStatus:/app
- ~/.nuget/packages:/root/.nuget/packages:ro
- ~/clrdbg:/clrdbg:ro
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"

+ 10
- 0
docker-compose.vs.release.yml View File

@ -60,3 +60,13 @@ services:
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
webstatus:
build:
args:
source: ${DOCKER_BUILD_SOURCE}
volumes:
- ~/clrdbg:/clrdbg:ro
entrypoint: tail -f /dev/null
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"

+ 13
- 0
docker-compose.yml View File

@ -9,6 +9,7 @@ services:
depends_on:
- basket.data
- identity.api
- rabbitmq
catalog.api:
image: eshop/catalog.api
@ -17,6 +18,7 @@ services:
dockerfile: Dockerfile
depends_on:
- sql.data
- rabbitmq
identity.api:
image: eshop/identity.api
@ -61,3 +63,14 @@ services:
image: redis
ports:
- "6379:6379"
rabbitmq:
image: rabbitmq
ports:
- "5672:5672"
webstatus:
image: eshop/webstatus
build:
context: ./src/Web/WebStatus
dockerfile: Dockerfile

+ 380
- 0
eShopOnContainers-ServicesAndWebApps.sln View File

@ -39,10 +39,24 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.Infrastructure", "src\Services\Ordering\Ordering.Infrastructure\Ordering.Infrastructure.csproj", "{95F1F07C-4D92-4742-BD07-E5B805AAB651}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "test\Services\UnitTest\UnitTest.csproj", "{7796F5D8-31FC-45A4-B673-19DE5BA194CF}"
ProjectSection(ProjectDependencies) = postProject
{A579E108-5445-403D-A407-339AC4D1611B} = {A579E108-5445-403D-A407-339AC4D1611B}
{F16E3C6A-1C94-4EAB-BE91-099618060B68} = {F16E3C6A-1C94-4EAB-BE91-099618060B68}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.API", "src\Services\Identity\Identity.API\Identity.API.csproj", "{A579E108-5445-403D-A407-339AC4D1611B}"
EndProject
Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{FEA0C318-FFED-4D39-8781-265718CA43DD}"
ProjectSection(ProjectDependencies) = postProject
{A579E108-5445-403D-A407-339AC4D1611B} = {A579E108-5445-403D-A407-339AC4D1611B}
{5B810E3D-112E-4857-B197-F09D2FD41E27} = {5B810E3D-112E-4857-B197-F09D2FD41E27}
{F16E3C6A-1C94-4EAB-BE91-099618060B68} = {F16E3C6A-1C94-4EAB-BE91-099618060B68}
{F0333D8E-0B27-42B7-B2C6-78F3657624E2} = {F0333D8E-0B27-42B7-B2C6-78F3657624E2}
{42681D9D-750A-4DF7-BD9F-9292CFD5C253} = {42681D9D-750A-4DF7-BD9F-9292CFD5C253}
{2110CBB0-3B38-4EE4-A743-DF6968D80D90} = {2110CBB0-3B38-4EE4-A743-DF6968D80D90}
{CFE2FACB-4538-4B99-8A10-306F3882952D} = {CFE2FACB-4538-4B99-8A10-306F3882952D}
{231226CE-690B-4979-8870-9A79D80928E2} = {231226CE-690B-4979-8870-9A79D80928E2}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSPA", "src\Web\WebSPA\WebSPA.csproj", "{F16E3C6A-1C94-4EAB-BE91-099618060B68}"
EndProject
@ -50,6 +64,26 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "test\Se
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "test\Services\FunctionalTests\FunctionalTests.csproj", "{CFE2FACB-4538-4B99-8A10-306F3882952D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{DB0EFB20-B024-4E5E-A75C-52143C131D25}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EventBus", "EventBus", "{807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBus", "src\BuildingBlocks\EventBus\EventBus\EventBus.csproj", "{0044B293-1DCC-4224-B948-00CF6DC7F510}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBusRabbitMQ", "src\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj", "{8088F3FC-6787-45FA-A924-816EC81CBFAC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationEventLogEF", "src\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj", "{9EE28E45-1533-472B-8267-56C48855BA0E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HealthChecks", "HealthChecks", "{A81ECBC2-6B00-4DCD-8388-469174033379}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj", "{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj", "{942ED6E8-0050-495F-A0EA-01E97F63760C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.Data", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj", "{7804FC60-23E6-490C-8E08-F9FEF829F184}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@ -544,6 +578,342 @@ Global
{CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|x64.Build.0 = Release|Any CPU
{CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|x86.ActiveCfg = Release|Any CPU
{CFE2FACB-4538-4B99-8A10-306F3882952D}.Release|x86.Build.0 = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|ARM.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|iPhone.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|x64.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|x64.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|x86.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.AppStore|x86.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|ARM.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|ARM.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|iPhone.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|x64.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|x64.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|x86.ActiveCfg = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Debug|x86.Build.0 = Debug|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|Any CPU.Build.0 = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|ARM.ActiveCfg = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|ARM.Build.0 = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|iPhone.ActiveCfg = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|iPhone.Build.0 = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|x64.ActiveCfg = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|x64.Build.0 = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|x86.ActiveCfg = Release|Any CPU
{0044B293-1DCC-4224-B948-00CF6DC7F510}.Release|x86.Build.0 = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|ARM.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|iPhone.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|x64.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|x64.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|x86.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.AppStore|x86.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|ARM.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|ARM.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|iPhone.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|x64.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|x64.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|x86.ActiveCfg = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Debug|x86.Build.0 = Debug|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|Any CPU.Build.0 = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|ARM.ActiveCfg = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|ARM.Build.0 = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|iPhone.ActiveCfg = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|iPhone.Build.0 = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|x64.ActiveCfg = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|x64.Build.0 = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|x86.ActiveCfg = Release|Any CPU
{8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|x86.Build.0 = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|ARM.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|iPhone.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|x64.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|x64.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|x86.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|x86.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|ARM.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|iPhone.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|x64.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|x64.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|x86.ActiveCfg = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|x86.Build.0 = Debug|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|Any CPU.Build.0 = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|ARM.ActiveCfg = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|ARM.Build.0 = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|iPhone.ActiveCfg = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|iPhone.Build.0 = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x64.ActiveCfg = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x64.Build.0 = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.ActiveCfg = Release|Any CPU
{9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.Build.0 = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|ARM.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhone.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x64.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x64.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x86.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.AppStore|x86.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|ARM.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhone.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x64.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x64.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x86.ActiveCfg = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Debug|x86.Build.0 = Debug|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|Any CPU.Build.0 = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|ARM.ActiveCfg = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|ARM.Build.0 = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhone.ActiveCfg = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhone.Build.0 = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|x64.ActiveCfg = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|x64.Build.0 = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|x86.ActiveCfg = Release|Any CPU
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E}.Release|x86.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|ARM.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhone.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x64.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x64.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x86.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x86.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|ARM.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|ARM.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhone.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x64.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x64.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x86.ActiveCfg = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x86.Build.0 = Debug|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|Any CPU.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|ARM.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|ARM.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhone.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhone.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x64.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x64.Build.0 = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x86.ActiveCfg = Release|Any CPU
{942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x86.Build.0 = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|ARM.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhone.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x64.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x64.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x86.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.AppStore|x86.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|ARM.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|ARM.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhone.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x64.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x64.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x86.ActiveCfg = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Debug|x86.Build.0 = Debug|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|Any CPU.Build.0 = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|ARM.ActiveCfg = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|ARM.Build.0 = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhone.ActiveCfg = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhone.Build.0 = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|x64.ActiveCfg = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|x64.Build.0 = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|x86.ActiveCfg = Release|Any CPU
{7804FC60-23E6-490C-8E08-F9FEF829F184}.Release|x86.Build.0 = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|ARM.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|iPhone.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|x64.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|x64.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|x86.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.AppStore|x86.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|ARM.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|ARM.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|iPhone.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|x64.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|x64.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|x86.ActiveCfg = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Debug|x86.Build.0 = Debug|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|Any CPU.Build.0 = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|ARM.ActiveCfg = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|ARM.Build.0 = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|iPhone.ActiveCfg = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|iPhone.Build.0 = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x64.ActiveCfg = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x64.Build.0 = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.ActiveCfg = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -567,5 +937,15 @@ Global
{F16E3C6A-1C94-4EAB-BE91-099618060B68} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
{5B810E3D-112E-4857-B197-F09D2FD41E27} = {EF0337F2-ED00-4643-89FD-EE10863F1870}
{CFE2FACB-4538-4B99-8A10-306F3882952D} = {EF0337F2-ED00-4643-89FD-EE10863F1870}
{DB0EFB20-B024-4E5E-A75C-52143C131D25} = {932D8224-11F6-4D07-B109-DA28AD288A63}
{807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{0044B293-1DCC-4224-B948-00CF6DC7F510} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
{8088F3FC-6787-45FA-A924-816EC81CBFAC} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
{9EE28E45-1533-472B-8267-56C48855BA0E} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F}
{A81ECBC2-6B00-4DCD-8388-469174033379} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{DF8367F8-E6BD-4D07-99D2-E416BF8AB01E} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{942ED6E8-0050-495F-A0EA-01E97F63760C} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{7804FC60-23E6-490C-8E08-F9FEF829F184} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
EndGlobalSection
EndGlobal

+ 222
- 10
eShopOnContainers.sln View File

@ -1,18 +1,10 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26213.1
VisualStudioVersion = 15.0.26228.9
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3AF739CD-81D8-428D-A08A-0A58372DEBF6}"
ProjectSection(SolutionItems) = preProject
docker-compose.override.yml = docker-compose.override.yml
docker-compose.yml = docker-compose.yml
global.json = global.json
NuGet.config = NuGet.config
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{91CF7717-08AB-4E65-B10E-0B426F01E2E8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web Apps", "Web Apps", "{E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}"
@ -49,7 +41,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared Code", "Shared Code"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{9CC7814B-72A6-465B-A61C-57B512DEE303}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSPA", "src\Web\WebSPA\WebSPA.csproj"", "{9842DB3A-1391-48C7-A49C-2FABD0A18AC2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSPA", "src\Web\WebSPA\WebSPA.csproj", "{9842DB3A-1391-48C7-A49C-2FABD0A18AC2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile Apps", "Mobile Apps", "{B7B1D395-4E06-4036-BE86-C216756B9367}"
EndProject
@ -75,6 +67,28 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "test\Services\U
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.API", "src\Services\Identity\Identity.API\Identity.API.csproj", "{A579E108-5445-403D-A407-339AC4D1611B}"
EndProject
Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{FEA0C318-FFED-4D39-8781-265718CA43DD}"
ProjectSection(ProjectDependencies) = postProject
{A579E108-5445-403D-A407-339AC4D1611B} = {A579E108-5445-403D-A407-339AC4D1611B}
{9842DB3A-1391-48C7-A49C-2FABD0A18AC2} = {9842DB3A-1391-48C7-A49C-2FABD0A18AC2}
{F0333D8E-0B27-42B7-B2C6-78F3657624E2} = {F0333D8E-0B27-42B7-B2C6-78F3657624E2}
{42681D9D-750A-4DF7-BD9F-9292CFD5C253} = {42681D9D-750A-4DF7-BD9F-9292CFD5C253}
{2110CBB0-3B38-4EE4-A743-DF6968D80D90} = {2110CBB0-3B38-4EE4-A743-DF6968D80D90}
{231226CE-690B-4979-8870-9A79D80928E2} = {231226CE-690B-4979-8870-9A79D80928E2}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{1EF3AC0F-F27C-46DD-AC53-D762D2C11C45}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EventBus", "EventBus", "{B473B70F-0796-4862-B1AD-BB742D93B868}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBus", "src\BuildingBlocks\EventBus\EventBus\EventBus.csproj", "{3D6B7A87-162E-4479-B256-1291BEB503B6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBusRabbitMQ", "src\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj", "{52AF222A-258C-4032-ACDD-857D7251BC1E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationEventLogEF", "src\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj", "{438B774F-5569-4DE2-AA62-3F8BAEB31C55}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "test\Services\FunctionalTests\FunctionalTests.csproj", "{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@ -1004,6 +1018,198 @@ Global
{FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x64.Build.0 = Release|Any CPU
{FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x86.ActiveCfg = Release|Any CPU
{FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x86.Build.0 = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|ARM.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|iPhone.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|x64.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|x64.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|x86.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|x86.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|ARM.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|ARM.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|iPhone.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|x64.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|x64.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|x86.ActiveCfg = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|x86.Build.0 = Debug|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|Any CPU.Build.0 = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|ARM.ActiveCfg = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|ARM.Build.0 = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|iPhone.ActiveCfg = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|iPhone.Build.0 = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|x64.ActiveCfg = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|x64.Build.0 = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|x86.ActiveCfg = Release|Any CPU
{3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|x86.Build.0 = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|ARM.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|iPhone.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|x64.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|x64.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|x86.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|x86.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|ARM.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|iPhone.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|x64.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|x64.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|x86.ActiveCfg = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|x86.Build.0 = Debug|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|Any CPU.Build.0 = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|ARM.ActiveCfg = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|ARM.Build.0 = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|iPhone.ActiveCfg = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|iPhone.Build.0 = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|x64.ActiveCfg = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|x64.Build.0 = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|x86.ActiveCfg = Release|Any CPU
{52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|x86.Build.0 = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|ARM.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|iPhone.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|x64.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|x64.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|x86.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|x86.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|ARM.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|ARM.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|iPhone.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|x64.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|x64.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|x86.ActiveCfg = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|x86.Build.0 = Debug|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|Any CPU.Build.0 = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|ARM.ActiveCfg = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|ARM.Build.0 = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|iPhone.ActiveCfg = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|iPhone.Build.0 = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|x64.ActiveCfg = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|x64.Build.0 = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|x86.ActiveCfg = Release|Any CPU
{438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|x86.Build.0 = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|ARM.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|iPhone.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|x64.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|x64.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|x86.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|x86.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|ARM.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|ARM.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|iPhone.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|x64.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|x64.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|x86.ActiveCfg = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|x86.Build.0 = Debug|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|Any CPU.Build.0 = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|ARM.ActiveCfg = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|ARM.Build.0 = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|iPhone.ActiveCfg = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|iPhone.Build.0 = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|x64.ActiveCfg = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|x64.Build.0 = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|x86.ActiveCfg = Release|Any CPU
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1039,5 +1245,11 @@ Global
{67F9D3A8-F71E-4428-913F-C37AE82CDB24} = {778289CA-31F7-4464-8C2A-612EE846F8A7}
{7796F5D8-31FC-45A4-B673-19DE5BA194CF} = {EF0337F2-ED00-4643-89FD-EE10863F1870}
{A579E108-5445-403D-A407-339AC4D1611B} = {02DF7FEE-C302-433D-A6CD-237A2569F236}
{1EF3AC0F-F27C-46DD-AC53-D762D2C11C45} = {932D8224-11F6-4D07-B109-DA28AD288A63}
{B473B70F-0796-4862-B1AD-BB742D93B868} = {1EF3AC0F-F27C-46DD-AC53-D762D2C11C45}
{3D6B7A87-162E-4479-B256-1291BEB503B6} = {B473B70F-0796-4862-B1AD-BB742D93B868}
{52AF222A-258C-4032-ACDD-857D7251BC1E} = {B473B70F-0796-4862-B1AD-BB742D93B868}
{438B774F-5569-4DE2-AA62-3F8BAEB31C55} = {B473B70F-0796-4862-B1AD-BB742D93B868}
{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357} = {EF0337F2-ED00-4643-89FD-EE10863F1870}
EndGlobalSection
EndGlobal

+ 14
- 0
src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs View File

@ -0,0 +1,14 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions
{
public interface IEventBus
{
void Subscribe<T>(IIntegrationEventHandler<T> handler) where T: IntegrationEvent;
void Unsubscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEvent;
void Publish(IntegrationEvent @event);
}
}

+ 18
- 0
src/BuildingBlocks/EventBus/EventBus/Abstractions/IIntegrationEventHandler.cs View File

@ -0,0 +1,18 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions
{
public interface IIntegrationEventHandler<in TIntegrationEvent> : IIntegrationEventHandler
where TIntegrationEvent: IntegrationEvent
{
Task Handle(TIntegrationEvent @event);
}
public interface IIntegrationEventHandler
{
}
}

+ 17
- 0
src/BuildingBlocks/EventBus/EventBus/EventBus.csproj View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<RuntimeFrameworkVersion>1.1.0</RuntimeFrameworkVersion>
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.EventBus</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Folder Include="Abstractions\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
</ItemGroup>
</Project>

+ 18
- 0
src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events
{
public class IntegrationEvent
{
public IntegrationEvent()
{
Id = Guid.NewGuid();
CreationDate = DateTime.UtcNow;
}
public Guid Id { get; }
public DateTime CreationDate { get; }
}
}

+ 171
- 0
src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs View File

@ -0,0 +1,171 @@

using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
{
public class EventBusRabbitMQ : IEventBus, IDisposable
{
private readonly string _brokerName = "eshop_event_bus";
private readonly string _connectionString;
private readonly Dictionary<string, List<IIntegrationEventHandler>> _handlers;
private readonly List<Type> _eventTypes;
private IModel _model;
private IConnection _connection;
private string _queueName;
public EventBusRabbitMQ(string connectionString)
{
_connectionString = connectionString;
_handlers = new Dictionary<string, List<IIntegrationEventHandler>>();
_eventTypes = new List<Type>();
}
public void Publish(IntegrationEvent @event)
{
var eventName = @event.GetType().Name;
var factory = new ConnectionFactory() { HostName = _connectionString };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: _brokerName,
type: "direct");
string message = JsonConvert.SerializeObject(@event);
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: _brokerName,
routingKey: eventName,
basicProperties: null,
body: body);
}
}
public void Subscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEvent
{
var eventName = typeof(T).Name;
if (_handlers.ContainsKey(eventName))
{
_handlers[eventName].Add(handler);
}
else
{
var channel = GetChannel();
channel.QueueBind(queue: _queueName,
exchange: _brokerName,
routingKey: eventName);
_handlers.Add(eventName, new List<IIntegrationEventHandler>());
_handlers[eventName].Add(handler);
_eventTypes.Add(typeof(T));
}
}
public void Unsubscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEvent
{
var eventName = typeof(T).Name;
if (_handlers.ContainsKey(eventName) && _handlers[eventName].Contains(handler))
{
_handlers[eventName].Remove(handler);
if (_handlers[eventName].Count == 0)
{
_handlers.Remove(eventName);
var eventType = _eventTypes.Single(e => e.Name == eventName);
_eventTypes.Remove(eventType);
_model.QueueUnbind(queue: _queueName,
exchange: _brokerName,
routingKey: eventName);
if (_handlers.Keys.Count == 0)
{
_queueName = string.Empty;
_model.Dispose();
_connection.Dispose();
}
}
}
}
public void Dispose()
{
_handlers.Clear();
_model?.Dispose();
_connection?.Dispose();
}
private IModel GetChannel()
{
if (_model != null)
{
return _model;
}
else
{
(_model, _connection) = CreateConnection();
return _model;
}
}
private (IModel model, IConnection connection) CreateConnection()
{
var factory = new ConnectionFactory() { HostName = _connectionString };
var con = factory.CreateConnection();
var channel = con.CreateModel();
channel.ExchangeDeclare(exchange: _brokerName,
type: "direct");
if (string.IsNullOrEmpty(_queueName))
{
_queueName = channel.QueueDeclare().QueueName;
}
var consumer = new EventingBasicConsumer(channel);
consumer.Received += async (model, ea) =>
{
var eventName = ea.RoutingKey;
var message = Encoding.UTF8.GetString(ea.Body);
await ProcessEvent(eventName, message);
};
channel.BasicConsume(queue: _queueName,
noAck: true,
consumer: consumer);
return (channel, con);
}
private async Task ProcessEvent(string eventName, string message)
{
if (_handlers.ContainsKey(eventName))
{
Type eventType = _eventTypes.Single(t => t.Name == eventName);
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
var handlers = _handlers[eventName];
foreach (var handler in handlers)
{
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
}
}
}
}
}

+ 19
- 0
src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<RuntimeFrameworkVersion>1.1.0</RuntimeFrameworkVersion>
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="RabbitMQ.Client" Version="4.1.1" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EventBus\EventBus.csproj" />
</ItemGroup>
</Project>

+ 13
- 0
src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF
{
public enum EventStateEnum
{
NotPublished = 0,
Published = 1,
PublishedFailed = 2
}
}

+ 48
- 0
src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogContext.cs View File

@ -0,0 +1,48 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF
{
public class IntegrationEventLogContext : DbContext
{
public IntegrationEventLogContext(DbContextOptions<IntegrationEventLogContext> options) : base(options)
{
}
public DbSet<IntegrationEventLogEntry> IntegrationEventLogs { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<IntegrationEventLogEntry>(ConfigureIntegrationEventLogEntry);
}
void ConfigureIntegrationEventLogEntry(EntityTypeBuilder<IntegrationEventLogEntry> builder)
{
builder.ToTable("IntegrationEventLog");
builder.HasKey(e => e.EventId);
builder.Property(e => e.EventId)
.IsRequired();
builder.Property(e => e.Content)
.IsRequired();
builder.Property(e => e.CreationTime)
.IsRequired();
builder.Property(e => e.State)
.IsRequired();
builder.Property(e => e.TimesSent)
.IsRequired();
builder.Property(e => e.EventTypeName)
.IsRequired();
}
}
}

+ 27
- 0
src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<RuntimeFrameworkVersion>1.1.0</RuntimeFrameworkVersion>
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EventBus\EventBus.csproj" />
</ItemGroup>
</Project>

+ 28
- 0
src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF
{
public class IntegrationEventLogEntry
{
private IntegrationEventLogEntry() { }
public IntegrationEventLogEntry(IntegrationEvent @event)
{
EventId = @event.Id;
CreationTime = @event.CreationDate;
EventTypeName = @event.GetType().FullName;
Content = JsonConvert.SerializeObject(@event);
State = EventStateEnum.NotPublished;
TimesSent = 0;
}
public Guid EventId { get; private set; }
public string EventTypeName { get; private set; }
public EventStateEnum State { get; set; }
public int TimesSent { get; set; }
public DateTime CreationTime { get; private set; }
public string Content { get; private set; }
}
}

+ 15
- 0
src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs View File

@ -0,0 +1,15 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services
{
public interface IIntegrationEventLogService
{
Task SaveEventAsync(IntegrationEvent @event, DbTransaction transaction);
Task MarkEventAsPublishedAsync(IntegrationEvent @event);
}
}

+ 53
- 0
src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs View File

@ -0,0 +1,53 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
using System;
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services
{
public class IntegrationEventLogService : IIntegrationEventLogService
{
private readonly IntegrationEventLogContext _integrationEventLogContext;
private readonly DbConnection _dbConnection;
public IntegrationEventLogService(DbConnection dbConnection)
{
_dbConnection = dbConnection?? throw new ArgumentNullException("dbConnection");
_integrationEventLogContext = new IntegrationEventLogContext(
new DbContextOptionsBuilder<IntegrationEventLogContext>()
.UseSqlServer(_dbConnection)
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning))
.Options);
}
public Task SaveEventAsync(IntegrationEvent @event, DbTransaction transaction)
{
if(transaction == null)
{
throw new ArgumentNullException("transaction", $"A {typeof(DbTransaction).FullName} is required as a pre-requisite to save the event.");
}
var eventLogEntry = new IntegrationEventLogEntry(@event);
_integrationEventLogContext.Database.UseTransaction(transaction);
_integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry);
return _integrationEventLogContext.SaveChangesAsync();
}
public Task MarkEventAsPublishedAsync(IntegrationEvent @event)
{
var eventLogEntry = _integrationEventLogContext.IntegrationEventLogs.Single(ie => ie.EventId == @event.Id);
eventLogEntry.TimesSent++;
eventLogEntry.State = EventStateEnum.Published;
_integrationEventLogContext.IntegrationEventLogs.Update(eventLogEntry);
return _integrationEventLogContext.SaveChangesAsync();
}
}
}

+ 68
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckMiddleware.cs View File

@ -0,0 +1,68 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.HealthChecks;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.HealthChecks
{
public class HealthCheckMiddleware
{
private RequestDelegate _next;
private string _path;
private int? _port;
private IHealthCheckService _service;
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, int port)
{
_port = port;
_service = service;
_next = next;
}
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, string path)
{
_path = path;
_service = service;
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (IsHealthCheckRequest(context))
{
var result = await _service.CheckHealthAsync();
var status = result.CheckStatus;
if (status != CheckStatus.Healthy)
context.Response.StatusCode = 503;
context.Response.Headers.Add("content-type", "application/json");
await context.Response.WriteAsync(JsonConvert.SerializeObject(new { status = status.ToString() }));
return;
}
else
{
await _next.Invoke(context);
}
}
private bool IsHealthCheckRequest(HttpContext context)
{
if (_port.HasValue)
{
var connInfo = context.Features.Get<IHttpConnectionFeature>();
if (connInfo.LocalPort == _port)
return true;
}
if (context.Request.Path == _path)
return true;
return false;
}
}
}

+ 38
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckStartupFilter.cs View File

@ -0,0 +1,38 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
namespace Microsoft.AspNetCore.HealthChecks
{
public class HealthCheckStartupFilter : IStartupFilter
{
private string _path;
private int? _port;
public HealthCheckStartupFilter(int port)
{
_port = port;
}
public HealthCheckStartupFilter(string path)
{
_path = path;
}
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
if (_port.HasValue)
app.UseMiddleware<HealthCheckMiddleware>(_port);
else
app.UseMiddleware<HealthCheckMiddleware>(_path);
next(app);
};
}
}
}

+ 36
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostBuilderExtension.cs View File

@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.HealthChecks;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Hosting
{
public static class HealthCheckWebHostBuilderExtension
{
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, int port)
{
Guard.ArgumentValid(port > 0 && port < 65536, nameof(port), "Port must be a value between 1 and 65535");
builder.ConfigureServices(services =>
{
var existingUrl = builder.GetSetting(WebHostDefaults.ServerUrlsKey);
builder.UseSetting(WebHostDefaults.ServerUrlsKey, $"{existingUrl};http://localhost:{port}");
services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(port));
});
return builder;
}
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, string path)
{
Guard.ArgumentNotNull(nameof(path), path);
// REVIEW: Is there a better URL path validator somewhere?
Guard.ArgumentValid(!path.Contains("?"), nameof(path), "Path cannot contain query string values");
Guard.ArgumentValid(path.StartsWith("/"), nameof(path), "Path should start with /");
builder.ConfigureServices(services => services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(path)));
return builder;
}
}
}

+ 38
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostExtensions.cs View File

@ -0,0 +1,38 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.HealthChecks;
namespace Microsoft.AspNetCore.Hosting
{
public static class HealthCheckWebHostExtensions
{
private const int DEFAULT_TIMEOUT_SECONDS = 300;
public static void RunWhenHealthy(this IWebHost webHost)
{
webHost.RunWhenHealthy(TimeSpan.FromSeconds(DEFAULT_TIMEOUT_SECONDS));
}
public static void RunWhenHealthy(this IWebHost webHost, TimeSpan timeout)
{
var healthChecks = webHost.Services.GetService(typeof(IHealthCheckService)) as IHealthCheckService;
var loops = 0;
do
{
var checkResult = healthChecks.CheckHealthAsync().Result;
if (checkResult.CheckStatus == CheckStatus.Healthy)
{
webHost.Run();
break;
}
System.Threading.Thread.Sleep(1000);
loops++;
} while (loops < timeout.TotalSeconds);
}
}
}

+ 16
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/Microsoft.AspNetCore.HealthChecks.csproj View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="1.0.2" />
<ProjectReference Include="..\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
</ItemGroup>
</Project>

+ 45
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.Data/HealthCheckBuilderDataExtensions.cs View File

@ -0,0 +1,45 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Data;
using System.Data.SqlClient;
namespace Microsoft.Extensions.HealthChecks
{
public static class HealthCheckBuilderDataExtensions
{
public static HealthCheckBuilder AddSqlCheck(this HealthCheckBuilder builder, string name, string connectionString)
{
builder.AddCheck($"SqlCheck({name})", async () =>
{
try
{
//TODO: There is probably a much better way to do this.
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = "SELECT 1";
var result = (int)await command.ExecuteScalarAsync().ConfigureAwait(false);
if (result == 1)
{
return HealthCheckResult.Healthy($"SqlCheck({name}): Healthy");
}
return HealthCheckResult.Unhealthy($"SqlCheck({name}): Unhealthy");
}
}
}
catch(Exception ex)
{
return HealthCheckResult.Unhealthy($"SqlCheck({name}): Exception during check: {ex.GetType().FullName}");
}
});
return builder;
}
}
}

+ 19
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.Data/Microsoft.Extensions.HealthChecks.Data.csproj View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Data.SqlClient" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
</ItemGroup>
</Project>

+ 13
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CheckStatus.cs View File

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.Extensions.HealthChecks
{
public enum CheckStatus
{
Unknown,
Unhealthy,
Healthy,
Warning
}
}

+ 110
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/AddCheck.cs View File

@ -0,0 +1,110 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks
{
public static partial class HealthCheckBuilderExtensions
{
// Lambda versions of AddCheck for Func/Func<Task>/Func<ValueTask>
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<IHealthCheckResult> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromCheck(check, builder.DefaultCacheDuration));
return builder;
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromCheck(check, builder.DefaultCacheDuration));
return builder;
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<IHealthCheckResult> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromCheck(check, cacheDuration));
return builder;
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, IHealthCheckResult> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromCheck(check, cacheDuration));
return builder;
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromTaskCheck(check, builder.DefaultCacheDuration));
return builder;
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromTaskCheck(check, builder.DefaultCacheDuration));
return builder;
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromTaskCheck(check, cacheDuration));
return builder;
}
public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromTaskCheck(check, cacheDuration));
return builder;
}
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, builder.DefaultCacheDuration));
return builder;
}
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, builder.DefaultCacheDuration));
return builder;
}
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, cacheDuration));
return builder;
}
public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(builder), builder);
builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check, cacheDuration));
return builder;
}
}
}

+ 55
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs View File

@ -0,0 +1,55 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.Extensions.HealthChecks
{
public static partial class HealthCheckBuilderExtensions
{
// Numeric checks
public static HealthCheckBuilder AddMinValueCheck<T>(this HealthCheckBuilder builder, string name, T minValue, Func<T> currentValueFunc)
where T : IComparable<T>
{
Guard.ArgumentNotNull(nameof(builder), builder);
Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc);
builder.AddCheck(name, () =>
{
var currentValue = currentValueFunc();
var status = currentValue.CompareTo(minValue) >= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy;
return HealthCheckResult.FromStatus(
status,
$"{name}: min={minValue}, current={currentValue}",
new Dictionary<string, object> { { "min", minValue }, { "current", currentValue } }
);
});
return builder;
}
public static HealthCheckBuilder AddMaxValueCheck<T>(this HealthCheckBuilder builder, string name, T maxValue, Func<T> currentValueFunc)
where T : IComparable<T>
{
Guard.ArgumentNotNull(nameof(builder), builder);
Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc);
builder.AddCheck($"{name}", () =>
{
var currentValue = currentValueFunc();
var status = currentValue.CompareTo(maxValue) <= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy;
return HealthCheckResult.FromStatus(
status,
$"{name}: max={maxValue}, current={currentValue}",
new Dictionary<string, object> { { "max", maxValue }, { "current", currentValue } }
);
});
return builder;
}
}
}

+ 21
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/SystemChecks.cs View File

@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
namespace Microsoft.Extensions.HealthChecks
{
public static partial class HealthCheckBuilderExtensions
{
// System checks
public static HealthCheckBuilder AddPrivateMemorySizeCheck(this HealthCheckBuilder builder, long maxSize)
=> AddMaxValueCheck(builder, $"PrivateMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().PrivateMemorySize64);
public static HealthCheckBuilder AddVirtualMemorySizeCheck(this HealthCheckBuilder builder, long maxSize)
=> AddMaxValueCheck(builder, $"VirtualMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().VirtualMemorySize64);
public static HealthCheckBuilder AddWorkingSetCheck(this HealthCheckBuilder builder, long maxSize)
=> AddMaxValueCheck(builder, $"WorkingSet({maxSize})", maxSize, () => Process.GetCurrentProcess().WorkingSet64);
}
}

+ 109
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs View File

@ -0,0 +1,109 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.HealthChecks.Internal;
namespace Microsoft.Extensions.HealthChecks
{
public static partial class HealthCheckBuilderExtensions
{
// URL checks
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url)
=> AddUrlCheck(builder, url, response => UrlChecker.DefaultUrlCheck(response));
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url,
Func<HttpResponseMessage, IHealthCheckResult> checkFunc)
{
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
return AddUrlCheck(builder, url, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
}
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url,
Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc)
{
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
return AddUrlCheck(builder, url, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
}
public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url,
Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
{
Guard.ArgumentNotNull(nameof(builder), builder);
Guard.ArgumentNotNullOrWhitespace(nameof(url), url);
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
var urlCheck = new UrlChecker(checkFunc, url);
builder.AddCheck($"UrlCheck({url})", () => urlCheck.CheckAsync());
return builder;
}
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName)
=> AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => UrlChecker.DefaultUrlCheck(response));
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
Func<HttpResponseMessage, IHealthCheckResult> checkFunc)
{
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
return AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
}
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc)
{
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
return AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
}
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
{
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
return AddUrlChecks(builder, urlItems, groupName, CheckStatus.Warning, response => checkFunc(response));
}
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
CheckStatus partialSuccessStatus)
=> AddUrlChecks(builder, urlItems, groupName, partialSuccessStatus, response => UrlChecker.DefaultUrlCheck(response));
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
CheckStatus partialSuccessStatus, Func<HttpResponseMessage, IHealthCheckResult> checkFunc)
{
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
return AddUrlChecks(builder, urlItems, groupName, partialSuccessStatus, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
}
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
CheckStatus partialSuccessStatus, Func<HttpResponseMessage, Task<IHealthCheckResult>> checkFunc)
{
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
return AddUrlChecks(builder, urlItems, groupName, partialSuccessStatus, response => new ValueTask<IHealthCheckResult>(checkFunc(response)));
}
public static HealthCheckBuilder AddUrlChecks(this HealthCheckBuilder builder, IEnumerable<string> urlItems, string groupName,
CheckStatus partialSuccessStatus, Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc)
{
var urls = urlItems?.ToArray();
Guard.ArgumentNotNull(nameof(builder), builder);
Guard.ArgumentNotNullOrEmpty(nameof(urlItems), urls);
Guard.ArgumentNotNullOrWhitespace(nameof(groupName), groupName);
var urlChecker = new UrlChecker(checkFunc, urls) { PartiallyHealthyStatus = partialSuccessStatus };
builder.AddCheck($"UrlChecks({groupName})", () => urlChecker.CheckAsync());
return builder;
}
}
}

+ 83
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CompositeHealthCheckResult.cs View File

@ -0,0 +1,83 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Extensions.HealthChecks
{
// REVIEW: Does this need to be thread safe?
/// <summary>
/// Represents a composite health check result built from several results.
/// </summary>
public class CompositeHealthCheckResult : IHealthCheckResult
{
private static readonly IReadOnlyDictionary<string, object> _emptyData = new Dictionary<string, object>();
private readonly CheckStatus _initialStatus;
private readonly CheckStatus _partiallyHealthyStatus;
private readonly Dictionary<string, IHealthCheckResult> _results = new Dictionary<string, IHealthCheckResult>(StringComparer.OrdinalIgnoreCase);
public CompositeHealthCheckResult(CheckStatus partiallyHealthyStatus = CheckStatus.Warning,
CheckStatus initialStatus = CheckStatus.Unknown)
{
_partiallyHealthyStatus = partiallyHealthyStatus;
_initialStatus = initialStatus;
}
public CheckStatus CheckStatus
{
get
{
var checkStatuses = new HashSet<CheckStatus>(_results.Select(x => x.Value.CheckStatus));
if (checkStatuses.Count == 0)
return _initialStatus;
if (checkStatuses.Count == 1)
return checkStatuses.First();
if (checkStatuses.Contains(CheckStatus.Healthy))
return _partiallyHealthyStatus;
return CheckStatus.Unhealthy;
}
}
public string Description => string.Join(Environment.NewLine, _results.Select(r => r.Value.Description));
public IReadOnlyDictionary<string, object> Data
{
get
{
var result = new Dictionary<string, object>();
foreach (var kvp in _results)
result.Add(kvp.Key, kvp.Value.Data);
return result;
}
}
public IReadOnlyDictionary<string, IHealthCheckResult> Results => _results;
// REVIEW: Should description be required? Seems redundant for success checks.
public void Add(string name, CheckStatus status, string description)
=> Add(name, status, description, null);
public void Add(string name, CheckStatus status, string description, Dictionary<string, object> data)
{
Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
Guard.ArgumentValid(status != CheckStatus.Unknown, nameof(status), "Cannot add unknown status to composite health check result");
Guard.ArgumentNotNullOrWhitespace(nameof(description), description);
_results.Add(name, HealthCheckResult.FromStatus(status, description, data));
}
public void Add(string name, IHealthCheckResult checkResult)
{
Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
Guard.ArgumentNotNull(nameof(checkResult), checkResult);
_results.Add(name, checkResult);
}
}
}

+ 76
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheck.cs View File

@ -0,0 +1,76 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks
{
public class HealthCheck : IHealthCheck
{
private DateTimeOffset _cacheExpiration;
private IHealthCheckResult _cachedResult;
private volatile int _writerCount;
protected HealthCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
{
Guard.ArgumentNotNull(nameof(check), check);
Guard.ArgumentValid(cacheDuration >= TimeSpan.Zero, nameof(cacheDuration), "Cache duration must either be zero (disabled) or a positive value");
Check = check;
CacheDuration = cacheDuration;
}
public TimeSpan CacheDuration { get; }
protected Func<CancellationToken, ValueTask<IHealthCheckResult>> Check { get; }
protected virtual DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
public async ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken)
{
while (_cacheExpiration <= UtcNow)
{
// Can't use a standard lock here because of async, so we'll use this flag to determine when we should write a value,
// and the waiters who aren't allowed to write will just spin wait for the new value.
if (Interlocked.Exchange(ref _writerCount, 1) != 0)
{
await Task.Delay(5, cancellationToken).ConfigureAwait(false);
continue;
}
try
{
_cachedResult = await Check(cancellationToken).ConfigureAwait(false);
_cacheExpiration = UtcNow + CacheDuration;
break;
}
finally
{
_writerCount = 0;
}
}
return _cachedResult;
}
public static HealthCheck FromCheck(Func<IHealthCheckResult> check, TimeSpan cacheDuration)
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()), cacheDuration);
public static HealthCheck FromCheck(Func<CancellationToken, IHealthCheckResult> check, TimeSpan cacheDuration)
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)), cacheDuration);
public static HealthCheck FromTaskCheck(Func<Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check()), cacheDuration);
public static HealthCheck FromTaskCheck(Func<CancellationToken, Task<IHealthCheckResult>> check, TimeSpan cacheDuration)
=> new HealthCheck(token => new ValueTask<IHealthCheckResult>(check(token)), cacheDuration);
public static HealthCheck FromValueTaskCheck(Func<ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
=> new HealthCheck(token => check(), cacheDuration);
public static HealthCheck FromValueTaskCheck(Func<CancellationToken, ValueTask<IHealthCheckResult>> check, TimeSpan cacheDuration)
=> new HealthCheck(check, cacheDuration);
}
}

+ 40
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs View File

@ -0,0 +1,40 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.Extensions.HealthChecks
{
public class HealthCheckBuilder
{
private readonly Dictionary<string, IHealthCheck> _checks;
public HealthCheckBuilder()
{
_checks = new Dictionary<string, IHealthCheck>(StringComparer.OrdinalIgnoreCase);
DefaultCacheDuration = TimeSpan.FromMinutes(5);
}
public IReadOnlyDictionary<string, IHealthCheck> Checks => _checks;
public TimeSpan DefaultCacheDuration { get; private set; }
public HealthCheckBuilder AddCheck(string name, IHealthCheck check)
{
Guard.ArgumentNotNullOrWhitespace(nameof(name), name);
Guard.ArgumentNotNull(nameof(check), check);
_checks.Add(name, check);
return this;
}
public HealthCheckBuilder WithDefaultCacheDuration(TimeSpan duration)
{
Guard.ArgumentValid(duration >= TimeSpan.Zero, nameof(duration), "Duration must be zero (disabled) or a positive duration");
DefaultCacheDuration = duration;
return this;
}
}
}

+ 18
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckExtensions.cs View File

@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks
{
public static class HealthCheckExtensions
{
public static ValueTask<IHealthCheckResult> CheckAsync(this IHealthCheck healthCheck)
{
Guard.ArgumentNotNull(nameof(healthCheck), healthCheck);
return healthCheck.CheckAsync(CancellationToken.None);
}
}
}

+ 54
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResult.cs View File

@ -0,0 +1,54 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Extensions.HealthChecks
{
public class HealthCheckResult : IHealthCheckResult
{
private static readonly IReadOnlyDictionary<string, object> _emptyData = new Dictionary<string, object>();
public CheckStatus CheckStatus { get; }
public IReadOnlyDictionary<string, object> Data { get; }
public string Description { get; }
private HealthCheckResult(CheckStatus checkStatus, string description, IReadOnlyDictionary<string, object> data)
{
CheckStatus = checkStatus;
Description = description;
Data = data ?? _emptyData;
}
public static HealthCheckResult Unhealthy(string description)
=> new HealthCheckResult(CheckStatus.Unhealthy, description, null);
public static HealthCheckResult Unhealthy(string description, IReadOnlyDictionary<string, object> data)
=> new HealthCheckResult(CheckStatus.Unhealthy, description, data);
public static HealthCheckResult Healthy(string description)
=> new HealthCheckResult(CheckStatus.Healthy, description, null);
public static HealthCheckResult Healthy(string description, IReadOnlyDictionary<string, object> data)
=> new HealthCheckResult(CheckStatus.Healthy, description, data);
public static HealthCheckResult Warning(string description)
=> new HealthCheckResult(CheckStatus.Warning, description, null);
public static HealthCheckResult Warning(string description, IReadOnlyDictionary<string, object> data)
=> new HealthCheckResult(CheckStatus.Warning, description, data);
public static HealthCheckResult Unknown(string description)
=> new HealthCheckResult(CheckStatus.Unknown, description, null);
public static HealthCheckResult Unknown(string description, IReadOnlyDictionary<string, object> data)
=> new HealthCheckResult(CheckStatus.Unknown, description, data);
public static HealthCheckResult FromStatus(CheckStatus status, string description)
=> new HealthCheckResult(status, description, null);
public static HealthCheckResult FromStatus(CheckStatus status, string description, IReadOnlyDictionary<string, object> data)
=> new HealthCheckResult(status, description, data);
}
}

+ 12
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResults.cs View File

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.Extensions.HealthChecks
{
public class HealthCheckResults
{
public IList<IHealthCheckResult> CheckResults { get; } = new List<IHealthCheckResult>();
}
}

+ 54
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckService.cs View File

@ -0,0 +1,54 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Microsoft.Extensions.HealthChecks
{
public class HealthCheckService : IHealthCheckService
{
public IReadOnlyDictionary<string, IHealthCheck> _checks;
private ILogger<HealthCheckService> _logger;
public HealthCheckService(HealthCheckBuilder builder, ILogger<HealthCheckService> logger)
{
_checks = builder.Checks;
_logger = logger;
}
public async Task<CompositeHealthCheckResult> CheckHealthAsync(CheckStatus partiallyHealthyStatus, CancellationToken cancellationToken)
{
var logMessage = new StringBuilder();
var result = new CompositeHealthCheckResult(partiallyHealthyStatus);
foreach (var check in _checks)
{
try
{
var healthCheckResult = await check.Value.CheckAsync().ConfigureAwait(false);
logMessage.AppendLine($"HealthCheck: {check.Key} : {healthCheckResult.CheckStatus}");
result.Add(check.Key, healthCheckResult);
}
catch (Exception ex)
{
logMessage.AppendLine($"HealthCheck: {check.Key} : Exception {ex.GetType().FullName} thrown");
result.Add(check.Key, CheckStatus.Unhealthy, $"Exception during check: {ex.GetType().FullName}");
}
}
if (logMessage.Length == 0)
logMessage.AppendLine("HealthCheck: No checks have been registered");
_logger.Log((result.CheckStatus == CheckStatus.Healthy ? LogLevel.Information : LogLevel.Error), 0, logMessage.ToString(), null, MessageFormatter);
return result;
}
private static string MessageFormatter(string state, Exception error) => state;
}
}

+ 22
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceCollectionExtensions.cs View File

@ -0,0 +1,22 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.HealthChecks;
namespace Microsoft.Extensions.DependencyInjection
{
public static class HealthCheckServiceCollectionExtensions
{
public static IServiceCollection AddHealthChecks(this IServiceCollection services, Action<HealthCheckBuilder> checkupAction)
{
var checkupBuilder = new HealthCheckBuilder();
checkupAction.Invoke(checkupBuilder);
services.AddSingleton(checkupBuilder);
services.AddSingleton<IHealthCheckService, HealthCheckService>();
return services;
}
}
}

+ 38
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceExtensions.cs View File

@ -0,0 +1,38 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks
{
public static class HealthCheckServiceExtensions
{
public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service)
{
Guard.ArgumentNotNull(nameof(service), service);
return service.CheckHealthAsync(CheckStatus.Unhealthy, CancellationToken.None);
}
public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service, CheckStatus partiallyHealthyStatus)
{
Guard.ArgumentNotNull(nameof(service), service);
return service.CheckHealthAsync(partiallyHealthyStatus, CancellationToken.None);
}
public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service, CancellationToken cancellationToken)
{
Guard.ArgumentNotNull(nameof(service), service);
return service.CheckHealthAsync(CheckStatus.Unhealthy, cancellationToken);
}
public static Task<CompositeHealthCheckResult> CheckHealthAsync(this IHealthCheckService service, CheckStatus partiallyHealthyStatus, CancellationToken cancellationToken)
{
Guard.ArgumentNotNull(nameof(service), service);
return service.CheckHealthAsync(partiallyHealthyStatus, cancellationToken);
}
}
}

+ 16
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheck.cs View File

@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks
{
public interface IHealthCheck
{
TimeSpan CacheDuration { get; }
ValueTask<IHealthCheckResult> CheckAsync(CancellationToken cancellationToken);
}
}

+ 14
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckResult.cs View File

@ -0,0 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.Extensions.HealthChecks
{
public interface IHealthCheckResult
{
CheckStatus CheckStatus { get; }
string Description { get; }
IReadOnlyDictionary<string, object> Data { get; }
}
}

+ 13
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckService.cs View File

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks
{
public interface IHealthCheckService
{
Task<CompositeHealthCheckResult> CheckHealthAsync(CheckStatus partiallyHealthyStatus, CancellationToken cancellationToken);
}
}

+ 92
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Internal/UrlChecker.cs View File

@ -0,0 +1,92 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace Microsoft.Extensions.HealthChecks.Internal
{
public class UrlChecker
{
private readonly Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> _checkFunc;
private readonly string[] _urls;
// REVIEW: Cache timeout here?
public UrlChecker(Func<HttpResponseMessage, ValueTask<IHealthCheckResult>> checkFunc, params string[] urls)
{
Guard.ArgumentNotNull(nameof(checkFunc), checkFunc);
Guard.ArgumentNotNullOrEmpty(nameof(urls), urls);
_checkFunc = checkFunc;
_urls = urls;
}
public CheckStatus PartiallyHealthyStatus { get; set; } = CheckStatus.Warning;
public Task<IHealthCheckResult> CheckAsync()
=> _urls.Length == 1 ? CheckSingleAsync() : CheckMultiAsync();
public async Task<IHealthCheckResult> CheckSingleAsync()
{
var httpClient = CreateHttpClient();
var result = default(IHealthCheckResult);
await CheckUrlAsync(httpClient, _urls[0], (_, checkResult) => result = checkResult).ConfigureAwait(false);
return result;
}
public async Task<IHealthCheckResult> CheckMultiAsync()
{
var composite = new CompositeHealthCheckResult(PartiallyHealthyStatus);
var httpClient = CreateHttpClient();
// REVIEW: Should these be done in parallel?
foreach (var url in _urls)
await CheckUrlAsync(httpClient, url, (name, checkResult) => composite.Add(name, checkResult)).ConfigureAwait(false);
return composite;
}
private async Task CheckUrlAsync(HttpClient httpClient, string url, Action<string, IHealthCheckResult> adder)
{
var name = $"UrlCheck({url})";
try
{
var response = await httpClient.GetAsync(url).ConfigureAwait(false);
var result = await _checkFunc(response);
adder(name, result);
}
catch (Exception ex)
{
adder(name, HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}"));
}
}
private HttpClient CreateHttpClient()
{
var httpClient = GetHttpClient();
httpClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true };
return httpClient;
}
public static async ValueTask<IHealthCheckResult> DefaultUrlCheck(HttpResponseMessage response)
{
// REVIEW: Should this be an explicit 200 check, or just an "is success" check?
var status = response.StatusCode == HttpStatusCode.OK ? CheckStatus.Healthy : CheckStatus.Unhealthy;
var data = new Dictionary<string, object>
{
{ "url", response.RequestMessage.RequestUri.ToString() },
{ "status", (int)response.StatusCode },
{ "reason", response.ReasonPhrase },
{ "body", await response.Content?.ReadAsStringAsync() }
};
return HealthCheckResult.FromStatus(status, $"UrlCheck({response.RequestMessage.RequestUri}): status code {response.StatusCode} ({(int)response.StatusCode})", data);
}
protected virtual HttpClient GetHttpClient()
=> new HttpClient();
}
}

+ 20
- 0
src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Microsoft.Extensions.HealthChecks.csproj View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.3.0" />
</ItemGroup>
</Project>

+ 45
- 0
src/BuildingBlocks/HealthChecks/src/common/Guard.cs View File

@ -0,0 +1,45 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
static class Guard
{
public static void ArgumentNotNull(string argumentName, object value)
{
if (value == null)
throw new ArgumentNullException(argumentName);
}
public static void ArgumentNotNullOrEmpty<T>(string argumentName, string value)
{
if (value == null)
throw new ArgumentNullException(argumentName);
if (string.IsNullOrEmpty(value))
throw new ArgumentException("Value cannot be an empty string", argumentName);
}
// Use IReadOnlyCollection<T> instead of IEnumerable<T> to discourage double enumeration
public static void ArgumentNotNullOrEmpty<T>(string argumentName, IReadOnlyCollection<T> items)
{
if (items == null)
throw new ArgumentNullException(argumentName);
if (items.Count == 0)
throw new ArgumentException("Collection must contain at least one item", argumentName);
}
public static void ArgumentNotNullOrWhitespace(string argumentName, string value)
{
if (value == null)
throw new ArgumentNullException(argumentName);
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("Value must contain a non-whitespace value", argumentName);
}
public static void ArgumentValid(bool valid, string argumentName, string exceptionMessage)
{
if (!valid)
throw new ArgumentException(exceptionMessage, argumentName);
}
}

+ 18
- 10
src/Services/Basket/Basket.API/Basket.API.csproj View File

@ -23,22 +23,30 @@
<ItemGroup>
<PackageReference Include="System.Threading" Version="4.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.1" />
<PackageReference Include="StackExchange.Redis" Version="1.1.608" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="1.0.1-rc3" />
<PackageReference Include="Swashbuckle" Version="6.0.0-beta902" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Dockerfile">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>


+ 2
- 0
src/Services/Basket/Basket.API/BasketSettings.cs View File

@ -8,5 +8,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
public class BasketSettings
{
public string ConnectionString { get; set; }
public string EventBusConnection { get; set; }
}
}

+ 8
- 0
src/Services/Basket/Basket.API/Dockerfile.nanowin View File

@ -0,0 +1,8 @@
FROM microsoft/dotnet:1.1-runtime-nanoserver
SHELL ["powershell"]
ARG source
WORKDIR /app
RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord
EXPOSE 80
COPY ${source:-obj/Docker/publish} .
ENTRYPOINT ["dotnet", "Basket.API.dll"]

+ 18
- 0
src/Services/Basket/Basket.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs View File

@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Basket.API.Infrastructure.ActionResults
{
public class InternalServerErrorObjectResult : ObjectResult
{
public InternalServerErrorObjectResult(object error)
: base(error)
{
StatusCode = StatusCodes.Status500InternalServerError;
}
}
}

+ 24
- 0
src/Services/Basket/Basket.API/Infrastructure/Exceptions/BasketDomainException.cs View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Basket.API.Infrastructure.Exceptions
{
/// <summary>
/// Exception type for app exceptions
/// </summary>
public class BasketDomainException : Exception
{
public BasketDomainException()
{ }
public BasketDomainException(string message)
: base(message)
{ }
public BasketDomainException(string message, Exception innerException)
: base(message, innerException)
{ }
}
}

+ 67
- 0
src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs View File

@ -0,0 +1,67 @@
using Basket.API.Infrastructure.ActionResults;
using Basket.API.Infrastructure.Exceptions;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace Basket.API.Infrastructure.Filters
{
public class HttpGlobalExceptionFilter : IExceptionFilter
{
private readonly IHostingEnvironment env;
private readonly ILogger<HttpGlobalExceptionFilter> logger;
public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger<HttpGlobalExceptionFilter> logger)
{
this.env = env;
this.logger = logger;
}
public void OnException(ExceptionContext context)
{
logger.LogError(new EventId(context.Exception.HResult),
context.Exception,
context.Exception.Message);
if (context.Exception.GetType() == typeof(BasketDomainException))
{
var json = new JsonErrorResponse
{
Messages = new[] { context.Exception.Message }
};
context.Result = new BadRequestObjectResult(json);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
else
{
var json = new JsonErrorResponse
{
Messages = new[] { "An error ocurr.Try it again." }
};
if (env.IsDevelopment())
{
json.DeveloperMeesage = context.Exception;
}
context.Result = new InternalServerErrorObjectResult(json);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
context.ExceptionHandled = true;
}
private class JsonErrorResponse
{
public string[] Messages { get; set; }
public object DeveloperMeesage { get; set; }
}
}
}

+ 46
- 0
src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs View File

@ -0,0 +1,46 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.EventHandling
{
public class ProductPriceChangedIntegrationEventHandler : IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>
{
private readonly IBasketRepository _repository;
public ProductPriceChangedIntegrationEventHandler(IBasketRepository repository)
{
_repository = repository;
}
public async Task Handle(ProductPriceChangedIntegrationEvent @event)
{
var userIds = await _repository.GetUsers();
foreach (var id in userIds)
{
var basket = await _repository.GetBasketAsync(id);
await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, @event.OldPrice, basket);
}
}
private async Task UpdatePriceInBasketItems(int productId, decimal newPrice, decimal oldPrice, CustomerBasket basket)
{
var itemsToUpdate = basket?.Items?.Where(x => int.Parse(x.ProductId) == productId).ToList();
if (itemsToUpdate != null)
{
foreach (var item in itemsToUpdate)
{
if(item.UnitPrice == oldPrice)
{
var originalPrice = item.UnitPrice;
item.UnitPrice = newPrice;
item.OldUnitPrice = originalPrice;
}
}
await _repository.UpdateBasketAsync(basket);
}
}
}
}

+ 26
- 0
src/Services/Basket/Basket.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs View File

@ -0,0 +1,26 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events
{
// Integration Events notes:
// An Event is “something that has happened in the past”, therefore its name has to be
// An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems.
public class ProductPriceChangedIntegrationEvent : IntegrationEvent
{
public int ProductId { get; private set; }
public decimal NewPrice { get; private set; }
public decimal OldPrice { get; private set; }
public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice, decimal oldPrice)
{
ProductId = productId;
NewPrice = newPrice;
OldPrice = oldPrice;
}
}
}

+ 1
- 0
src/Services/Basket/Basket.API/Model/BasketItem.cs View File

@ -11,6 +11,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
public string ProductId { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public decimal OldUnitPrice { get; set; }
public int Quantity { get; set; }
public string PictureUrl { get; set; }
}


+ 1
- 0
src/Services/Basket/Basket.API/Model/IBasketRepository.cs View File

@ -8,6 +8,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
public interface IBasketRepository
{
Task<CustomerBasket> GetBasketAsync(string customerId);
Task<IEnumerable<string>> GetUsers();
Task<CustomerBasket> UpdateBasketAsync(CustomerBasket basket);
Task<bool> DeleteBasketAsync(string id);
}


+ 34
- 4
src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs View File

@ -31,6 +31,18 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
return await database.KeyDeleteAsync(id.ToString());
}
public async Task<IEnumerable<string>> GetUsers()
{
var server = await GetServer();
IEnumerable<RedisKey> data = server.Keys();
if (data == null)
{
return null;
}
return data.Select(k => k.ToString());
}
public async Task<CustomerBasket> GetBasketAsync(string customerId)
{
var database = await GetDatabase();
@ -63,14 +75,32 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
{
if (_redis == null)
{
//TODO: Need to make this more robust. Also want to understand why the static connection method cannot accept dns names.
var ips = await Dns.GetHostAddressesAsync(_settings.ConnectionString);
_logger.LogInformation($"Connecting to database {_settings.ConnectionString} at IP {ips.First().ToString()}");
_redis = await ConnectionMultiplexer.ConnectAsync(ips.First().ToString());
await ConnectToRedisAsync();
}
return _redis.GetDatabase();
}
private async Task<IServer> GetServer()
{
if (_redis == null)
{
await ConnectToRedisAsync();
}
var endpoint = _redis.GetEndPoints();
return _redis.GetServer(endpoint.First());
}
private async Task ConnectToRedisAsync()
{
//TODO: Need to make this more robust. Also want to understand why the static connection method cannot accept dns names.
var ips = await Dns.GetHostAddressesAsync(_settings.ConnectionString);
_logger.LogInformation($"Connecting to database {_settings.ConnectionString} at IP {ips.First().ToString()}");
_redis = await ConnectionMultiplexer.ConnectAsync(ips.First().ToString());
}
}
}

+ 1
- 0
src/Services/Basket/Basket.API/Program.cs View File

@ -10,6 +10,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
{
var host = new WebHostBuilder()
.UseKestrel()
.UseHealthChecks("/hc")
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()


+ 42
- 14
src/Services/Basket/Basket.API/Startup.cs View File

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
@ -11,8 +8,15 @@ using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using StackExchange.Redis;
using Microsoft.Extensions.Options;
using System.Net;
using Swashbuckle.Swagger.Model;
using Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events;
using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.EventHandling;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
using System;
using Microsoft.Extensions.HealthChecks;
using System.Threading.Tasks;
using Basket.API.Infrastructure.Filters;
namespace Microsoft.eShopOnContainers.Services.Basket.API
{
@ -33,8 +37,18 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks(checks =>
{
checks.AddValueTaskCheck("Always OK", () => new ValueTask<IHealthCheckResult>(HealthCheckResult.Healthy("Ok")));
});
// Add framework services.
services.AddMvc();
services.AddMvc(options =>
{
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
}).AddControllersAsServices();
services.Configure<BasketSettings>(Configuration);
//By connecting here we are making sure that our service
@ -76,6 +90,12 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
});
services.AddTransient<IBasketRepository, RedisBasketRepository>();
services.AddTransient<IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>, ProductPriceChangedIntegrationEventHandler>();
var serviceProvider = services.BuildServiceProvider();
var configuration = serviceProvider.GetRequiredService<IOptionsSnapshot<BasketSettings>>().Value;
services.AddSingleton<IEventBus>(provider => new EventBusRabbitMQ(configuration.EventBusConnection));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -89,20 +109,28 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
// Use frameworks
app.UseCors("CorsPolicy");
var identityUrl = Configuration.GetValue<string>("IdentityUrl");
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = identityUrl.ToString(),
ScopeName = "basket",
RequireHttpsMetadata = false
});
ConfigureAuth(app);
app.UseMvcWithDefaultRoute();
app.UseSwagger()
.UseSwaggerUi();
var catalogPriceHandler = app.ApplicationServices.GetService<IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>>();
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<ProductPriceChangedIntegrationEvent>(catalogPriceHandler);
}
protected virtual void ConfigureAuth(IApplicationBuilder app)
{
var identityUrl = Configuration.GetValue<string>("IdentityUrl");
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = identityUrl.ToString(),
ScopeName = "basket",
RequireHttpsMetadata = false
});
}
}
}

+ 29
- 17
src/Services/Catalog/Catalog.API/Catalog.API.csproj View File

@ -21,6 +21,7 @@
<Content Include="Pics\**\*;">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Compile Include="IntegrationEvents\EventHandling\AnyFutureIntegrationEventHandler.cs.txt" />
<Content Update="web.config;">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
@ -28,23 +29,25 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Swashbuckle" Version="6.0.0-beta902" />
</ItemGroup>
@ -52,6 +55,15 @@
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Dockerfile">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>


+ 121
- 13
src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs View File

@ -1,38 +1,51 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events;
using Microsoft.eShopOnContainers.Services.Catalog.API.Model;
using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
{
[Route("api/v1/[controller]")]
public class CatalogController : ControllerBase
{
private readonly CatalogContext _context;
private readonly CatalogContext _catalogContext;
private readonly IOptionsSnapshot<Settings> _settings;
private readonly IEventBus _eventBus;
private readonly Func<DbConnection, IIntegrationEventLogService> _integrationEventLogServiceFactory;
public CatalogController(CatalogContext context, IOptionsSnapshot<Settings> settings)
public CatalogController(CatalogContext Context, IOptionsSnapshot<Settings> settings, IEventBus eventBus, Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
{
_context = context;
_catalogContext = Context;
_settings = settings;
_eventBus = eventBus;
_integrationEventLogServiceFactory = integrationEventLogServiceFactory;
((DbContext)context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
((DbContext)Context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
// GET api/v1/[controller]/items[?pageSize=3&pageIndex=10]
[HttpGet]
[Route("[action]")]
public async Task<IActionResult> Items([FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)
{
var totalItems = await _context.CatalogItems
var totalItems = await _catalogContext.CatalogItems
.LongCountAsync();
var itemsOnPage = await _context.CatalogItems
var itemsOnPage = await _catalogContext.CatalogItems
.OrderBy(c=>c.Name)
.Skip(pageSize * pageIndex)
.Take(pageSize)
@ -41,7 +54,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
itemsOnPage = ComposePicUri(itemsOnPage);
var model = new PaginatedItemsViewModel<CatalogItem>(
pageIndex, pageSize, totalItems, itemsOnPage);
pageIndex, pageSize, totalItems, itemsOnPage);
return Ok(model);
}
@ -52,11 +65,11 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
public async Task<IActionResult> Items(string name, [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)
{
var totalItems = await _context.CatalogItems
var totalItems = await _catalogContext.CatalogItems
.Where(c => c.Name.StartsWith(name))
.LongCountAsync();
var itemsOnPage = await _context.CatalogItems
var itemsOnPage = await _catalogContext.CatalogItems
.Where(c => c.Name.StartsWith(name))
.Skip(pageSize * pageIndex)
.Take(pageSize)
@ -75,7 +88,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
[Route("[action]/type/{catalogTypeId}/brand/{catalogBrandId}")]
public async Task<IActionResult> Items(int? catalogTypeId, int? catalogBrandId, [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)
{
var root = (IQueryable<CatalogItem>)_context.CatalogItems;
var root = (IQueryable<CatalogItem>)_catalogContext.CatalogItems;
if (catalogTypeId.HasValue)
{
@ -99,7 +112,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
var model = new PaginatedItemsViewModel<CatalogItem>(
pageIndex, pageSize, totalItems, itemsOnPage);
return Ok(model);
}
@ -108,7 +121,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
[Route("[action]")]
public async Task<IActionResult> CatalogTypes()
{
var items = await _context.CatalogTypes
var items = await _catalogContext.CatalogTypes
.ToListAsync();
return Ok(items);
@ -119,12 +132,107 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
[Route("[action]")]
public async Task<IActionResult> CatalogBrands()
{
var items = await _context.CatalogBrands
var items = await _catalogContext.CatalogBrands
.ToListAsync();
return Ok(items);
}
//POST api/v1/[controller]/update
[Route("update")]
[HttpPost]
public async Task<IActionResult> UpdateProduct([FromBody]CatalogItem productToUpdate)
{
var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id);
if (catalogItem == null) return NotFound();
bool raiseProductPriceChangedEvent = false;
IntegrationEvent priceChangedEvent = null;
if (catalogItem.Price != productToUpdate.Price) raiseProductPriceChangedEvent = true;
if (raiseProductPriceChangedEvent) // Create event if price has changed
{
var oldPrice = catalogItem.Price;
priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice);
}
//Update current product
catalogItem = productToUpdate;
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
var strategy = _catalogContext.Database.CreateExecutionStrategy();
var eventLogService = _integrationEventLogServiceFactory(_catalogContext.Database.GetDbConnection());
await strategy.ExecuteAsync(async () =>
{
// Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
using (var transaction = _catalogContext.Database.BeginTransaction())
{
_catalogContext.CatalogItems.Update(catalogItem);
await _catalogContext.SaveChangesAsync();
//Save to EventLog only if product price changed
if (raiseProductPriceChangedEvent)
{
await eventLogService.SaveEventAsync(priceChangedEvent, _catalogContext.Database.CurrentTransaction.GetDbTransaction());
}
transaction.Commit();
}
});
//Publish to Event Bus only if product price changed
if (raiseProductPriceChangedEvent)
{
_eventBus.Publish(priceChangedEvent);
await eventLogService.MarkEventAsPublishedAsync(priceChangedEvent);
}
return Ok();
}
//POST api/v1/[controller]/create
[Route("create")]
[HttpPost]
public async Task<IActionResult> CreateProduct([FromBody]CatalogItem product)
{
_catalogContext.CatalogItems.Add(
new CatalogItem
{
CatalogBrandId = product.CatalogBrandId,
CatalogTypeId = product.CatalogTypeId,
Description = product.Description,
Name = product.Name,
PictureUri = product.PictureUri,
Price = product.Price
});
await _catalogContext.SaveChangesAsync();
return Ok();
}
//DELETE api/v1/[controller]/id
[Route("{id}")]
[HttpDelete]
public async Task<IActionResult> DeleteProduct(int id)
{
var product = _catalogContext.CatalogItems.SingleOrDefault(x => x.Id == id);
if (product == null)
{
return NotFound();
}
_catalogContext.CatalogItems.Remove(product);
await _catalogContext.SaveChangesAsync();
return Ok();
}
private List<CatalogItem> ComposePicUri(List<CatalogItem> items) {
var baseUri = _settings.Value.ExternalCatalogBaseUrl;
items.ForEach(x =>


+ 8
- 0
src/Services/Catalog/Catalog.API/Dockerfile.nanowin View File

@ -0,0 +1,8 @@
FROM microsoft/dotnet:1.1-runtime-nanoserver
SHELL ["powershell"]
ARG source
WORKDIR /app
RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord
EXPOSE 80
COPY ${source:-obj/Docker/publish} .
ENTRYPOINT ["dotnet", "Catalog.API.dll"]

+ 18
- 0
src/Services/Catalog/Catalog.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs View File

@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Catalog.API.Infrastructure.ActionResults
{
public class InternalServerErrorObjectResult : ObjectResult
{
public InternalServerErrorObjectResult(object error)
: base(error)
{
StatusCode = StatusCodes.Status500InternalServerError;
}
}
}

+ 32
- 2
src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs View File

@ -3,21 +3,25 @@
using EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
using Model;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
public class CatalogContext : DbContext
{
public CatalogContext(DbContextOptions options) : base(options)
public CatalogContext(DbContextOptions<CatalogContext> options) : base(options)
{
}
public DbSet<CatalogItem> CatalogItems { get; set; }
public DbSet<CatalogBrand> CatalogBrands { get; set; }
public DbSet<CatalogType> CatalogTypes { get; set; }
//public DbSet<IntegrationEventLogEntry> IntegrationEventLog { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<CatalogBrand>(ConfigureCatalogBrand);
builder.Entity<CatalogType>(ConfigureCatalogType);
builder.Entity<CatalogItem>(ConfigureCatalogItem);
}
//builder.Entity<IntegrationEventLogEntry>(ConfigureIntegrationEventLogEntry);
}
void ConfigureCatalogItem(EntityTypeBuilder<CatalogItem> builder)
{
@ -75,5 +79,31 @@
.IsRequired()
.HasMaxLength(100);
}
//void ConfigureIntegrationEventLogEntry(EntityTypeBuilder<IntegrationEventLogEntry> builder)
//{
// builder.ToTable("IntegrationEventLog");
// builder.HasKey(e => e.EventId);
// builder.Property(e => e.EventId)
// .IsRequired();
// builder.Property(e => e.Content)
// .IsRequired();
// builder.Property(e => e.CreationTime)
// .IsRequired();
// builder.Property(e => e.State)
// .IsRequired();
// builder.Property(e => e.TimesSent)
// .IsRequired();
// builder.Property(e => e.EventTypeName)
// .IsRequired();
//}
}
}

+ 4
- 4
src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs View File

@ -70,14 +70,14 @@
{
return new List<CatalogItem>()
{
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Sweatshirt", Name = ".NET Bot Black Sweatshirt", Price = 19.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/1" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/1" },
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/2" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/3" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation Sweatshirt", Name = ".NET Foundation Sweatshirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/4" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/4" },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=5, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/5" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Sweatshirt", Name = ".NET Blue Sweatshirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/6" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/6" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/7" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Sweatshirt", Name = "Kudu Purple Sweatshirt", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/8" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/8" },
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=5, Description = "Cup<T> White Mug", Name = "Cup<T> White Mug", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/9" },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/10" },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = "Cup<T> Sheet", Name = "Cup<T> Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/11" },


src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103152832_Initial.Designer.cs → src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103152832_Initial.Designer.cs View File


src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103152832_Initial.cs → src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103152832_Initial.cs View File


src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103153420_UpdateTableNames.Designer.cs → src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103153420_UpdateTableNames.Designer.cs View File


src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103153420_UpdateTableNames.cs → src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103153420_UpdateTableNames.cs View File


+ 122
- 0
src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170314083211_AddEventTable.Designer.cs View File

@ -0,0 +1,122 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
namespace Catalog.API.Infrastructure.Migrations
{
[DbContext(typeof(CatalogContext))]
[Migration("20170314083211_AddEventTable")]
partial class AddEventTable
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752")
.HasAnnotation("SqlServer:Sequence:.catalog_brand_hilo", "'catalog_brand_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_hilo", "'catalog_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_type_hilo", "'catalog_type_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_brand_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Brand")
.IsRequired()
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogBrand");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<int>("CatalogBrandId");
b.Property<int>("CatalogTypeId");
b.Property<string>("Description");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50);
b.Property<string>("PictureUri");
b.Property<decimal>("Price");
b.HasKey("Id");
b.HasIndex("CatalogBrandId");
b.HasIndex("CatalogTypeId");
b.ToTable("Catalog");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_type_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogType");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data.IntegrationEvent", b =>
{
b.Property<Guid>("EventId")
.ValueGeneratedOnAdd();
b.Property<string>("Content")
.IsRequired();
b.Property<DateTime>("CreationTime");
b.Property<string>("EventTypeName")
.IsRequired()
.HasMaxLength(200);
b.Property<int>("State");
b.Property<int>("TimesSent");
b.HasKey("EventId");
b.ToTable("IntegrationEvent");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", "CatalogBrand")
.WithMany()
.HasForeignKey("CatalogBrandId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", "CatalogType")
.WithMany()
.HasForeignKey("CatalogTypeId")
.OnDelete(DeleteBehavior.Cascade);
});
}
}
}

+ 34
- 0
src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170314083211_AddEventTable.cs View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Catalog.API.Infrastructure.Migrations
{
public partial class AddEventTable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "IntegrationEvent",
columns: table => new
{
EventId = table.Column<Guid>(nullable: false),
Content = table.Column<string>(nullable: false),
CreationTime = table.Column<DateTime>(nullable: false),
EventTypeName = table.Column<string>(maxLength: 200, nullable: false),
State = table.Column<int>(nullable: false),
TimesSent = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IntegrationEvent", x => x.EventId);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "IntegrationEvent");
}
}
}

+ 121
- 0
src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316012921_RefactoringToIntegrationEventLog.Designer.cs View File

@ -0,0 +1,121 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
namespace Catalog.API.Infrastructure.Migrations
{
[DbContext(typeof(CatalogContext))]
[Migration("20170316012921_RefactoringToIntegrationEventLog")]
partial class RefactoringToIntegrationEventLog
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.1")
.HasAnnotation("SqlServer:Sequence:.catalog_brand_hilo", "'catalog_brand_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_hilo", "'catalog_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_type_hilo", "'catalog_type_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_brand_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Brand")
.IsRequired()
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogBrand");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<int>("CatalogBrandId");
b.Property<int>("CatalogTypeId");
b.Property<string>("Description");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50);
b.Property<string>("PictureUri");
b.Property<decimal>("Price");
b.HasKey("Id");
b.HasIndex("CatalogBrandId");
b.HasIndex("CatalogTypeId");
b.ToTable("Catalog");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_type_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogType");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data.IntegrationEventLogEntry", b =>
{
b.Property<Guid>("EventId")
.ValueGeneratedOnAdd();
b.Property<string>("Content")
.IsRequired();
b.Property<DateTime>("CreationTime");
b.Property<string>("EventTypeName")
.IsRequired();
b.Property<int>("State");
b.Property<int>("TimesSent");
b.HasKey("EventId");
b.ToTable("IntegrationEventLog");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", "CatalogBrand")
.WithMany()
.HasForeignKey("CatalogBrandId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", "CatalogType")
.WithMany()
.HasForeignKey("CatalogTypeId")
.OnDelete(DeleteBehavior.Cascade);
});
}
}
}

+ 53
- 0
src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316012921_RefactoringToIntegrationEventLog.cs View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Catalog.API.Infrastructure.Migrations
{
public partial class RefactoringToIntegrationEventLog : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "IntegrationEvent");
migrationBuilder.CreateTable(
name: "IntegrationEventLog",
columns: table => new
{
EventId = table.Column<Guid>(nullable: false),
Content = table.Column<string>(nullable: false),
CreationTime = table.Column<DateTime>(nullable: false),
EventTypeName = table.Column<string>(nullable: false),
State = table.Column<int>(nullable: false),
TimesSent = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IntegrationEventLog", x => x.EventId);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "IntegrationEventLog");
migrationBuilder.CreateTable(
name: "IntegrationEvent",
columns: table => new
{
EventId = table.Column<Guid>(nullable: false),
Content = table.Column<string>(nullable: false),
CreationTime = table.Column<DateTime>(nullable: false),
EventTypeName = table.Column<string>(maxLength: 200, nullable: false),
State = table.Column<int>(nullable: false),
TimesSent = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IntegrationEvent", x => x.EventId);
});
}
}
}

+ 122
- 0
src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316120022_RefactoringEventBusNamespaces.Designer.cs View File

@ -0,0 +1,122 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
namespace Catalog.API.Infrastructure.Migrations
{
[DbContext(typeof(CatalogContext))]
[Migration("20170316120022_RefactoringEventBusNamespaces")]
partial class RefactoringEventBusNamespaces
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.1")
.HasAnnotation("SqlServer:Sequence:.catalog_brand_hilo", "'catalog_brand_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_hilo", "'catalog_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_type_hilo", "'catalog_type_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events.IntegrationEventLogEntry", b =>
{
b.Property<Guid>("EventId")
.ValueGeneratedOnAdd();
b.Property<string>("Content")
.IsRequired();
b.Property<DateTime>("CreationTime");
b.Property<string>("EventTypeName")
.IsRequired();
b.Property<int>("State");
b.Property<int>("TimesSent");
b.HasKey("EventId");
b.ToTable("IntegrationEventLog");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_brand_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Brand")
.IsRequired()
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogBrand");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<int>("CatalogBrandId");
b.Property<int>("CatalogTypeId");
b.Property<string>("Description");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50);
b.Property<string>("PictureUri");
b.Property<decimal>("Price");
b.HasKey("Id");
b.HasIndex("CatalogBrandId");
b.HasIndex("CatalogTypeId");
b.ToTable("Catalog");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_type_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogType");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", "CatalogBrand")
.WithMany()
.HasForeignKey("CatalogBrandId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", "CatalogType")
.WithMany()
.HasForeignKey("CatalogTypeId")
.OnDelete(DeleteBehavior.Cascade);
});
}
}
}

+ 19
- 0
src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316120022_RefactoringEventBusNamespaces.cs View File

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

+ 99
- 0
src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.Designer.cs View File

@ -0,0 +1,99 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
namespace Catalog.API.Infrastructure.Migrations
{
[DbContext(typeof(CatalogContext))]
[Migration("20170322124244_RemoveIntegrationEventLogs")]
partial class RemoveIntegrationEventLogs
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.1")
.HasAnnotation("SqlServer:Sequence:.catalog_brand_hilo", "'catalog_brand_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_hilo", "'catalog_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_type_hilo", "'catalog_type_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_brand_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Brand")
.IsRequired()
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogBrand");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<int>("CatalogBrandId");
b.Property<int>("CatalogTypeId");
b.Property<string>("Description");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50);
b.Property<string>("PictureUri");
b.Property<decimal>("Price");
b.HasKey("Id");
b.HasIndex("CatalogBrandId");
b.HasIndex("CatalogTypeId");
b.ToTable("Catalog");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_type_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogType");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", "CatalogBrand")
.WithMany()
.HasForeignKey("CatalogBrandId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", "CatalogType")
.WithMany()
.HasForeignKey("CatalogTypeId")
.OnDelete(DeleteBehavior.Cascade);
});
}
}
}

+ 34
- 0
src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.cs View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Catalog.API.Infrastructure.Migrations
{
public partial class RemoveIntegrationEventLogs : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "IntegrationEventLog");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "IntegrationEventLog",
columns: table => new
{
EventId = table.Column<Guid>(nullable: false),
Content = table.Column<string>(nullable: false),
CreationTime = table.Column<DateTime>(nullable: false),
EventTypeName = table.Column<string>(nullable: false),
State = table.Column<int>(nullable: false),
TimesSent = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IntegrationEventLog", x => x.EventId);
});
}
}
}

src/Services/Catalog/Catalog.API/Infrastructure/Migrations/CatalogContextModelSnapshot.cs → src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/CatalogContextModelSnapshot.cs View File

@ -13,13 +13,13 @@ namespace Catalog.API.Infrastructure.Migrations
protected override void BuildModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.0.1")
.HasAnnotation("ProductVersion", "1.1.1")
.HasAnnotation("SqlServer:Sequence:.catalog_brand_hilo", "'catalog_brand_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_hilo", "'catalog_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_type_hilo", "'catalog_type_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogBrand", b =>
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -28,14 +28,14 @@ namespace Catalog.API.Infrastructure.Migrations
b.Property<string>("Brand")
.IsRequired()
.HasAnnotation("MaxLength", 100);
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogBrand");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogItem", b =>
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -50,7 +50,7 @@ namespace Catalog.API.Infrastructure.Migrations
b.Property<string>("Name")
.IsRequired()
.HasAnnotation("MaxLength", 50);
.HasMaxLength(50);
b.Property<string>("PictureUri");
@ -65,7 +65,7 @@ namespace Catalog.API.Infrastructure.Migrations
b.ToTable("Catalog");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogType", b =>
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -74,21 +74,21 @@ namespace Catalog.API.Infrastructure.Migrations
b.Property<string>("Type")
.IsRequired()
.HasAnnotation("MaxLength", 100);
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogType");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogItem", b =>
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogBrand", "CatalogBrand")
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", "CatalogBrand")
.WithMany()
.HasForeignKey("CatalogBrandId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogType", "CatalogType")
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", "CatalogType")
.WithMany()
.HasForeignKey("CatalogTypeId")
.OnDelete(DeleteBehavior.Cascade);

+ 24
- 0
src/Services/Catalog/Catalog.API/Infrastructure/Exceptions/CatalogDomainException.cs View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Catalog.API.Infrastructure.Exceptions
{
/// <summary>
/// Exception type for app exceptions
/// </summary>
public class CatalogDomainException : Exception
{
public CatalogDomainException()
{ }
public CatalogDomainException(string message)
: base(message)
{ }
public CatalogDomainException(string message, Exception innerException)
: base(message, innerException)
{ }
}
}

+ 63
- 0
src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs View File

@ -0,0 +1,63 @@
using Catalog.API.Infrastructure.ActionResults;
using Catalog.API.Infrastructure.Exceptions;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System.Net;
namespace Catalog.API.Infrastructure.Filters
{
public class HttpGlobalExceptionFilter : IExceptionFilter
{
private readonly IHostingEnvironment env;
private readonly ILogger<HttpGlobalExceptionFilter> logger;
public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger<HttpGlobalExceptionFilter> logger)
{
this.env = env;
this.logger = logger;
}
public void OnException(ExceptionContext context)
{
logger.LogError(new EventId(context.Exception.HResult),
context.Exception,
context.Exception.Message);
if (context.Exception.GetType() == typeof(CatalogDomainException))
{
var json = new JsonErrorResponse
{
Messages = new[] { context.Exception.Message }
};
context.Result = new BadRequestObjectResult(json);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
else
{
var json = new JsonErrorResponse
{
Messages = new[] { "An error ocurr.Try it again." }
};
if (env.IsDevelopment())
{
json.DeveloperMeesage = context.Exception;
}
context.Result = new InternalServerErrorObjectResult(json);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
context.ExceptionHandled = true;
}
private class JsonErrorResponse
{
public string[] Messages { get; set; }
public object DeveloperMeesage { get; set; }
}
}
}

+ 43
- 0
src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.Designer.cs View File

@ -0,0 +1,43 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
namespace Catalog.API.Migrations
{
[DbContext(typeof(IntegrationEventLogContext))]
[Migration("20170322145434_IntegrationEventInitial")]
partial class IntegrationEventInitial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.1")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.IntegrationEventLogEntry", b =>
{
b.Property<Guid>("EventId")
.ValueGeneratedOnAdd();
b.Property<string>("Content")
.IsRequired();
b.Property<DateTime>("CreationTime");
b.Property<string>("EventTypeName")
.IsRequired();
b.Property<int>("State");
b.Property<int>("TimesSent");
b.HasKey("EventId");
b.ToTable("IntegrationEventLog");
});
}
}
}

+ 34
- 0
src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.cs View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Catalog.API.Migrations
{
public partial class IntegrationEventInitial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "IntegrationEventLog",
columns: table => new
{
EventId = table.Column<Guid>(nullable: false),
Content = table.Column<string>(nullable: false),
CreationTime = table.Column<DateTime>(nullable: false),
EventTypeName = table.Column<string>(nullable: false),
State = table.Column<int>(nullable: false),
TimesSent = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IntegrationEventLog", x => x.EventId);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "IntegrationEventLog");
}
}
}

+ 42
- 0
src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextModelSnapshot.cs View File

@ -0,0 +1,42 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
namespace Catalog.API.Migrations
{
[DbContext(typeof(IntegrationEventLogContext))]
partial class IntegrationEventLogContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.1")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.IntegrationEventLogEntry", b =>
{
b.Property<Guid>("EventId")
.ValueGeneratedOnAdd();
b.Property<string>("Content")
.IsRequired();
b.Property<DateTime>("CreationTime");
b.Property<string>("EventTypeName")
.IsRequired();
b.Property<int>("State");
b.Property<int>("TimesSent");
b.HasKey("EventId");
b.ToTable("IntegrationEventLog");
});
}
}
}

+ 3
- 0
src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/AnyFutureIntegrationEventHandler.cs.txt View File

@ -0,0 +1,3 @@

// To implement ProductPriceChangedEvent.cs here

+ 26
- 0
src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs View File

@ -0,0 +1,26 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
{
// Integration Events notes:
// An Event is “something that has happened in the past”, therefore its name has to be
// An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems.
public class ProductPriceChangedIntegrationEvent : IntegrationEvent
{
public int ProductId { get; private set; }
public decimal NewPrice { get; private set; }
public decimal OldPrice { get; private set; }
public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice, decimal oldPrice)
{
ProductId = productId;
NewPrice = newPrice;
OldPrice = oldPrice;
}
}
}

+ 1
- 0
src/Services/Catalog/Catalog.API/Program.cs View File

@ -10,6 +10,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API
{
var host = new WebHostBuilder()
.UseKestrel()
.UseHealthChecks("/hc")
.UseIISIntegration()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseWebRoot("Pics")


+ 44
- 14
src/Services/Catalog/Catalog.API/Startup.cs View File

@ -1,21 +1,25 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API
{
using AspNetCore.Http;
using Extensions.FileProviders;
using global::Catalog.API.Infrastructure.Filters;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.HealthChecks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Data.SqlClient;
using System.IO;
using System.Data.Common;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
public class Startup
{
@ -40,12 +44,30 @@
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CatalogContext>(c =>
// Add framework services.
services.AddHealthChecks(checks =>
{
checks.AddUrlCheck(Configuration["ExternalCatalogBaseUrl"]);
});
services.AddMvc(options =>
{
c.UseSqlServer(Configuration["ConnectionString"]);
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
}).AddControllersAsServices();
services.AddDbContext<CatalogContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
// Changing default behavior when client evaluation occurs to throw.
// Default in EF Core would be to log a warning when client evaluation is performed.
c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
//Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval
});
@ -74,18 +96,18 @@
.AllowCredentials());
});
services.AddMvc();
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
sp => (DbConnection c) => new IntegrationEventLogService(c));
var serviceProvider = services.BuildServiceProvider();
var configuration = serviceProvider.GetRequiredService<IOptionsSnapshot<Settings>>().Value;
services.AddSingleton<IEventBus>(new EventBusRabbitMQ(configuration.EventBusConnection));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//Configure logs
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
@ -102,7 +124,13 @@
WaitForSqlAvailability(context, loggerFactory);
//Seed Data
CatalogContextSeed.SeedAsync(app, loggerFactory)
.Wait();
.Wait();
var integrationEventLogContext = new IntegrationEventLogContext(
new DbContextOptionsBuilder<IntegrationEventLogContext>()
.UseSqlServer(Configuration["ConnectionString"], b => b.MigrationsAssembly("Catalog.API"))
.Options);
integrationEventLogContext.Database.Migrate();
}
private void WaitForSqlAvailability(CatalogContext ctx, ILoggerFactory loggerFactory, int? retry = 0)
@ -125,6 +153,8 @@
finally {
ctx.Database.CloseConnection();
}
}
}
}

+ 1
- 0
src/Services/Catalog/Catalog.API/settings.cs View File

@ -9,5 +9,6 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API
public class Settings
{
public string ExternalCatalogBaseUrl {get;set;}
public string EventBusConnection { get; set; }
}
}

+ 8
- 0
src/Services/Identity/Identity.API/Dockerfile.nanowin View File

@ -0,0 +1,8 @@
FROM microsoft/dotnet:1.1-runtime-nanoserver
SHELL ["powershell"]
ARG source
WORKDIR /app
RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord
EXPOSE 80
COPY ${source:-obj/Docker/publish} .
ENTRYPOINT ["dotnet", "Identity.API.dll"]

+ 6
- 0
src/Services/Identity/Identity.API/Identity.API.csproj View File

@ -57,6 +57,12 @@
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0-msbuild3-final" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.Data\Microsoft.Extensions.HealthChecks.Data.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Dockerfile">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>


+ 1
- 0
src/Services/Identity/Identity.API/Program.cs View File

@ -9,6 +9,7 @@ namespace eShopOnContainers.Identity
{
var host = new WebHostBuilder()
.UseKestrel()
.UseHealthChecks("/hc")
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()


+ 8
- 1
src/Services/Identity/Identity.API/Startup.cs View File

@ -17,6 +17,7 @@ using IdentityServer4.Services;
using System.Threading;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.HealthChecks;
namespace eShopOnContainers.Identity
{
@ -43,7 +44,8 @@ namespace eShopOnContainers.Identity
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
@ -56,6 +58,11 @@ namespace eShopOnContainers.Identity
services.AddMvc();
services.AddHealthChecks(checks =>
{
checks.AddSqlCheck("Identity_Db", Configuration.GetConnectionString("DefaultConnection"));
});
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddTransient<ILoginService<ApplicationUser>, EFLoginService>();


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save