commit
a7394be2bc
2
.env
2
.env
@ -5,7 +5,7 @@
|
||||
# The IP below should be swapped to your real IP or DNS name, like 192.168.88.248, etc. if testing from remote browsers or mobile devices
|
||||
|
||||
ESHOP_EXTERNAL_DNS_NAME_OR_IP=localhost
|
||||
ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP=10.121.122.92
|
||||
ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP=10.121.122.162
|
||||
|
||||
#ESHOP_AZURE_REDIS_BASKET_DB=<YourAzureRedisBasketInfo>
|
||||
#ESHOP_AZURE_STORAGE_CATALOG_URL=<YourAzureStorage_Catalog_BLOB_URL>
|
||||
|
@ -8,7 +8,7 @@ version: '3'
|
||||
#
|
||||
# IMPORTANT: Note that this compose file uses ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP instead of ESHOP_EXTERNAL_DNS_NAME_OR_IP
|
||||
|
||||
# Set ASPNETCORE_ENVIRONMENT=Development, instead Production, if you want to show up errors while testing.
|
||||
# Set ASPNETCORE_ENVIRONMENT= Development or Production, depending if you want to show up errors while testing.
|
||||
#
|
||||
# You need to start it with the following CLI command:
|
||||
# docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
|
||||
@ -17,7 +17,7 @@ services:
|
||||
|
||||
basket.api:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://0.0.0.0:80
|
||||
- ConnectionString=${ESHOP_AZURE_REDIS_BASKET_DB:-basket.data}
|
||||
- identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
|
||||
@ -35,7 +35,7 @@ services:
|
||||
|
||||
catalog.api:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://0.0.0.0:80
|
||||
- ConnectionString=${ESHOP_AZURE_CATALOG_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word}
|
||||
- PicBaseUrl=${ESHOP_AZURE_STORAGE_CATALOG_URL:-http://localhost:5101/api/v1/catalog/items/[0]/pic/} #Local: You need to open your local dev-machine firewall at range 5100-5110.
|
||||
@ -54,7 +54,7 @@ services:
|
||||
|
||||
identity.api:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://0.0.0.0:80
|
||||
- SpaClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5104
|
||||
- XamarinCallback=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback #localhost do not work for UWP login, so we have to use "external" IP always
|
||||
@ -72,7 +72,7 @@ services:
|
||||
|
||||
ordering.api:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://0.0.0.0:80
|
||||
- ConnectionString=${ESHOP_AZURE_ORDERING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word}
|
||||
- identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
|
||||
@ -92,7 +92,7 @@ services:
|
||||
|
||||
marketing.api:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://0.0.0.0:80
|
||||
- ConnectionString=${ESHOP_AZURE_MARKETING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word}
|
||||
- MongoConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data}
|
||||
@ -116,7 +116,7 @@ services:
|
||||
|
||||
webspa:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://0.0.0.0:80
|
||||
- CatalogUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101
|
||||
- OrderingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102
|
||||
@ -126,7 +126,7 @@ services:
|
||||
- LocationsUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5109
|
||||
- CatalogUrlHC=http://catalog.api/hc
|
||||
- OrderingUrlHC=http://ordering.api/hc
|
||||
- IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
|
||||
- IdentityUrlHC=http://identity.api/hc
|
||||
- BasketUrlHC=http://basket.api/hc
|
||||
- MarketingUrlHC=http://marketing.api/hc
|
||||
- PaymentUrlHC=http://payment.api/hc
|
||||
@ -138,17 +138,17 @@ services:
|
||||
|
||||
webmvc:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://0.0.0.0:80
|
||||
- CatalogUrl=http://catalog.api
|
||||
- OrderingUrl=http://ordering.api
|
||||
- BasketUrl=http://basket.api
|
||||
- LocationsUrl=http://locations.api
|
||||
- IdentityUrl=http://10.0.75.1:5105
|
||||
- MarketingUrl=http://marketing.api #Local: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. #Remote: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.
|
||||
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. #Remote: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.
|
||||
- MarketingUrl=http://marketing.api
|
||||
- CatalogUrlHC=http://catalog.api/hc
|
||||
- OrderingUrlHC=http://ordering.api/hc
|
||||
- IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
|
||||
- IdentityUrlHC=http://identity.api/hc
|
||||
- BasketUrlHC=http://basket.api/hc
|
||||
- MarketingUrlHC=http://marketing.api/hc
|
||||
- PaymentUrlHC=http://payment.api/hc
|
||||
@ -161,7 +161,7 @@ services:
|
||||
|
||||
webstatus:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://0.0.0.0:80
|
||||
- CatalogUrl=http://catalog.api/hc
|
||||
- OrderingUrl=http://ordering.api/hc
|
||||
@ -179,7 +179,7 @@ services:
|
||||
|
||||
payment.api:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://0.0.0.0:80
|
||||
- EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq}
|
||||
- EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
|
||||
@ -192,7 +192,7 @@ services:
|
||||
|
||||
locations.api:
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Production
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://0.0.0.0:80
|
||||
- ConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data}
|
||||
- Database=LocationsDb
|
||||
|
Binary file not shown.
Binary file not shown.
BIN
img/loadtests/k8ssettings.PNG
Normal file
BIN
img/loadtests/k8ssettings.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
BIN
img/loadtests/loadtestproj_dir.PNG
Normal file
BIN
img/loadtests/loadtestproj_dir.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.0 KiB |
BIN
img/loadtests/runloadtest.PNG
Normal file
BIN
img/loadtests/runloadtest.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
img/loadtests/sfmanifestsettings.PNG
Normal file
BIN
img/loadtests/sfmanifestsettings.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
@ -5,9 +5,12 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions
|
||||
{
|
||||
public interface IEventBus
|
||||
{
|
||||
void Publish(IntegrationEvent @event);
|
||||
|
||||
void Subscribe<T, TH>()
|
||||
where T : IntegrationEvent
|
||||
where TH : IIntegrationEventHandler<T>;
|
||||
|
||||
void SubscribeDynamic<TH>(string eventName)
|
||||
where TH : IDynamicIntegrationEventHandler;
|
||||
|
||||
@ -17,7 +20,5 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions
|
||||
void Unsubscribe<T, TH>()
|
||||
where TH : IIntegrationEventHandler<T>
|
||||
where T : IntegrationEvent;
|
||||
|
||||
void Publish(IntegrationEvent @event);
|
||||
}
|
||||
}
|
||||
|
@ -1,145 +0,0 @@
|
||||
//using Microsoft.eShopOnContainers.BuildingBlocks.CommandBus;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Polly;
|
||||
using Polly.Retry;
|
||||
using RabbitMQ.Client;
|
||||
using RabbitMQ.Client.Events;
|
||||
using RabbitMQ.Client.Exceptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/*
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||
{
|
||||
public class CommandBusRabbitMQ : ICommandBus, IDisposable
|
||||
{
|
||||
const string BROKER_NAME = "eshop_command_bus";
|
||||
|
||||
private readonly IRabbitMQPersistentConnection _persistentConnection;
|
||||
private readonly ILogger<CommandBusRabbitMQ> _logger;
|
||||
|
||||
private IModel _consumerChannel;
|
||||
private string _queueName;
|
||||
|
||||
private readonly Dictionary<string, IIntegrationCommandHandler> _handlers;
|
||||
private readonly Dictionary<string, Type> _typeMappings;
|
||||
|
||||
public CommandBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection,
|
||||
ILogger<CommandBusRabbitMQ> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_persistentConnection = persistentConnection;
|
||||
_handlers = new Dictionary<string, IIntegrationCommandHandler>();
|
||||
_typeMappings = new Dictionary<string, Type>();
|
||||
}
|
||||
|
||||
public void Send<T>(string name, T data)
|
||||
{
|
||||
Send(new IntegrationCommand<T>(name, data));
|
||||
}
|
||||
|
||||
public void Handle<TC>(string name, IIntegrationCommandHandler<TC> handler)
|
||||
{
|
||||
_handlers.Add(name, handler);
|
||||
_typeMappings.Add(name, typeof(TC));
|
||||
}
|
||||
|
||||
public void Handle(string name, IIntegrationCommandHandler handler)
|
||||
{
|
||||
_handlers.Add(name, handler);
|
||||
}
|
||||
public void Handle<TI, TC>(TI handler) where TI : IIntegrationCommandHandler<TC>
|
||||
{
|
||||
var name = typeof(TI).Name;
|
||||
_handlers.Add(name, handler);
|
||||
_typeMappings.Add(name, typeof(TC));
|
||||
}
|
||||
|
||||
private void Send<T>(IntegrationCommand<T> command)
|
||||
{
|
||||
if (!_persistentConnection.IsConnected)
|
||||
{
|
||||
_persistentConnection.TryConnect();
|
||||
}
|
||||
|
||||
var policy = RetryPolicy.Handle<BrokerUnreachableException>()
|
||||
.Or<SocketException>()
|
||||
.WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) =>
|
||||
{
|
||||
_logger.LogWarning(ex.ToString());
|
||||
});
|
||||
|
||||
using (var channel = _persistentConnection.CreateModel())
|
||||
{
|
||||
var commandName = command.Name;
|
||||
channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct");
|
||||
var message = JsonConvert.SerializeObject(command);
|
||||
var body = Encoding.UTF8.GetBytes(message);
|
||||
policy.Execute(() =>
|
||||
{
|
||||
channel.BasicPublish(exchange: BROKER_NAME,
|
||||
routingKey: commandName,
|
||||
basicProperties: null,
|
||||
body: body);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private IModel CreateConsumerChannel()
|
||||
{
|
||||
if (!_persistentConnection.IsConnected)
|
||||
{
|
||||
_persistentConnection.TryConnect();
|
||||
}
|
||||
|
||||
var channel = _persistentConnection.CreateModel();
|
||||
|
||||
channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct");
|
||||
_queueName = channel.QueueDeclare().QueueName;
|
||||
var consumer = new EventingBasicConsumer(channel);
|
||||
consumer.Received += async (model, ea) =>
|
||||
{
|
||||
var commandName = ea.RoutingKey;
|
||||
var message = Encoding.UTF8.GetString(ea.Body);
|
||||
await InvokeHandler(commandName, message);
|
||||
};
|
||||
|
||||
channel.BasicConsume(queue: _queueName,
|
||||
noAck: true,
|
||||
consumer: consumer);
|
||||
|
||||
channel.CallbackException += (sender, ea) =>
|
||||
{
|
||||
_consumerChannel.Dispose();
|
||||
_consumerChannel = CreateConsumerChannel();
|
||||
};
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
private Task InvokeHandler(string commandName, string message)
|
||||
{
|
||||
if (_handlers.ContainsKey(commandName))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_consumerChannel != null)
|
||||
{
|
||||
_consumerChannel.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
@ -32,11 +32,12 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||
private string _queueName;
|
||||
|
||||
public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger<EventBusRabbitMQ> logger,
|
||||
ILifetimeScope autofac, IEventBusSubscriptionsManager subsManager, int retryCount = 5)
|
||||
ILifetimeScope autofac, IEventBusSubscriptionsManager subsManager, string queueName = null, int retryCount = 5)
|
||||
{
|
||||
_persistentConnection = persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager();
|
||||
_queueName = queueName;
|
||||
_consumerChannel = CreateConsumerChannel();
|
||||
_autofac = autofac;
|
||||
_retryCount = retryCount;
|
||||
@ -147,7 +148,6 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||
_subsManager.RemoveDynamicSubscription<TH>(eventName);
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_consumerChannel != null)
|
||||
@ -170,7 +170,12 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||
channel.ExchangeDeclare(exchange: BROKER_NAME,
|
||||
type: "direct");
|
||||
|
||||
_queueName = channel.QueueDeclare().QueueName;
|
||||
channel.QueueDeclare(queue: _queueName,
|
||||
durable: true,
|
||||
exclusive: false,
|
||||
autoDelete: false,
|
||||
arguments: null);
|
||||
|
||||
|
||||
var consumer = new EventingBasicConsumer(channel);
|
||||
consumer.Received += async (model, ea) =>
|
||||
@ -196,8 +201,6 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
|
||||
|
||||
private async Task ProcessEvent(string eventName, string message)
|
||||
{
|
||||
|
||||
|
||||
if (_subsManager.HasSubscriptionsForEvent(eventName))
|
||||
{
|
||||
using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME))
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Polly" Version="5.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,7 +1,10 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Polly;
|
||||
using Polly.Retry;
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace Microsoft.AspNetCore.Hosting
|
||||
{
|
||||
@ -21,10 +24,26 @@ namespace Microsoft.AspNetCore.Hosting
|
||||
{
|
||||
logger.LogInformation($"Migrating database associated with context {typeof(TContext).Name}");
|
||||
|
||||
context.Database
|
||||
var retry = Policy.Handle<SqlException>()
|
||||
.WaitAndRetry(new TimeSpan[]
|
||||
{
|
||||
TimeSpan.FromSeconds(5),
|
||||
TimeSpan.FromSeconds(10),
|
||||
TimeSpan.FromSeconds(15),
|
||||
});
|
||||
|
||||
retry.Execute(() =>
|
||||
{
|
||||
//if the sql server container is not created on run docker compose this
|
||||
//migration can't fail for network related exception. The retry options for DbContext only
|
||||
//apply to transient exceptions.
|
||||
|
||||
context.Database
|
||||
.Migrate();
|
||||
|
||||
seeder(context,services);
|
||||
seeder(context, services);
|
||||
});
|
||||
|
||||
|
||||
logger.LogInformation($"Migrated database associated with context {typeof(TContext).Name}");
|
||||
}
|
||||
|
@ -34,148 +34,139 @@
|
||||
<Color x:Key="AndroidListViewBackgroundColor">Transparent</Color>
|
||||
<Color x:Key="iOSListViewBackgroundColor">Transparent</Color>
|
||||
|
||||
<OnPlatform
|
||||
x:TypeArguments="Color"
|
||||
x:Key="ActivityIndicatorColor"
|
||||
iOS="{ StaticResource iOSDefaultTintColor }" />
|
||||
<OnPlatform x:TypeArguments="Color"
|
||||
x:Key="ActivityIndicatorColor">
|
||||
<On Platform="iOS" Value="{ StaticResource iOSDefaultTintColor }" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:TypeArguments="Color"
|
||||
x:Key="DefaultButtonClassBackgroundColor"
|
||||
Android="{ StaticResource AndroidDefaultButtonClassBackgroundColor }"
|
||||
iOS="{ StaticResource iOSDefaultButtonClassBackgroundColor }" />
|
||||
<OnPlatform x:TypeArguments="Color"
|
||||
x:Key="DefaultButtonClassBackgroundColor">
|
||||
<On Platform="iOS" Value="{ StaticResource iOSDefaultButtonClassBackgroundColor }" />
|
||||
<On Platform="Android" Value="{ StaticResource AndroidDefaultButtonClassBackgroundColor }" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:TypeArguments="Color"
|
||||
x:Key="DefaultButtonClassBorderColor"
|
||||
Android="{ StaticResource AndroidDefaultButtonClassBorderColor }"
|
||||
iOS="{ StaticResource iOSDefaultButtonClassBorderColor }" />
|
||||
<OnPlatform x:TypeArguments="Color"
|
||||
x:Key="DefaultButtonClassBorderColor">
|
||||
<On Platform="iOS" Value="{ StaticResource iOSDefaultButtonClassBorderColor }" />
|
||||
<On Platform="Android" Value="{ StaticResource AndroidDefaultButtonClassBorderColor }" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:TypeArguments="Color"
|
||||
x:Key="DefaultButtonClassTextColor"
|
||||
Android="{ StaticResource AndroidDefaultButtonClassTextColor }"
|
||||
iOS="{ StaticResource iOSDefaultButtonClassTextColor }" />
|
||||
<OnPlatform x:TypeArguments="Color"
|
||||
x:Key="DefaultButtonClassTextColor">
|
||||
<On Platform="iOS" Value="{ StaticResource iOSDefaultButtonClassTextColor }" />
|
||||
<On Platform="Android" Value="{ StaticResource AndroidDefaultButtonClassTextColor }" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:TypeArguments="Color"
|
||||
x:Key="EntryBackgroundColor"
|
||||
Android="{ StaticResource AndroidEntryBackgroundColor }"
|
||||
iOS="{ StaticResource iOSEntryBackgroundColor }" />
|
||||
<OnPlatform x:TypeArguments="Color"
|
||||
x:Key="EntryBackgroundColor">
|
||||
<On Platform="iOS" Value="{ StaticResource iOSEntryBackgroundColor }" />
|
||||
<On Platform="Android" Value="{ StaticResource AndroidEntryBackgroundColor }" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:TypeArguments="Color"
|
||||
x:Key="ThemeListViewBackgroundColor"
|
||||
Android="{ StaticResource AndroidListViewBackgroundColor }"
|
||||
iOS="{ StaticResource iOSListViewBackgroundColor }" />
|
||||
<OnPlatform x:TypeArguments="Color"
|
||||
x:Key="ThemeListViewBackgroundColor">
|
||||
<On Platform="iOS" Value="{ StaticResource iOSListViewBackgroundColor }" />
|
||||
<On Platform="Android" Value="{ StaticResource AndroidListViewBackgroundColor }" />
|
||||
</OnPlatform>
|
||||
|
||||
<!-- SIZES -->
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
x:Key="BaseButtonBorderRadius"
|
||||
iOS="6" />
|
||||
<OnPlatform x:TypeArguments="x:Double"
|
||||
x:Key="BaseButtonBorderRadius">
|
||||
<On Platform="iOS" Value="6" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
x:Key="BaseButtonBorderWidth"
|
||||
Android="0"
|
||||
iOS="0" />
|
||||
<OnPlatform x:TypeArguments="x:Double"
|
||||
x:Key="BaseButtonBorderWidth">
|
||||
<On Platform="iOS, Android" Value="0" />
|
||||
</OnPlatform>
|
||||
|
||||
<!-- FONTS -->
|
||||
<OnPlatform
|
||||
x:Key="MontserratRegular"
|
||||
x:TypeArguments="x:String"
|
||||
iOS="Montserrat-Regular"
|
||||
Android="Montserrat-Regular.ttf#Montserrat"
|
||||
WinPhone="Assets/Fonts/Montserrat-Regular.ttf#Montserrat"/>
|
||||
<OnPlatform x:Key="MontserratRegular"
|
||||
x:TypeArguments="x:String">
|
||||
<On Platform="iOS" Value="Montserrat-Regular" />
|
||||
<On Platform="Android" Value="Montserrat-Regular.ttf#Montserrat" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/Fonts/Montserrat-Regular.ttf#Montserrat" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:Key="MontserratBold"
|
||||
x:TypeArguments="x:String"
|
||||
iOS="Montserrat-Bold"
|
||||
Android="Montserrat-Bold.ttf#Montserrat"
|
||||
WinPhone="Assets/Fonts/Montserrat-Bold.ttf#Montserrat"/>
|
||||
<OnPlatform x:Key="MontserratBold"
|
||||
x:TypeArguments="x:String">
|
||||
<On Platform="iOS" Value="Montserrat-Bold" />
|
||||
<On Platform="Android" Value="Montserrat-Bold.ttf#Montserrat" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/Fonts/Montserrat-Bold.ttf#Montserrat" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:Key="SourceSansProRegular"
|
||||
x:TypeArguments="x:String"
|
||||
iOS="SourceSansPro-Regular"
|
||||
Android="SourceSansPro-Regular.ttf#Source Sans Pro"
|
||||
WinPhone="Assets/Fonts/SourceSansPro-Regular.ttf#Source Sans Pro"/>
|
||||
<OnPlatform x:Key="SourceSansProRegular"
|
||||
x:TypeArguments="x:String">
|
||||
<On Platform="iOS" Value="SourceSansPro-Regular" />
|
||||
<On Platform="Android" Value="SourceSansPro-Regular.ttf#Source Sans Pro" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/Fonts/SourceSansPro-Regular.ttf#Source Sans Pro" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
x:Key="BaseButtonFontSize"
|
||||
Android="16"
|
||||
iOS="18" />
|
||||
<OnPlatform x:TypeArguments="x:Double"
|
||||
x:Key="BaseButtonFontSize">
|
||||
<On Platform="iOS" Value="18" />
|
||||
<On Platform="Android" Value="16" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
x:Key="BaseFontSize"
|
||||
Android="15"
|
||||
iOS="16" />
|
||||
<OnPlatform x:TypeArguments="x:Double"
|
||||
x:Key="BaseFontSize">
|
||||
<On Platform="iOS" Value="16" />
|
||||
<On Platform="Android" Value="15" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:Key="LittleSize"
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="11"
|
||||
Android="12"
|
||||
WinPhone="12"/>
|
||||
<OnPlatform x:Key="LittleSize"
|
||||
x:TypeArguments="x:Double">
|
||||
<On Platform="iOS" Value="11" />
|
||||
<On Platform="Android, UWP, WinRT, WinPhone" Value="12" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:Key="MidMediumSize"
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="12"
|
||||
Android="14"
|
||||
WinPhone="14"/>
|
||||
<OnPlatform x:Key="MidMediumSize"
|
||||
x:TypeArguments="x:Double">
|
||||
<On Platform="iOS" Value="12" />
|
||||
<On Platform="Android, UWP, WinRT, WinPhone" Value="14" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:Key="MediumSize"
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="14"
|
||||
Android="16"
|
||||
WinPhone="16"/>
|
||||
<OnPlatform x:Key="MediumSize"
|
||||
x:TypeArguments="x:Double">
|
||||
<On Platform="iOS" Value="14" />
|
||||
<On Platform="Android, UWP, WinRT, WinPhone" Value="16" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:Key="LargeSize"
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="16"
|
||||
Android="18"
|
||||
WinPhone="18"/>
|
||||
<OnPlatform x:Key="LargeSize"
|
||||
x:TypeArguments="x:Double">
|
||||
<On Platform="iOS" Value="16" />
|
||||
<On Platform="Android, UWP, WinRT, WinPhone" Value="18" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:Key="LargerSize"
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="18"
|
||||
Android="20"
|
||||
WinPhone="20"/>
|
||||
<OnPlatform x:Key="LargerSize"
|
||||
x:TypeArguments="x:Double">
|
||||
<On Platform="iOS" Value="18" />
|
||||
<On Platform="Android, UWP, WinRT, WinPhone" Value="20" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:Key="BigSize"
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="20"
|
||||
Android="24"
|
||||
WinPhone="24"/>
|
||||
<OnPlatform x:Key="BigSize"
|
||||
x:TypeArguments="x:Double">
|
||||
<On Platform="iOS" Value="20" />
|
||||
<On Platform="Android, UWP, WinRT, WinPhone" Value="24" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:Key="ExtraBigSize"
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="24"
|
||||
Android="32"
|
||||
WinPhone="32"/>
|
||||
<OnPlatform x:Key="ExtraBigSize"
|
||||
x:TypeArguments="x:Double">
|
||||
<On Platform="iOS" Value="24" />
|
||||
<On Platform="Android, UWP, WinRT, WinPhone" Value="32" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:Key="HugeSize"
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="32"
|
||||
Android="48"
|
||||
WinPhone="48"/>
|
||||
<OnPlatform x:Key="HugeSize"
|
||||
x:TypeArguments="x:Double">
|
||||
<On Platform="iOS" Value="32" />
|
||||
<On Platform="Android, UWP, WinRT, WinPhone" Value="48" />
|
||||
</OnPlatform>
|
||||
|
||||
<OnPlatform
|
||||
x:TypeArguments="FontAttributes"
|
||||
x:Key="BaseButtonFontAttributes"
|
||||
Android="None"
|
||||
iOS="Bold" />
|
||||
<OnPlatform x:TypeArguments="FontAttributes"
|
||||
x:Key="BaseButtonFontAttributes">
|
||||
<On Platform="iOS" Value="Bold" />
|
||||
<On Platform="Android" Value="None" />
|
||||
</OnPlatform>
|
||||
|
||||
<!-- CONVERTERS -->
|
||||
<converters:CountToBoolConverter x:Key="CountToBoolConverter" />
|
||||
|
@ -44,11 +44,10 @@
|
||||
<!-- ANDROID -->
|
||||
<Grid>
|
||||
<Grid.IsVisible>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Boolean"
|
||||
Android="True"
|
||||
iOS="True"
|
||||
WinPhone="False"/>
|
||||
<OnPlatform x:TypeArguments="x:Boolean">
|
||||
<On Platform="iOS, Android" Value="True" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="False" />
|
||||
</OnPlatform>
|
||||
</Grid.IsVisible>
|
||||
<BoxView
|
||||
BackgroundColor="{StaticResource LightGreenColor}"
|
||||
@ -64,11 +63,10 @@
|
||||
<!-- IOS & UWP -->
|
||||
<Grid>
|
||||
<Grid.IsVisible>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Boolean"
|
||||
Android="False"
|
||||
iOS="False"
|
||||
WinPhone="True"/>
|
||||
<OnPlatform x:TypeArguments="x:Boolean">
|
||||
<On Platform="iOS, Android" Value="False" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="True" />
|
||||
</OnPlatform>
|
||||
</Grid.IsVisible>
|
||||
<Image
|
||||
Source="Assets\circle_button_background.png"
|
||||
|
@ -68,6 +68,7 @@ namespace eShopOnContainers.Core.Services.Order
|
||||
CardSecurityNumber = order.CardSecurityNumber,
|
||||
CardTypeId = order.CardTypeId,
|
||||
City = order.ShippingCity,
|
||||
State = order.ShippingState,
|
||||
Country = order.ShippingCountry,
|
||||
ZipCode = order.ShippingZipCode,
|
||||
Street = order.ShippingStreet
|
||||
|
@ -165,7 +165,7 @@ namespace eShopOnContainers.Core.ViewModels
|
||||
await NavigationService.RemoveLastFromBackStackAsync();
|
||||
|
||||
// Show Dialog
|
||||
await DialogService.ShowAlertAsync("Order sent successfully!", string.Format("Order {0}", Order.OrderNumber), "Ok");
|
||||
await DialogService.ShowAlertAsync("Order sent successfully!", "Checkout", "Ok");
|
||||
await NavigationService.RemoveLastFromBackStackAsync();
|
||||
}
|
||||
catch
|
||||
|
@ -171,11 +171,10 @@
|
||||
VerticalOptions="Center"
|
||||
HorizontalOptions="Center">
|
||||
<ActivityIndicator.WidthRequest>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="100"
|
||||
Android="100"
|
||||
WinPhone="400" />
|
||||
<OnPlatform x:TypeArguments="x:Double">
|
||||
<On Platform="iOS, Android" Value="100" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="400" />
|
||||
</OnPlatform>
|
||||
</ActivityIndicator.WidthRequest>
|
||||
</ActivityIndicator>
|
||||
</Grid>
|
||||
|
@ -90,11 +90,10 @@
|
||||
VerticalOptions="Center"
|
||||
HorizontalOptions="Center">
|
||||
<ActivityIndicator.WidthRequest>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="100"
|
||||
Android="100"
|
||||
WinPhone="400" />
|
||||
<OnPlatform x:TypeArguments="x:Double">
|
||||
<On Platform="iOS, Android" Value="100" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="400" />
|
||||
</OnPlatform>
|
||||
</ActivityIndicator.WidthRequest>
|
||||
</ActivityIndicator>
|
||||
</Grid>
|
||||
|
@ -114,11 +114,10 @@
|
||||
VerticalOptions="Center"
|
||||
HorizontalOptions="Center">
|
||||
<ActivityIndicator.WidthRequest>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="100"
|
||||
Android="100"
|
||||
WinPhone="400" />
|
||||
<OnPlatform x:TypeArguments="x:Double">
|
||||
<On Platform="iOS, Android" Value="100" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="400" />
|
||||
</OnPlatform>
|
||||
</ActivityIndicator.WidthRequest>
|
||||
</ActivityIndicator>
|
||||
</Grid>
|
||||
|
@ -231,11 +231,10 @@
|
||||
VerticalOptions="Center"
|
||||
HorizontalOptions="Center">
|
||||
<ActivityIndicator.WidthRequest>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="100"
|
||||
Android="100"
|
||||
WinPhone="400" />
|
||||
<OnPlatform x:TypeArguments="x:Double">
|
||||
<On Platform="iOS, Android" Value="100" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="400" />
|
||||
</OnPlatform>
|
||||
</ActivityIndicator.WidthRequest>
|
||||
</ActivityIndicator>
|
||||
</Grid>
|
||||
|
@ -76,11 +76,10 @@
|
||||
SelectedItem="{Binding Brand, Mode=TwoWay}"
|
||||
Style="{StaticResource FilterPickerStyle}">
|
||||
<Picker.HeightRequest>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
Android="48"
|
||||
iOS="48"
|
||||
WinPhone="36"/>
|
||||
<OnPlatform x:TypeArguments="x:Double">
|
||||
<On Platform="iOS, Android" Value="48" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="36" />
|
||||
</OnPlatform>
|
||||
</Picker.HeightRequest>
|
||||
</Picker>
|
||||
<!-- TYPE -->
|
||||
@ -91,11 +90,10 @@
|
||||
SelectedItem="{Binding Type, Mode=TwoWay}"
|
||||
Style="{StaticResource FilterPickerStyle}">
|
||||
<Picker.HeightRequest>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
Android="48"
|
||||
iOS="48"
|
||||
WinPhone="36"/>
|
||||
<OnPlatform x:TypeArguments="x:Double">
|
||||
<On Platform="iOS, Android" Value="48" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="36" />
|
||||
</OnPlatform>
|
||||
</Picker.HeightRequest>
|
||||
</Picker>
|
||||
<Button
|
||||
|
@ -9,10 +9,9 @@
|
||||
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
|
||||
viewModelBase:ViewModelLocator.AutoWireViewModel="true">
|
||||
<ContentPage.Title>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:String"
|
||||
iOS="eShop on Containers"
|
||||
WinPhone="eShop on Containers"/>
|
||||
<OnPlatform x:TypeArguments="x:String">
|
||||
<On Platform="iOS, UWP, WinRT, WinPhone" Value="eShop on Containers" />
|
||||
</OnPlatform>
|
||||
</ContentPage.Title>
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
@ -179,10 +178,10 @@
|
||||
Style="{StaticResource HeaderLabelStyle}" />
|
||||
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
|
||||
<Entry.Style>
|
||||
<OnPlatform x:TypeArguments="Style"
|
||||
iOS="{StaticResource EntryStyle}"
|
||||
Android="{StaticResource EntryStyle}"
|
||||
WinPhone="{StaticResource UwpEntryStyle}"/>
|
||||
<OnPlatform x:TypeArguments="Style">
|
||||
<On Platform="iOS, Android" Value="{StaticResource EntryStyle}" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="{StaticResource UwpEntryStyle}" />
|
||||
</OnPlatform>
|
||||
</Entry.Style>
|
||||
<Entry.Behaviors>
|
||||
<behaviors:EventToCommandBehavior
|
||||
@ -208,10 +207,10 @@
|
||||
IsPassword="True"
|
||||
Text="{Binding Password.Value, Mode=TwoWay}">
|
||||
<Entry.Style>
|
||||
<OnPlatform x:TypeArguments="Style"
|
||||
iOS="{StaticResource EntryStyle}"
|
||||
Android="{StaticResource EntryStyle}"
|
||||
WinPhone="{StaticResource UwpEntryStyle}"/>
|
||||
<OnPlatform x:TypeArguments="Style">
|
||||
<On Platform="iOS, Android" Value="{StaticResource EntryStyle}" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="{StaticResource UwpEntryStyle}" />
|
||||
</OnPlatform>
|
||||
</Entry.Style>
|
||||
<Entry.Behaviors>
|
||||
<behaviors:EventToCommandBehavior
|
||||
@ -270,11 +269,10 @@
|
||||
Grid.ColumnSpan="3"
|
||||
Aspect="AspectFill">
|
||||
<Image.Source>
|
||||
<OnPlatform
|
||||
x:TypeArguments="ImageSource"
|
||||
Android="banner.png"
|
||||
iOS="banner.png"
|
||||
WinPhone="Assets\banner.png"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS, Android" Value="banner.png" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets\banner.png" />
|
||||
</OnPlatform>
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<Grid
|
||||
@ -319,11 +317,10 @@
|
||||
<Image
|
||||
Style="{StaticResource SettingsImageStyle}">
|
||||
<Image.Source>
|
||||
<OnPlatform
|
||||
x:TypeArguments="ImageSource"
|
||||
WinPhone="Assets/app_settings.png"
|
||||
Android="app_settings"
|
||||
iOS="app_settings"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS, Android" Value="app_settings" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/app_settings.png" />
|
||||
</OnPlatform>
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<Grid.GestureRecognizers>
|
||||
@ -345,24 +342,22 @@
|
||||
AbsoluteLayout.LayoutFlags="All">
|
||||
<WebView.Behaviors>
|
||||
<OnPlatform x:TypeArguments="Behavior">
|
||||
<OnPlatform.Android>
|
||||
<behaviors:EventToCommandBehavior
|
||||
EventName="Navigating"
|
||||
EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}"
|
||||
Command="{Binding NavigateCommand}" />
|
||||
</OnPlatform.Android>
|
||||
<OnPlatform.iOS>
|
||||
<behaviors:EventToCommandBehavior
|
||||
EventName="Navigating"
|
||||
EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}"
|
||||
Command="{Binding NavigateCommand}" />
|
||||
</OnPlatform.iOS>
|
||||
<OnPlatform.WinPhone>
|
||||
<behaviors:EventToCommandBehavior
|
||||
EventName="Navigated"
|
||||
EventArgsConverter="{StaticResource WebNavigatedEventArgsConverter}"
|
||||
Command="{Binding NavigateCommand}" />
|
||||
</OnPlatform.WinPhone>
|
||||
<On Platform="iOS, Android">
|
||||
<On.Value>
|
||||
<behaviors:EventToCommandBehavior
|
||||
EventName="Navigating"
|
||||
EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}"
|
||||
Command="{Binding NavigateCommand}" />
|
||||
</On.Value>
|
||||
</On>
|
||||
<On Platform="UWP">
|
||||
<On.Value>
|
||||
<behaviors:EventToCommandBehavior
|
||||
EventName="Navigated"
|
||||
EventArgsConverter="{StaticResource WebNavigatedEventArgsConverter}"
|
||||
Command="{Binding NavigateCommand}" />
|
||||
</On.Value>
|
||||
</On>
|
||||
</OnPlatform>
|
||||
</WebView.Behaviors>
|
||||
</WebView>
|
||||
@ -376,11 +371,10 @@
|
||||
VerticalOptions="Center"
|
||||
HorizontalOptions="Center">
|
||||
<ActivityIndicator.WidthRequest>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="100"
|
||||
Android="100"
|
||||
WinPhone="400" />
|
||||
<OnPlatform x:TypeArguments="x:Double">
|
||||
<On Platform="iOS, Android" Value="100" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="400" />
|
||||
</OnPlatform>
|
||||
</ActivityIndicator.WidthRequest>
|
||||
</ActivityIndicator>
|
||||
</Grid>
|
||||
|
@ -10,21 +10,19 @@
|
||||
BarTextColor="{StaticResource WhiteColor}"
|
||||
viewModelBase:ViewModelLocator.AutoWireViewModel="true">
|
||||
<TabbedPage.Title>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:String"
|
||||
iOS="eShop on Containers"
|
||||
WinPhone="eShop on Containers"/>
|
||||
<OnPlatform x:TypeArguments="x:String">
|
||||
<On Platform="iOS, UWP, WinRT, WinPhone" Value="eShop on Containers" />
|
||||
</OnPlatform>
|
||||
</TabbedPage.Title>
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem
|
||||
Command="{Binding SettingsCommand}"
|
||||
Text="Settings">
|
||||
<ToolbarItem.Icon>
|
||||
<OnPlatform
|
||||
x:TypeArguments="FileImageSource"
|
||||
WinPhone="Assets/app_settings.png"
|
||||
Android="app_settings"
|
||||
iOS="app_settings"/>
|
||||
<OnPlatform x:TypeArguments="FileImageSource">
|
||||
<On Platform="iOS, Android" Value="app_settings" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/app_settings.png" />
|
||||
</OnPlatform>
|
||||
</ToolbarItem.Icon>
|
||||
</ToolbarItem>
|
||||
</ContentPage.ToolbarItems>
|
||||
@ -32,22 +30,20 @@
|
||||
<views:CatalogView
|
||||
x:Name="HomeView">
|
||||
<views:CatalogView.Icon>
|
||||
<OnPlatform
|
||||
x:TypeArguments="FileImageSource"
|
||||
Android="menu_filter"
|
||||
iOS="menu_filter"
|
||||
WinPhone="Assets\menu_filter.png"/>
|
||||
<OnPlatform x:TypeArguments="FileImageSource">
|
||||
<On Platform="iOS, Android" Value="menu_filter" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets\menu_filter.png" />
|
||||
</OnPlatform>
|
||||
</views:CatalogView.Icon>
|
||||
</views:CatalogView>
|
||||
<!-- PROFILE -->
|
||||
<views:ProfileView
|
||||
x:Name="ProfileView">
|
||||
<views:ProfileView.Icon>
|
||||
<OnPlatform
|
||||
x:TypeArguments="FileImageSource"
|
||||
Android="menu_profile"
|
||||
iOS="menu_profile"
|
||||
WinPhone="Assets\menu_profile.png"/>
|
||||
<OnPlatform x:TypeArguments="FileImageSource">
|
||||
<On Platform="iOS, Android" Value="menu_profile" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets\menu_profile.png" />
|
||||
</OnPlatform>
|
||||
</views:ProfileView.Icon>
|
||||
</views:ProfileView>
|
||||
<!-- BASKET -->
|
||||
@ -56,22 +52,20 @@
|
||||
controls:CustomTabbedPage.BadgeText="{Binding BadgeCount}"
|
||||
controls:CustomTabbedPage.BadgeColor="{StaticResource LightGreenColor}">
|
||||
<views:BasketView.Icon>
|
||||
<OnPlatform
|
||||
x:TypeArguments="FileImageSource"
|
||||
Android="menu_cart"
|
||||
iOS="menu_cart"
|
||||
WinPhone="Assets\menu_cart.png"/>
|
||||
<OnPlatform x:TypeArguments="FileImageSource">
|
||||
<On Platform="iOS, Android" Value="menu_cart" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets\menu_cart.png" />
|
||||
</OnPlatform>
|
||||
</views:BasketView.Icon>
|
||||
</views:BasketView>
|
||||
<!-- CAMPAIGNS -->
|
||||
<views:CampaignView
|
||||
x:Name="CampaignView">
|
||||
<views:CampaignView.Icon>
|
||||
<OnPlatform
|
||||
x:TypeArguments="FileImageSource"
|
||||
Android="menu_campaigns"
|
||||
iOS="menu_campaigns"
|
||||
WinPhone="Assets\menu_campaigns.png"/>
|
||||
<OnPlatform x:TypeArguments="FileImageSource">
|
||||
<On Platform="iOS, Android" Value="menu_campaigns" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets\menu_campaigns.png" />
|
||||
</OnPlatform>
|
||||
</views:CampaignView.Icon>
|
||||
</views:CampaignView>
|
||||
</TabbedPage>
|
@ -264,11 +264,10 @@
|
||||
VerticalOptions="Center"
|
||||
HorizontalOptions="Center">
|
||||
<ActivityIndicator.WidthRequest>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:Double"
|
||||
iOS="100"
|
||||
Android="100"
|
||||
WinPhone="400" />
|
||||
<OnPlatform x:TypeArguments="x:Double">
|
||||
<On Platform="iOS, Android" Value="100" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="400" />
|
||||
</OnPlatform>
|
||||
</ActivityIndicator.WidthRequest>
|
||||
</ActivityIndicator>
|
||||
</Grid>
|
||||
|
@ -99,11 +99,10 @@
|
||||
<animations:FadeInAnimation
|
||||
Direction="Up">
|
||||
<animations:FadeInAnimation.Duration>
|
||||
<OnPlatform
|
||||
x:TypeArguments="x:String"
|
||||
Android="500"
|
||||
iOS="0"
|
||||
WinPhone="500"/>
|
||||
<OnPlatform x:TypeArguments="x:String">
|
||||
<On Platform="iOS" Value="0" />
|
||||
<On Platform="Android, UWP, WinRT, WinPhone" Value="500" />
|
||||
</OnPlatform>
|
||||
</animations:FadeInAnimation.Duration>
|
||||
</animations:FadeInAnimation>
|
||||
</animations:StoryBoard>
|
||||
@ -156,16 +155,18 @@
|
||||
Command="{Binding ToggleMockServicesCommand}"
|
||||
Style="{StaticResource SettingsToggleButtonStyle}">
|
||||
<controls:ToggleButton.CheckedImage>
|
||||
<OnPlatform x:TypeArguments="ImageSource"
|
||||
Android="switch_on.png"
|
||||
iOS="switchOn.png"
|
||||
WinPhone="Assets/switchOn.png"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS" Value="switchOn.png" />
|
||||
<On Platform="Android" Value="switch_on.png" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOn.png" />
|
||||
</OnPlatform>
|
||||
</controls:ToggleButton.CheckedImage>
|
||||
<controls:ToggleButton.UnCheckedImage>
|
||||
<OnPlatform x:TypeArguments="ImageSource"
|
||||
Android="switch_off.png"
|
||||
iOS="switchOff.png"
|
||||
WinPhone="Assets/switchOff.png"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS" Value="switchOff.png" />
|
||||
<On Platform="Android" Value="switch_off.png" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOff.png" />
|
||||
</OnPlatform>
|
||||
</controls:ToggleButton.UnCheckedImage>
|
||||
</controls:ToggleButton>
|
||||
<!-- ENDPOINT -->
|
||||
@ -181,11 +182,10 @@
|
||||
<Entry
|
||||
Text="{Binding Endpoint, Mode=TwoWay}">
|
||||
<Entry.Style>
|
||||
<OnPlatform
|
||||
x:TypeArguments="Style"
|
||||
iOS="{StaticResource SettingsEntryStyle}"
|
||||
Android="{StaticResource SettingsEntryStyle}"
|
||||
WinPhone="{StaticResource SettingsUwpEntryStyle}"/>
|
||||
<OnPlatform x:TypeArguments="Style">
|
||||
<On Platform="iOS, Android" Value="{StaticResource SettingsEntryStyle}" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="{StaticResource SettingsUwpEntryStyle}" />
|
||||
</OnPlatform>
|
||||
</Entry.Style>
|
||||
</Entry>
|
||||
</StackLayout>
|
||||
@ -212,16 +212,18 @@
|
||||
Style="{StaticResource SettingsToggleButtonStyle}"
|
||||
IsVisible="{Binding UserIsLogged}">
|
||||
<controls:ToggleButton.CheckedImage>
|
||||
<OnPlatform x:TypeArguments="ImageSource"
|
||||
Android="switch_on.png"
|
||||
iOS="switchOn.png"
|
||||
WinPhone="Assets/switchOn.png"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS" Value="switchOn.png" />
|
||||
<On Platform="Android" Value="switch_on.png" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOn.png" />
|
||||
</OnPlatform>
|
||||
</controls:ToggleButton.CheckedImage>
|
||||
<controls:ToggleButton.UnCheckedImage>
|
||||
<OnPlatform x:TypeArguments="ImageSource"
|
||||
Android="switch_off.png"
|
||||
iOS="switchOff.png"
|
||||
WinPhone="Assets/switchOff.png"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS" Value="switchOff.png" />
|
||||
<On Platform="Android" Value="switch_off.png" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOff.png" />
|
||||
</OnPlatform>
|
||||
</controls:ToggleButton.UnCheckedImage>
|
||||
</controls:ToggleButton>
|
||||
<!-- FAKE LOCATIONS -->
|
||||
@ -238,11 +240,10 @@
|
||||
Text="{Binding Latitude, Mode=TwoWay, Converter={StaticResource DoubleConverter}}"
|
||||
Keyboard="Text">
|
||||
<Entry.Style>
|
||||
<OnPlatform
|
||||
x:TypeArguments="Style"
|
||||
iOS="{StaticResource SettingsEntryStyle}"
|
||||
Android="{StaticResource SettingsEntryStyle}"
|
||||
WinPhone="{StaticResource SettingsUwpEntryStyle}"/>
|
||||
<OnPlatform x:TypeArguments="Style">
|
||||
<On Platform="iOS, Android" Value="{StaticResource SettingsEntryStyle}" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="{StaticResource SettingsUwpEntryStyle}" />
|
||||
</OnPlatform>
|
||||
</Entry.Style>
|
||||
</Entry>
|
||||
<Label
|
||||
@ -252,11 +253,10 @@
|
||||
Text="{Binding Longitude, Mode=TwoWay, Converter={StaticResource DoubleConverter}}"
|
||||
Keyboard="Text">
|
||||
<Entry.Style>
|
||||
<OnPlatform
|
||||
x:TypeArguments="Style"
|
||||
iOS="{StaticResource SettingsEntryStyle}"
|
||||
Android="{StaticResource SettingsEntryStyle}"
|
||||
WinPhone="{StaticResource SettingsUwpEntryStyle}"/>
|
||||
<OnPlatform x:TypeArguments="Style">
|
||||
<On Platform="iOS, Android" Value="{StaticResource SettingsEntryStyle}" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="{StaticResource SettingsUwpEntryStyle}" />
|
||||
</OnPlatform>
|
||||
</Entry.Style>
|
||||
</Entry>
|
||||
<Button
|
||||
@ -289,16 +289,18 @@
|
||||
Style="{StaticResource SettingsToggleButtonStyle}"
|
||||
IsVisible="{Binding UseFakeLocation, Converter={StaticResource InverseBoolConverter}}">
|
||||
<controls:ToggleButton.CheckedImage>
|
||||
<OnPlatform x:TypeArguments="ImageSource"
|
||||
Android="switch_on.png"
|
||||
iOS="switchOn.png"
|
||||
WinPhone="Assets/switchOn.png"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS" Value="switchOn.png" />
|
||||
<On Platform="Android" Value="switch_on.png" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOn.png" />
|
||||
</OnPlatform>
|
||||
</controls:ToggleButton.CheckedImage>
|
||||
<controls:ToggleButton.UnCheckedImage>
|
||||
<OnPlatform x:TypeArguments="ImageSource"
|
||||
Android="switch_off.png"
|
||||
iOS="switchOff.png"
|
||||
WinPhone="Assets/switchOff.png"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS" Value="switchOff.png" />
|
||||
<On Platform="Android" Value="switch_off.png" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/switchOff.png" />
|
||||
</OnPlatform>
|
||||
</controls:ToggleButton.UnCheckedImage>
|
||||
</controls:ToggleButton>
|
||||
</Grid>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ContentView
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
@ -71,11 +71,10 @@
|
||||
<Grid
|
||||
BackgroundColor="{StaticResource BackgroundColor}">
|
||||
<Grid.Padding>
|
||||
<OnPlatform
|
||||
x:TypeArguments="Thickness"
|
||||
Android="0"
|
||||
iOS="0"
|
||||
WinPhone="12, 0"/>
|
||||
<OnPlatform x:TypeArguments="Thickness">
|
||||
<On Platform="iOS, Android" Value="0" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="12, 0" />
|
||||
</OnPlatform>
|
||||
</Grid.Padding>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ContentView
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
@ -59,18 +59,16 @@
|
||||
Source="{Binding PictureUri}"
|
||||
Aspect="AspectFill">
|
||||
<ffimageloading:CachedImage.LoadingPlaceholder>
|
||||
<OnPlatform
|
||||
x:TypeArguments="ImageSource"
|
||||
iOS="default_campaign"
|
||||
Android="default_campaign"
|
||||
WinPhone="Assets/default_campaign.png"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS, Android" Value="default_campaign" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/default_campaign.png" />
|
||||
</OnPlatform>
|
||||
</ffimageloading:CachedImage.LoadingPlaceholder>
|
||||
<ffimageloading:CachedImage.ErrorPlaceholder>
|
||||
<OnPlatform
|
||||
x:TypeArguments="ImageSource"
|
||||
iOS="noimage"
|
||||
Android="noimage"
|
||||
WinPhone="Assets/noimage.png"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS, Android" Value="noimage" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/noimage.png" />
|
||||
</OnPlatform>
|
||||
</ffimageloading:CachedImage.ErrorPlaceholder>
|
||||
</ffimageloading:CachedImage>
|
||||
<!-- NAME -->
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ContentView
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
@ -76,18 +76,16 @@
|
||||
Source="{Binding PictureUri}"
|
||||
Aspect="AspectFill">
|
||||
<ffimageloading:CachedImage.LoadingPlaceholder>
|
||||
<OnPlatform
|
||||
x:TypeArguments="ImageSource"
|
||||
iOS="default_product"
|
||||
Android="default_product"
|
||||
WinPhone="Assets/default_product.png"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS, Android" Value="default_product" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/default_product.png" />
|
||||
</OnPlatform>
|
||||
</ffimageloading:CachedImage.LoadingPlaceholder>
|
||||
<ffimageloading:CachedImage.ErrorPlaceholder>
|
||||
<OnPlatform
|
||||
x:TypeArguments="ImageSource"
|
||||
iOS="noimage"
|
||||
Android="noimage"
|
||||
WinPhone="Assets/noimage.png"/>
|
||||
<OnPlatform x:TypeArguments="ImageSource">
|
||||
<On Platform="iOS, Android" Value="noimage" />
|
||||
<On Platform="UWP, WinRT, WinPhone" Value="Assets/noimage.png" />
|
||||
</OnPlatform>
|
||||
</ffimageloading:CachedImage.ErrorPlaceholder>
|
||||
</ffimageloading:CachedImage>
|
||||
<Grid
|
||||
|
@ -257,6 +257,8 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
|
||||
|
||||
private void RegisterEventBus(IServiceCollection services)
|
||||
{
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
|
||||
if (Configuration.GetValue<bool>("AzureServiceBusEnabled"))
|
||||
{
|
||||
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
|
||||
@ -264,8 +266,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
|
||||
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
|
||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
|
||||
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
|
||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
|
||||
|
||||
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
|
||||
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope);
|
||||
@ -286,7 +287,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
|
||||
retryCount = int.Parse(Configuration["EventBusRetryCount"]);
|
||||
}
|
||||
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount);
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
|
||||
catalogItem = productToUpdate;
|
||||
_catalogContext.CatalogItems.Update(catalogItem);
|
||||
|
||||
if (raiseProductPriceChangedEvent) // Save and publish integration event if price has changed
|
||||
if (raiseProductPriceChangedEvent) // Save product's data and publish integration event through the Event Bus if price has changed
|
||||
{
|
||||
//Create Integration Event to be published through the Event Bus
|
||||
var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice);
|
||||
@ -191,7 +191,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
|
||||
// Publish through the Event Bus and mark the saved event as published
|
||||
await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent);
|
||||
}
|
||||
else // Save updated product
|
||||
else // Just save the updated product because the Product's Price hasn't changed.
|
||||
{
|
||||
await _catalogContext.SaveChangesAsync();
|
||||
}
|
||||
|
@ -224,6 +224,8 @@
|
||||
|
||||
private void RegisterEventBus(IServiceCollection services)
|
||||
{
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
|
||||
if (Configuration.GetValue<bool>("AzureServiceBusEnabled"))
|
||||
{
|
||||
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
|
||||
@ -232,7 +234,6 @@
|
||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
|
||||
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
|
||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
|
||||
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
|
||||
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope);
|
||||
@ -254,7 +255,7 @@
|
||||
retryCount = int.Parse(Configuration["EventBusRetryCount"]);
|
||||
}
|
||||
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount);
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Default": "Trace",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Default": "Trace",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
|
@ -227,6 +227,8 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API
|
||||
|
||||
private void RegisterEventBus(IServiceCollection services)
|
||||
{
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
|
||||
if (Configuration.GetValue<bool>("AzureServiceBusEnabled"))
|
||||
{
|
||||
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
|
||||
@ -235,7 +237,6 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API
|
||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
|
||||
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
|
||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
|
||||
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
|
||||
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope);
|
||||
@ -256,7 +257,7 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API
|
||||
retryCount = int.Parse(Configuration["EventBusRetryCount"]);
|
||||
}
|
||||
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount);
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Default": "Trace",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
|
@ -246,8 +246,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
private void RegisterEventBus(IServiceCollection services)
|
||||
private void RegisterEventBus(IServiceCollection services)
|
||||
{
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
|
||||
if (Configuration.GetValue<bool>("AzureServiceBusEnabled"))
|
||||
{
|
||||
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
|
||||
@ -255,8 +257,7 @@
|
||||
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
|
||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
|
||||
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
|
||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
|
||||
|
||||
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
|
||||
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope);
|
||||
@ -277,7 +278,7 @@
|
||||
retryCount = int.Parse(Configuration["EventBusRetryCount"]);
|
||||
}
|
||||
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount);
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
"Default": "Trace"
|
||||
}
|
||||
},
|
||||
"ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word",
|
||||
|
@ -9,18 +9,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.Application.Commands
|
||||
{
|
||||
public class CancelOrderCommandIdentifiedHandler : IdentifierCommandHandler<CancelOrderCommand, bool>
|
||||
{
|
||||
public CancelOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool CreateResultForDuplicateRequest()
|
||||
{
|
||||
return true; // Ignore duplicate requests for processing order.
|
||||
}
|
||||
}
|
||||
|
||||
// Regular CommandHandler
|
||||
public class CancelOrderCommandHandler : IAsyncRequestHandler<CancelOrderCommand, bool>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
@ -43,4 +32,18 @@ namespace Ordering.API.Application.Commands
|
||||
return await _orderRepository.UnitOfWork.SaveEntitiesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Use for Idempotency in Command process
|
||||
public class CancelOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CancelOrderCommand, bool>
|
||||
{
|
||||
public CancelOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool CreateResultForDuplicateRequest()
|
||||
{
|
||||
return true; // Ignore duplicate requests for processing order.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,19 +7,7 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
public class CreateOrderCommandIdentifiedHandler : IdentifierCommandHandler<CreateOrderCommand, bool>
|
||||
{
|
||||
public CreateOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool CreateResultForDuplicateRequest()
|
||||
{
|
||||
return true; // Ignore duplicate requests for creating order.
|
||||
}
|
||||
}
|
||||
|
||||
// Regular CommandHandler
|
||||
public class CreateOrderCommandHandler
|
||||
: IAsyncRequestHandler<CreateOrderCommand, bool>
|
||||
{
|
||||
@ -55,4 +43,18 @@
|
||||
.SaveEntitiesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Use for Idempotency in Command process
|
||||
public class CreateOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CreateOrderCommand, bool>
|
||||
{
|
||||
public CreateOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool CreateResultForDuplicateRequest()
|
||||
{
|
||||
return true; // Ignore duplicate requests for creating order.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,13 +10,13 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the command handler that performs the operation if request is not duplicated</typeparam>
|
||||
/// <typeparam name="R">Return value of the inner command handler</typeparam>
|
||||
public class IdentifierCommandHandler<T, R> : IAsyncRequestHandler<IdentifiedCommand<T, R>, R>
|
||||
public class IdentifiedCommandHandler<T, R> : IAsyncRequestHandler<IdentifiedCommand<T, R>, R>
|
||||
where T : IRequest<R>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
private readonly IRequestManager _requestManager;
|
||||
|
||||
public IdentifierCommandHandler(IMediator mediator, IRequestManager requestManager)
|
||||
public IdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager)
|
||||
{
|
||||
_mediator = mediator;
|
||||
_requestManager = requestManager;
|
||||
@ -48,6 +48,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
||||
{
|
||||
await _requestManager.CreateRequestForCommandAsync<T>(message.Id);
|
||||
|
||||
// Send the embeded business command to mediator so it runs its related CommandHandler
|
||||
var result = await _mediator.Send(message.Command);
|
||||
|
||||
return result;
|
@ -6,18 +6,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Ordering.API.Application.Commands
|
||||
{
|
||||
public class ShipOrderCommandIdentifiedHandler : IdentifierCommandHandler<ShipOrderCommand, bool>
|
||||
{
|
||||
public ShipOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool CreateResultForDuplicateRequest()
|
||||
{
|
||||
return true; // Ignore duplicate requests for processing order.
|
||||
}
|
||||
}
|
||||
|
||||
// Regular CommandHandler
|
||||
public class ShipOrderCommandHandler : IAsyncRequestHandler<ShipOrderCommand, bool>
|
||||
{
|
||||
private readonly IOrderRepository _orderRepository;
|
||||
@ -40,4 +29,18 @@ namespace Ordering.API.Application.Commands
|
||||
return await _orderRepository.UnitOfWork.SaveEntitiesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Use for Idempotency in Command process
|
||||
public class ShipOrderIdentifiedCommandHandler : IdentifiedCommandHandler<ShipOrderCommand, bool>
|
||||
{
|
||||
public ShipOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool CreateResultForDuplicateRequest()
|
||||
{
|
||||
return true; // Ignore duplicate requests for processing order.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ namespace Ordering.API.Application.DomainEventHandlers.OrderStartedEvent
|
||||
public async Task Handle(OrderStartedDomainEvent orderStartedEvent)
|
||||
{
|
||||
var cardTypeId = (orderStartedEvent.CardTypeId != 0) ? orderStartedEvent.CardTypeId : 1;
|
||||
|
||||
var buyer = await _buyerRepository.FindAsync(orderStartedEvent.UserId);
|
||||
bool buyerOriginallyExisted = (buyer == null) ? false : true;
|
||||
|
||||
|
@ -5,10 +5,10 @@
|
||||
|
||||
public interface IOrderQueries
|
||||
{
|
||||
Task<dynamic> GetOrderAsync(int id);
|
||||
Task<Order> GetOrderAsync(int id);
|
||||
|
||||
Task<IEnumerable<dynamic>> GetOrdersAsync();
|
||||
Task<IEnumerable<OrderSummary>> GetOrdersAsync();
|
||||
|
||||
Task<IEnumerable<dynamic>> GetCardTypesAsync();
|
||||
Task<IEnumerable<CardType>> GetCardTypesAsync();
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries
|
||||
{
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Data.SqlClient;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Dynamic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class OrderQueries
|
||||
@ -19,7 +17,7 @@
|
||||
}
|
||||
|
||||
|
||||
public async Task<dynamic> GetOrderAsync(int id)
|
||||
public async Task<Order> GetOrderAsync(int id)
|
||||
{
|
||||
using (var connection = new SqlConnection(_connectionString))
|
||||
{
|
||||
@ -44,13 +42,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<dynamic>> GetOrdersAsync()
|
||||
public async Task<IEnumerable<OrderSummary>> GetOrdersAsync()
|
||||
{
|
||||
using (var connection = new SqlConnection(_connectionString))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
return await connection.QueryAsync<dynamic>(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status],SUM(oi.units*oi.unitprice) as total
|
||||
return await connection.QueryAsync<OrderSummary>(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status],SUM(oi.units*oi.unitprice) as total
|
||||
FROM [ordering].[Orders] o
|
||||
LEFT JOIN[ordering].[orderitems] oi ON o.Id = oi.orderid
|
||||
LEFT JOIN[ordering].[orderstatus] os on o.OrderStatusId = os.Id
|
||||
@ -59,39 +57,41 @@
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<dynamic>> GetCardTypesAsync()
|
||||
public async Task<IEnumerable<CardType>> GetCardTypesAsync()
|
||||
{
|
||||
using (var connection = new SqlConnection(_connectionString))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
return await connection.QueryAsync<dynamic>("SELECT * FROM ordering.cardtypes");
|
||||
return await connection.QueryAsync<CardType>("SELECT * FROM ordering.cardtypes");
|
||||
}
|
||||
}
|
||||
|
||||
private dynamic MapOrderItems(dynamic result)
|
||||
private Order MapOrderItems(dynamic result)
|
||||
{
|
||||
dynamic order = new ExpandoObject();
|
||||
|
||||
order.ordernumber = result[0].ordernumber;
|
||||
order.date = result[0].date;
|
||||
order.status = result[0].status;
|
||||
order.description = result[0].description;
|
||||
order.street = result[0].street;
|
||||
order.city = result[0].city;
|
||||
order.zipcode = result[0].zipcode;
|
||||
order.country = result[0].country;
|
||||
|
||||
order.orderitems = new List<dynamic>();
|
||||
order.total = 0;
|
||||
var order = new Order
|
||||
{
|
||||
ordernumber = result[0].ordernumber,
|
||||
date = result[0].date,
|
||||
status = result[0].status,
|
||||
description = result[0].description,
|
||||
street = result[0].street,
|
||||
city = result[0].city,
|
||||
zipcode = result[0].zipcode,
|
||||
country = result[0].country,
|
||||
orderitems = new List<Orderitem>(),
|
||||
total = 0
|
||||
};
|
||||
|
||||
foreach (dynamic item in result)
|
||||
{
|
||||
dynamic orderitem = new ExpandoObject();
|
||||
orderitem.productname = item.productname;
|
||||
orderitem.units = item.units;
|
||||
orderitem.unitprice = item.unitprice;
|
||||
orderitem.pictureurl = item.pictureurl;
|
||||
var orderitem = new Orderitem
|
||||
{
|
||||
productname = item.productname,
|
||||
units = item.units,
|
||||
unitprice = (double)item.unitprice,
|
||||
pictureurl = item.pictureurl
|
||||
};
|
||||
|
||||
order.total += item.units * item.unitprice;
|
||||
order.orderitems.Add(orderitem);
|
||||
|
@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries
|
||||
{
|
||||
public class Orderitem
|
||||
{
|
||||
public string productname { get; set; }
|
||||
public int units { get; set; }
|
||||
public double unitprice { get; set; }
|
||||
public string pictureurl { get; set; }
|
||||
}
|
||||
|
||||
public class Order
|
||||
{
|
||||
public int ordernumber { get; set; }
|
||||
public DateTime date { get; set; }
|
||||
public string status { get; set; }
|
||||
public string description { get; set; }
|
||||
public string street { get; set; }
|
||||
public string city { get; set; }
|
||||
public string zipcode { get; set; }
|
||||
public string country { get; set; }
|
||||
public List<Orderitem> orderitems { get; set; }
|
||||
public decimal total { get; set; }
|
||||
}
|
||||
|
||||
public class OrderSummary
|
||||
{
|
||||
public int ordernumber { get; set; }
|
||||
public DateTime date { get; set; }
|
||||
public string status { get; set; }
|
||||
public double total { get; set; }
|
||||
}
|
||||
|
||||
public class CardType
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||
|
||||
namespace Ordering.API.Application.Validations
|
||||
{
|
||||
public class IdentifierCommandValidator : AbstractValidator<IdentifiedCommand<CreateOrderCommand,bool>>
|
||||
public class IdentifiedCommandValidator : AbstractValidator<IdentifiedCommand<CreateOrderCommand,bool>>
|
||||
{
|
||||
public IdentifierCommandValidator()
|
||||
public IdentifiedCommandValidator()
|
||||
{
|
||||
RuleFor(command => command.Id).NotEmpty();
|
||||
}
|
@ -64,7 +64,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
|
||||
|
||||
[Route("{orderId:int}")]
|
||||
[HttpGet]
|
||||
[ProducesResponseType((int)HttpStatusCode.OK)]
|
||||
[ProducesResponseType(typeof(Order),(int)HttpStatusCode.OK)]
|
||||
[ProducesResponseType((int)HttpStatusCode.NotFound)]
|
||||
public async Task<IActionResult> GetOrder(int orderId)
|
||||
{
|
||||
@ -83,6 +83,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
|
||||
|
||||
[Route("")]
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(IEnumerable<OrderSummary>), (int)HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> GetOrders()
|
||||
{
|
||||
var orderTask = _orderQueries.GetOrdersAsync();
|
||||
@ -94,6 +95,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
|
||||
|
||||
[Route("cardtypes")]
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(IEnumerable<CardType>), (int)HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> GetCardTypes()
|
||||
{
|
||||
var cardTypes = await _orderQueries
|
||||
|
@ -23,11 +23,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof
|
||||
builder.RegisterAssemblyTypes(typeof(CreateOrderCommand).GetTypeInfo().Assembly)
|
||||
.AsClosedTypesOf(typeof(IAsyncRequestHandler<,>));
|
||||
|
||||
// Register all the event classes (they implement IAsyncNotificationHandler) in assembly holding the Commands
|
||||
// Register the DomainEventHandler classes (they implement IAsyncNotificationHandler<>) in assembly holding the Domain Events
|
||||
builder.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly)
|
||||
.AsClosedTypesOf(typeof(IAsyncNotificationHandler<>));
|
||||
|
||||
|
||||
// Register the Command's Validators (Validators based on FluentValidation library)
|
||||
builder
|
||||
.RegisterAssemblyTypes(typeof(CreateOrderCommandValidator).GetTypeInfo().Assembly)
|
||||
.Where(t => t.IsClosedTypeOf(typeof(IValidator<>)))
|
||||
|
@ -294,6 +294,8 @@
|
||||
|
||||
private void RegisterEventBus(IServiceCollection services)
|
||||
{
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
|
||||
if (Configuration.GetValue<bool>("AzureServiceBusEnabled"))
|
||||
{
|
||||
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
|
||||
@ -301,8 +303,7 @@
|
||||
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
|
||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
|
||||
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
|
||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
|
||||
|
||||
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
|
||||
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope);
|
||||
@ -323,7 +324,7 @@
|
||||
retryCount = int.Parse(Configuration["EventBusRetryCount"]);
|
||||
}
|
||||
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount);
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Default": "Trace",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
|
@ -4,17 +4,12 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate
|
||||
{
|
||||
public class Address
|
||||
:ValueObject
|
||||
public class Address : ValueObject
|
||||
{
|
||||
public String Street { get; private set; }
|
||||
|
||||
public String City { get; private set; }
|
||||
|
||||
public String State { get; private set; }
|
||||
|
||||
public String Country { get; private set; }
|
||||
|
||||
public String ZipCode { get; private set; }
|
||||
|
||||
private Address() { }
|
||||
@ -30,6 +25,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
|
||||
|
||||
protected override IEnumerable<object> GetAtomicValues()
|
||||
{
|
||||
// Using a yield return statement to return each element one at a time
|
||||
yield return Street;
|
||||
yield return City;
|
||||
yield return State;
|
||||
|
@ -15,6 +15,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
|
||||
// aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections)
|
||||
private DateTime _orderDate;
|
||||
|
||||
// Address is a Value Object pattern example persisted as EF Core 2.0 owned entity
|
||||
public Address Address { get; private set; }
|
||||
|
||||
private int? _buyerId;
|
||||
@ -29,12 +30,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
|
||||
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
|
||||
// but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour.
|
||||
private readonly List<OrderItem> _orderItems;
|
||||
|
||||
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
|
||||
// Using List<>.AsReadOnly()
|
||||
// This will create a read only wrapper around the private list so is protected against "external updates".
|
||||
// It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance)
|
||||
//https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx
|
||||
|
||||
private int? _paymentMethodId;
|
||||
|
||||
@ -177,16 +173,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
|
||||
private void AddOrderStartedDomainEvent(string userId, int cardTypeId, string cardNumber,
|
||||
string cardSecurityNumber, string cardHolderName, DateTime cardExpiration)
|
||||
{
|
||||
var orderStartedDomainEvent = new OrderStartedDomainEvent(
|
||||
this, userId, cardTypeId, cardNumber, cardSecurityNumber,
|
||||
cardHolderName, cardExpiration);
|
||||
var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, cardTypeId,
|
||||
cardNumber, cardSecurityNumber,
|
||||
cardHolderName, cardExpiration);
|
||||
|
||||
this.AddDomainEvent(orderStartedDomainEvent);
|
||||
}
|
||||
|
||||
private void StatusChangeException(OrderStatus orderStatusToChange)
|
||||
{
|
||||
throw new OrderingDomainException($"Not possible to change order status from {OrderStatus.Name} to {orderStatusToChange.Name}.");
|
||||
throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}.");
|
||||
}
|
||||
|
||||
public decimal GetTotal()
|
||||
|
@ -9,8 +9,7 @@ namespace Ordering.Domain.Events
|
||||
/// <summary>
|
||||
/// Event used when an order is created
|
||||
/// </summary>
|
||||
public class OrderStartedDomainEvent
|
||||
: INotification
|
||||
public class OrderStartedDomainEvent : INotification
|
||||
{
|
||||
public string UserId { get; private set; }
|
||||
public int CardTypeId { get; private set; }
|
||||
@ -21,9 +20,9 @@ namespace Ordering.Domain.Events
|
||||
public Order Order { get; private set; }
|
||||
|
||||
public OrderStartedDomainEvent(Order order, string userId,
|
||||
int cardTypeId, string cardNumber,
|
||||
string cardSecurityNumber, string cardHolderName,
|
||||
DateTime cardExpiration)
|
||||
int cardTypeId, string cardNumber,
|
||||
string cardSecurityNumber, string cardHolderName,
|
||||
DateTime cardExpiration)
|
||||
{
|
||||
Order = order;
|
||||
UserId = userId;
|
||||
|
@ -6,12 +6,8 @@
|
||||
|
||||
public abstract class Entity
|
||||
{
|
||||
|
||||
int? _requestedHashCode;
|
||||
int _Id;
|
||||
|
||||
private List<INotification> _domainEvents;
|
||||
|
||||
int _Id;
|
||||
public virtual int Id
|
||||
{
|
||||
get
|
||||
@ -24,13 +20,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
public List<INotification> DomainEvents => _domainEvents;
|
||||
private List<INotification> _domainEvents;
|
||||
public List<INotification> DomainEvents => _domainEvents;
|
||||
|
||||
public void AddDomainEvent(INotification eventItem)
|
||||
{
|
||||
_domainEvents = _domainEvents ?? new List<INotification>();
|
||||
_domainEvents.Add(eventItem);
|
||||
}
|
||||
|
||||
public void RemoveDomainEvent(INotification eventItem)
|
||||
{
|
||||
if (_domainEvents is null) return;
|
||||
@ -74,7 +71,6 @@
|
||||
return base.GetHashCode();
|
||||
|
||||
}
|
||||
|
||||
public static bool operator ==(Entity left, Entity right)
|
||||
{
|
||||
if (Object.Equals(left, null))
|
||||
|
@ -14,16 +14,13 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
return ReferenceEquals(left, null) || left.Equals(right);
|
||||
}
|
||||
|
||||
|
||||
protected static bool NotEqualOperator(ValueObject left, ValueObject right)
|
||||
{
|
||||
return !(EqualOperator(left, right));
|
||||
}
|
||||
|
||||
|
||||
protected abstract IEnumerable<object> GetAtomicValues();
|
||||
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || obj.GetType() != GetType())
|
||||
@ -47,7 +44,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
return !thisValues.MoveNext() && !otherValues.MoveNext();
|
||||
}
|
||||
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return GetAtomicValues()
|
||||
|
@ -7,8 +7,7 @@ using System;
|
||||
|
||||
namespace Ordering.Infrastructure.EntityConfigurations
|
||||
{
|
||||
class OrderEntityTypeConfiguration
|
||||
: IEntityTypeConfiguration<Order>
|
||||
class OrderEntityTypeConfiguration : IEntityTypeConfiguration<Order>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Order> orderConfiguration)
|
||||
{
|
||||
@ -21,6 +20,7 @@ namespace Ordering.Infrastructure.EntityConfigurations
|
||||
orderConfiguration.Property(o => o.Id)
|
||||
.ForSqlServerUseSequenceHiLo("orderseq", OrderingContext.DEFAULT_SCHEMA);
|
||||
|
||||
//Address value object persisted as owned entity type supported since EF Core 2.0
|
||||
orderConfiguration.OwnsOne(o => o.Address);
|
||||
|
||||
orderConfiguration.Property<DateTime>("OrderDate").IsRequired();
|
||||
@ -32,7 +32,7 @@ namespace Ordering.Infrastructure.EntityConfigurations
|
||||
var navigation = orderConfiguration.Metadata.FindNavigation(nameof(Order.OrderItems));
|
||||
|
||||
// DDD Patterns comment:
|
||||
//Set as Field (New since EF 1.1) to access the OrderItem collection property through its field
|
||||
//Set as field (New since EF 1.1) to access the OrderItem collection property through its field
|
||||
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
|
||||
orderConfiguration.HasOne<PaymentMethod>()
|
||||
|
@ -12,22 +12,14 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure
|
||||
{
|
||||
public class OrderingContext
|
||||
: DbContext,IUnitOfWork
|
||||
|
||||
public class OrderingContext : DbContext, IUnitOfWork
|
||||
{
|
||||
public const string DEFAULT_SCHEMA = "ordering";
|
||||
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
|
||||
public DbSet<OrderItem> OrderItems { get; set; }
|
||||
|
||||
public DbSet<PaymentMethod> Payments { get; set; }
|
||||
|
||||
public DbSet<Buyer> Buyers { get; set; }
|
||||
|
||||
public DbSet<CardType> CardTypes { get; set; }
|
||||
|
||||
public DbSet<OrderStatus> OrderStatus { get; set; }
|
||||
|
||||
private readonly IMediator _mediator;
|
||||
@ -63,7 +55,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure
|
||||
// You will need to handle eventual consistency and compensatory actions in case of failures in any of the Handlers.
|
||||
await _mediator.DispatchDomainEventsAsync(this);
|
||||
|
||||
|
||||
// After executing this line all the changes (from the Command Handler and Domain Event Handlers)
|
||||
// performed throught the DbContext will be commited
|
||||
var result = await base.SaveChangesAsync();
|
||||
|
@ -11,7 +11,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositor
|
||||
: IBuyerRepository
|
||||
{
|
||||
private readonly OrderingContext _context;
|
||||
|
||||
public IUnitOfWork UnitOfWork
|
||||
{
|
||||
get
|
||||
@ -29,7 +28,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositor
|
||||
{
|
||||
if (buyer.IsTransient())
|
||||
{
|
||||
//TODO: when migrating to ef core 1.1.1 change Add by AddAsync-. A bug in ef core 1.1.0 does not allow to do it https://github.com/aspnet/EntityFramework/issues/7298
|
||||
return _context.Buyers
|
||||
.Add(buyer)
|
||||
.Entity;
|
||||
@ -37,8 +35,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositor
|
||||
else
|
||||
{
|
||||
return buyer;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public Buyer Update(Buyer buyer)
|
||||
|
@ -127,6 +127,8 @@ namespace Payment.API
|
||||
|
||||
private void RegisterEventBus(IServiceCollection services)
|
||||
{
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
|
||||
if (Configuration.GetValue<bool>("AzureServiceBusEnabled"))
|
||||
{
|
||||
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
|
||||
@ -134,8 +136,7 @@ namespace Payment.API
|
||||
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
|
||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
|
||||
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
|
||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
|
||||
var subscriptionClientName = Configuration["SubscriptionClientName"];
|
||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
|
||||
|
||||
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
|
||||
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope);
|
||||
@ -156,7 +157,7 @@ namespace Payment.API
|
||||
retryCount = int.Parse(Configuration["EventBusRetryCount"]);
|
||||
}
|
||||
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, retryCount);
|
||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
</article>
|
||||
@if (Model != null && Model.Any())
|
||||
{
|
||||
@foreach (var item in Model)
|
||||
foreach (var item in Model)
|
||||
{
|
||||
<article class="esh-orders-items row">
|
||||
<section class="esh-orders-item col-xs-2">@Html.DisplayFor(modelItem => item.OrderNumber)</section>
|
||||
|
@ -39,6 +39,7 @@
|
||||
<a asp-area="" asp-controller="Catalog" asp-action="Index">
|
||||
<img src="~/images/brand.png" />
|
||||
</a>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
@await Html.PartialAsync("_LoginPartial")
|
||||
|
@ -31,7 +31,7 @@
|
||||
});
|
||||
|
||||
Assert.NotNull(basket);
|
||||
Assert.Equal(1, basket.Items.Count);
|
||||
Assert.Single(basket.Items);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -41,7 +41,7 @@ namespace IntegrationTests.Services.Catalog
|
||||
var response = await server.CreateClient()
|
||||
.GetAsync(Get.ItemById(int.MinValue));
|
||||
|
||||
Assert.Equal(response.StatusCode, HttpStatusCode.BadRequest);
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ namespace IntegrationTests.Services.Catalog
|
||||
var response = await server.CreateClient()
|
||||
.GetAsync(Get.ItemById(int.MaxValue));
|
||||
|
||||
Assert.Equal(response.StatusCode, HttpStatusCode.NotFound);
|
||||
Assert.Equal( HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,15 +147,15 @@
|
||||
<None Include="WebMVC\AddProducts.webtest">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="WebMVC\CatalogFilter.webtest">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Identity.API\Logout.webtest">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="WebMVC\CreateNewOrder.webtest">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="WebMVC\CatalogFilter.webtest">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Choose>
|
||||
|
@ -3,7 +3,10 @@
|
||||
<Scenarios>
|
||||
<Scenario Name="OrderProductsLoadTest" DelayBetweenIterations="0" PercentNewUsers="0" IPSwitching="false" TestMixType="PercentageOfTestsStarted" ApplyDistributionToPacingDelay="true" MaxTestIterations="0" DisableDuringWarmup="false" DelayStartTime="0" AllowedAgents="">
|
||||
<ThinkProfile Value="0.2" Pattern="NormalDistribution" />
|
||||
<LoadProfile Pattern="Step" InitialUsers="1" MaxUsers="50" StepUsers="2" StepDuration="10" StepRampTime="10" />
|
||||
<LoadProfile Pattern="Step" InitialUsers="1" MaxUsers="1000" StepUsers="50" StepDuration="10" StepRampTime="10" />
|
||||
<InitializeTest>
|
||||
<TestProfile Name="AddProducts" Path="webmvc\addproducts.webtest" Id="2c9d53ae-0237-47bd-a5d2-6500ef5d8fcb" Percentage="0.0" Type="Microsoft.VisualStudio.TestTools.WebStress.DeclarativeWebTestElement, Microsoft.VisualStudio.QualityTools.LoadTest, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
</InitializeTest>
|
||||
<TestMix>
|
||||
<TestProfile Name="GetItems" Path="catalog.api\getitems.webtest" Id="e527de7e-beff-4824-af52-dda763fd5e6c" Percentage="19" Type="Microsoft.VisualStudio.TestTools.WebStress.DeclarativeWebTestElement, Microsoft.VisualStudio.QualityTools.LoadTest, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<TestProfile Name="GetCatalogTypes" Path="catalog.api\getcatalogtypes.webtest" Id="7df20b29-d5c3-447b-b73d-95c63e9c4061" Percentage="15" Type="Microsoft.VisualStudio.TestTools.WebStress.DeclarativeWebTestElement, Microsoft.VisualStudio.QualityTools.LoadTest, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
|
@ -164,7 +164,7 @@
|
||||
<FormPostParameter Name="Total" Value="8.5" RecordedValue="8.5" CorrelationBinding="" UrlEncode="True" />
|
||||
<FormPostParameter Name="action" Value="[ Place Order ]" RecordedValue="[ Place Order ]" CorrelationBinding="{{FormPostParam9.action}}" UrlEncode="True" />
|
||||
<FormPostParameter Name="ZipCode" Value="98052" RecordedValue="98052" CorrelationBinding="" UrlEncode="True" />
|
||||
<FormPostParameter Name="RequestId" Value="{{GenGuid}} " RecordedValue="f58b9345-ea25-4125-a8bf-b0992233af6c" CorrelationBinding="" UrlEncode="True" />
|
||||
<FormPostParameter Name="RequestId" Value="{{GenGuid}}" RecordedValue="{{GenGuid}}" CorrelationBinding="" UrlEncode="True" />
|
||||
<FormPostParameter Name="CardTypeId" Value="1" RecordedValue="" CorrelationBinding="" UrlEncode="True" />
|
||||
</FormPostHttpBody>
|
||||
</Request>
|
||||
|
84
test/Services/LoadTest/readme.md
Normal file
84
test/Services/LoadTest/readme.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Load Testing settings
|
||||
|
||||
This folder contains files needed to run load tests locally or on a Kubernetes / Service Fabric cluster.
|
||||
|
||||
<p>
|
||||
<img src="../../../img/loadtests/loadtestproj_dir.png">
|
||||
<p>
|
||||
|
||||
## Set a local environment
|
||||
|
||||
Modify the **app.config** file in the LoadTest project directory and set the following service urls.
|
||||
|
||||
```
|
||||
<Servers>
|
||||
<MvcWebServer url="http://localhost:5100" />
|
||||
<CatalogApiServer url="http://localhost:5101" />
|
||||
<OrderingApiServer url="http://localhost:5102" />
|
||||
<BasketApiServer url="http://localhost:5103" />
|
||||
<IdentityApiServer url="http://localhost:5105" />
|
||||
<LocationsApiServer url="http://localhost:5109" />
|
||||
<MarketingApiServer url="http://localhost:5110" />
|
||||
</Servers>
|
||||
```
|
||||
|
||||
Modify the **.env** file and set the following config property as shown bellow.
|
||||
|
||||
```
|
||||
USE_LOADTEST=True
|
||||
```
|
||||
## Set a Service Fabric environment
|
||||
|
||||
Modify the **app.config** file in the LoadTest project directory and set the following service urls.
|
||||
|
||||
```
|
||||
<Servers>
|
||||
<MvcWebServer url="http://<target_sf_dns>:5100" />
|
||||
<CatalogApiServer url="http://<target_sf_dns>:5101" />
|
||||
<OrderingApiServer url="http://<target_sf_dns>:5102" />
|
||||
<BasketApiServer url="http://<target_sf_dns>:5103" />
|
||||
<IdentityApiServer url="http://<target_sf_dns>:5105" />
|
||||
<LocationsApiServer url="http://<target_sf_dns>:5109" />
|
||||
<MarketingApiServer url="http://<target_sf_dns>:5110" />
|
||||
</Servers>
|
||||
```
|
||||
|
||||
Modify the **ServiceManifest.xml** files of the eShop SF Services and set the **UseLoadTest** environment variable to True. This setting enables the load tests to bypass authorization in api services.
|
||||
|
||||
<p>
|
||||
<img src="../../../img/loadtests/sfmanifestsettings.png">
|
||||
<p>
|
||||
|
||||
Deploy the SF services. **PLEASE** Read our [SF deployment guide for Linux](./../../../deploy/az/servicefabric/LinuxContainers/readme.md) And [SF deployment guide for Windows](./../../../deploy/az/servicefabric/WindowsContainers/readme.md) to know about how to deploy eshop on SF.
|
||||
|
||||
## Set a Kubernetes environment
|
||||
|
||||
Modify the **app.config** file in the LoadTest project directory and set the following service urls.
|
||||
|
||||
```
|
||||
<Servers>
|
||||
<MvcWebServer url="http://<public_ip_k8s>/webmvc" />
|
||||
<CatalogApiServer url="http://<public_ip_k8s>/catalog-api" />
|
||||
<OrderingApiServer url="http://<public_ip_k8s>/ordering-api" />
|
||||
<BasketApiServer url="http://<public_ip_k8s>/basket-api" />
|
||||
<IdentityApiServer url="http://<public_ip_k8s>/identity" />
|
||||
<LocationsApiServer url="http://<public_ip_k8s>/locations-api" />
|
||||
<MarketingApiServer url="http://<public_ip_k8s>/marketing-api" />
|
||||
</Servers>
|
||||
```
|
||||
|
||||
Modify the **conf_local.yml** file in the K8s directory and set the **EnableLoadTest** environment variable to True. This setting enables the load tests to bypass authorization in api services.
|
||||
|
||||
<p>
|
||||
<img src="../../../img/loadtests/k8ssettings.png">
|
||||
<p>
|
||||
|
||||
Deploy the kubernetes services. **PLEASE** Read our [k8s deployment guide](./../../../k8s/README.k8s.md) to know about how to deploy eshop on Kubernetes.
|
||||
|
||||
## Run Load Tests
|
||||
|
||||
Open the load test you want to perform ***.loadtest** files and click the Run Load test button.
|
||||
|
||||
<p>
|
||||
<img src="./../../../img/loadtests/runloadtest.png">
|
||||
<p>
|
@ -13,12 +13,12 @@ namespace UnitTest.Ordering.Application
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
public class IdentifierCommandHandlerTest
|
||||
public class IdentifiedCommandHandlerTest
|
||||
{
|
||||
private readonly Mock<IRequestManager> _requestManager;
|
||||
private readonly Mock<IMediator> _mediator;
|
||||
|
||||
public IdentifierCommandHandlerTest()
|
||||
public IdentifiedCommandHandlerTest()
|
||||
{
|
||||
_requestManager = new Mock<IRequestManager>();
|
||||
_mediator = new Mock<IMediator>();
|
||||
@ -38,7 +38,7 @@ namespace UnitTest.Ordering.Application
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
//Act
|
||||
var handler = new IdentifierCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object);
|
||||
var handler = new IdentifiedCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object);
|
||||
var result = await handler.Handle(fakeOrderCmd);
|
||||
|
||||
//Assert
|
||||
@ -60,7 +60,7 @@ namespace UnitTest.Ordering.Application
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
//Act
|
||||
var handler = new IdentifierCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object);
|
||||
var handler = new IdentifiedCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object);
|
||||
var result = await handler.Handle(fakeOrderCmd);
|
||||
|
||||
//Assert
|
@ -93,7 +93,7 @@ namespace UnitTest.Ordering.Application
|
||||
public async Task Get_orders_success()
|
||||
{
|
||||
//Arrange
|
||||
var fakeDynamicResult = Enumerable.Empty<object>();
|
||||
var fakeDynamicResult = Enumerable.Empty<OrderSummary>();
|
||||
_orderQueriesMock.Setup(x => x.GetOrdersAsync())
|
||||
.Returns(Task.FromResult(fakeDynamicResult));
|
||||
|
||||
@ -110,7 +110,7 @@ namespace UnitTest.Ordering.Application
|
||||
{
|
||||
//Arrange
|
||||
var fakeOrderId = 123;
|
||||
var fakeDynamicResult = new Object();
|
||||
var fakeDynamicResult = new Order();
|
||||
_orderQueriesMock.Setup(x => x.GetOrderAsync(It.IsAny<int>()))
|
||||
.Returns(Task.FromResult(fakeDynamicResult));
|
||||
|
||||
@ -126,7 +126,7 @@ namespace UnitTest.Ordering.Application
|
||||
public async Task Get_cardTypes_success()
|
||||
{
|
||||
//Arrange
|
||||
var fakeDynamicResult = Enumerable.Empty<object>();
|
||||
var fakeDynamicResult = Enumerable.Empty<CardType>();
|
||||
_orderQueriesMock.Setup(x => x.GetCardTypesAsync())
|
||||
.Returns(Task.FromResult(fakeDynamicResult));
|
||||
|
||||
|
@ -111,7 +111,6 @@ public class OrderAggregateTest
|
||||
public void Add_new_Order_raises_new_event()
|
||||
{
|
||||
//Arrange
|
||||
var userId = new Guid();
|
||||
var street = "fakeStreet";
|
||||
var city = "FakeCity";
|
||||
var state = "fakeState";
|
||||
@ -135,7 +134,6 @@ public class OrderAggregateTest
|
||||
public void Add_event_Order_explicitly_raises_new_event()
|
||||
{
|
||||
//Arrange
|
||||
var userId = new Guid();
|
||||
var street = "fakeStreet";
|
||||
var city = "FakeCity";
|
||||
var state = "fakeState";
|
||||
@ -159,7 +157,6 @@ public class OrderAggregateTest
|
||||
public void Remove_event_Order_explicitly()
|
||||
{
|
||||
//Arrange
|
||||
var userId = new Guid();
|
||||
var street = "fakeStreet";
|
||||
var city = "FakeCity";
|
||||
var state = "fakeState";
|
||||
|
Loading…
x
Reference in New Issue
Block a user