Hulkstance
Hulkstance
CC#
Created by Hulkstance on 7/24/2023 in #help
❔ Terraform Azure Modules Template
Hey guys, I'm going to deploy an ASP.NET Core Web API in Azure using Terraform and I wonder if there are any modules templates for the following services: - App Service (+ static public IP address, not sure if I need to setup virtual network for it yet) - Key Vault - App Configuration - Application Insights - Event Hub - Service Bus - SQL Database My K8s setup: - ACR - AKS - Application Gateway Is there a GitHub template which includes pre-defined modules for the listed services above? Best I could find was https://github.com/Azure/azure-data-labs-modules but it is missing App Configuration, App Service and a few more.
2 replies
CC#
Created by Hulkstance on 7/15/2023 in #help
❔ Key Vault + AppConfiguration usage
In my past I've been using it as following: - Secret configuration values, like passwords, connection strings, or API keys are added to Azure KeyVault. References to these values should be placed in the Azure AppConfiguration - Configuration values specific to a single service should be placed in the appsettings.json or appsettings.{ENVIRONMENT}.json file for that service - Shared configuration values among services should be added to Azure AppConfiguration So I wonder what's opinion on that. How do you use it? https://learn.microsoft.com/en-us/azure/azure-app-configuration/use-key-vault-references-dotnet-core?tabs=core6x
8 replies
CC#
Created by Hulkstance on 7/15/2023 in #help
❔ Application Insights Health Checks
I'm trying to find the docs for Application Insights Health Checks. https://learn.microsoft.com/en-us/azure/azure-monitor/app/asp-net-core?tabs=netcorenew%2Cnetcore6#use-applicationinsightsserviceoptions I already have it setup with User Secrets which I'm going to move to Key Vault in a bit. I also added <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" /> and builder.Services.AddApplicationInsightsTelemetry(builder.Configuration); and it worked out of box. However, I remember there used to be something like:
builder.Services.AddHealthChecks()
.AddApplicationInsightsPublisher();
builder.Services.AddHealthChecks()
.AddApplicationInsightsPublisher();
but I don't see it in the docs.
14 replies
CC#
Created by Hulkstance on 6/22/2023 in #help
❔ Column names in each table must be unique. Column name x in table y
There is an Azure SQL db with 10 DTUs and migrations are broken with the following message:
Column names in each table must be unique. Column name 'OrphanedBalance' in table 'Transactions.MarketTransaction' is specified more than once.
I renamed 'OrphanedBalance' to something else and I still keep getting the same error message which is really weird. I believe that might be due to some schema caching. Then I exported that database from Azure as a .bacpac file using Azure Data Studio (Data-Tier Application) and imported it locally and it worked fine locally. Any idea what's going on? (edited) June 22, 2023
2 replies
CC#
Created by Hulkstance on 3/24/2023 in #help
❔ Trying to use Azure SignalR Service as Backplane and for client affinity (Sticky sessions)
14 replies
CC#
Created by Hulkstance on 3/18/2023 in #help
❔ EF Core "upsert" - insert if it doesn't exist, update if it does
I'm trying to "optimize" the command below.
The term upsert is a combination of the words “update” and “insert.” In the context of relational databases, an upsert is a database operation that will update an existing row if a specified value already exists in a table, and insert a new row if the specified value doesn't already exist.
There are only two tables (the database structure can be find below), and there are different event types for which we have these three booleans. Administrators can change these global notification settings on demand. If the record for a specific event type id doesn't exist, it should load it at runtime. Since these settings can be updated concurrently by many administrators at the same time, I think I should handle DbUpdateConcurrencyException https://github.com/petercwq/EFCorePractice/blob/master/EFCorePractice/DbContextConcurrencyExtension.cs. What do you think? I'm not so good at EF Core, so I would like to know your opinion. I also had a look at https://stackoverflow.com/questions/5557829/update-row-if-it-exists-else-insert-logic-with-entity-framework and I feel like instead of using a stored procedure or an atomic transaction, I could use Update which cuts off the round trips to the database? I mean I wouldn't need to do .FindAsync and then insert/update.
4 replies
CC#
Created by Hulkstance on 3/11/2023 in #help
❔ ERD Review
3 replies
CC#
Created by Hulkstance on 2/20/2023 in #help
❔ React Redux SignalR
I'm trying to implement an auto-refreshing datagrid (Ant Design Table). Is there a good article on how to implement the React part?
public sealed class StockTickerHub : Hub
{
private readonly IStockTickerService _stockTickerService;

public StockTickerHub(IStockTickerService stockTickerService)
{
_stockTickerService = stockTickerService;
}

public ChannelReader<Stock> GetStockTickerStream(CancellationToken cancellationToken)
{
var channel = Channel.CreateUnbounded<Stock>();

_ = _stockTickerService.SubscribeAsync(channel.Writer, cancellationToken);

return channel.Reader;
}
}

public class Stock
{
public required string Symbol { get; init; }

public required double Price { get; init; }
}

public interface IStockTickerService
{
Task SubscribeAsync(ChannelWriter<Stock> writer, CancellationToken cancellationToken);
}

public sealed class StockTickerService : IStockTickerService
{
public async Task SubscribeAsync(ChannelWriter<Stock> writer, CancellationToken cancellationToken)
{
try
{
while (true)
{
var stock = new Stock
{
Symbol = "TESLA",
Price = Math.Round(Random.Shared.NextDouble() * 100, 2)
};
await writer.WriteAsync(stock, cancellationToken);
await Task.Delay(1000, cancellationToken);
}
}
finally
{
writer.Complete();
}
}
}
public sealed class StockTickerHub : Hub
{
private readonly IStockTickerService _stockTickerService;

public StockTickerHub(IStockTickerService stockTickerService)
{
_stockTickerService = stockTickerService;
}

public ChannelReader<Stock> GetStockTickerStream(CancellationToken cancellationToken)
{
var channel = Channel.CreateUnbounded<Stock>();

_ = _stockTickerService.SubscribeAsync(channel.Writer, cancellationToken);

return channel.Reader;
}
}

public class Stock
{
public required string Symbol { get; init; }

public required double Price { get; init; }
}

public interface IStockTickerService
{
Task SubscribeAsync(ChannelWriter<Stock> writer, CancellationToken cancellationToken);
}

public sealed class StockTickerService : IStockTickerService
{
public async Task SubscribeAsync(ChannelWriter<Stock> writer, CancellationToken cancellationToken)
{
try
{
while (true)
{
var stock = new Stock
{
Symbol = "TESLA",
Price = Math.Round(Random.Shared.NextDouble() * 100, 2)
};
await writer.WriteAsync(stock, cancellationToken);
await Task.Delay(1000, cancellationToken);
}
}
finally
{
writer.Complete();
}
}
}
5 replies
CC#
Created by Hulkstance on 1/29/2023 in #help
❔ FluentValidation on startup
What's not okay so that it fails with:
Unhandled exception. System.InvalidOperationException: No service for type 'FluentValidation.IValidator`1[Sts.MarketData.Api.Models.BinanceConfiguration]' has been registered.
public static class OptionsBuilderFluentValidationExtensions
{
public static OptionsBuilder<TOptions> ValidateFluentValidation<TOptions>(
this OptionsBuilder<TOptions> optionsBuilder) where TOptions : class
{
optionsBuilder.Services.AddSingleton<IValidateOptions<TOptions>>(
provider => new FluentValidationOptions<TOptions>(optionsBuilder.Name, provider));
return optionsBuilder;
}
}

public class FluentValidationOptions<TOptions> : IValidateOptions<TOptions> where TOptions : class
{
private readonly IServiceProvider _serviceProvider;
private readonly string? _name;

public FluentValidationOptions(string? name, IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_name = name;
}

public ValidateOptionsResult Validate(string? name, TOptions options)
{
if (_name != null && _name != name)
{
return ValidateOptionsResult.Skip;
}

ArgumentNullException.ThrowIfNull(options);

using var scope = _serviceProvider.CreateScope();
var validator = scope.ServiceProvider.GetRequiredService<IValidator<TOptions>>();
var results = validator.Validate(options);
if (results.IsValid)
{
return ValidateOptionsResult.Success;
}

var typeName = options.GetType().Name;
var errors = results.Errors
.Select(result => $"Fluent validation failed for '{typeName}.{result.PropertyName}' with the error: '{result.ErrorMessage}'.")
.ToList();

return ValidateOptionsResult.Fail(errors);
}
}
public static class OptionsBuilderFluentValidationExtensions
{
public static OptionsBuilder<TOptions> ValidateFluentValidation<TOptions>(
this OptionsBuilder<TOptions> optionsBuilder) where TOptions : class
{
optionsBuilder.Services.AddSingleton<IValidateOptions<TOptions>>(
provider => new FluentValidationOptions<TOptions>(optionsBuilder.Name, provider));
return optionsBuilder;
}
}

public class FluentValidationOptions<TOptions> : IValidateOptions<TOptions> where TOptions : class
{
private readonly IServiceProvider _serviceProvider;
private readonly string? _name;

public FluentValidationOptions(string? name, IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_name = name;
}

public ValidateOptionsResult Validate(string? name, TOptions options)
{
if (_name != null && _name != name)
{
return ValidateOptionsResult.Skip;
}

ArgumentNullException.ThrowIfNull(options);

using var scope = _serviceProvider.CreateScope();
var validator = scope.ServiceProvider.GetRequiredService<IValidator<TOptions>>();
var results = validator.Validate(options);
if (results.IsValid)
{
return ValidateOptionsResult.Success;
}

var typeName = options.GetType().Name;
var errors = results.Errors
.Select(result => $"Fluent validation failed for '{typeName}.{result.PropertyName}' with the error: '{result.ErrorMessage}'.")
.ToList();

return ValidateOptionsResult.Fail(errors);
}
}
5 replies
CC#
Created by Hulkstance on 1/17/2023 in #help
❔ Rx.NET issue
This is a minimal reproducible example of request/response model (like HTTP) but over websockets. What it technically does is push values to Details until DetailsEnd or Error is received for that particular request ID. It works as expected, excluding the part with the timeout and errorSignal. How do I make it return Result<IEnumerable<Details>>.FromError(new Error(123, "asd", null)); in case of a timeout or an error? https://dotnetfiddle.net/vCTQqw
2 replies
CC#
Created by Hulkstance on 12/26/2022 in #help
❔ Sockets - TCP
I'm going to implement a TCP listener for RF-V48 (4G GPS Tracking Bracelet) for elders. The device is technically a TCP client with its own protocol. An example for the positioning data: Server sends: [CS*YYYYYYYYYY*LEN*CR] e.g.: [SG*5678901234*0002*CR] Tracker returns: [CS*YYYYYYYYYY*LEN*CR] e.g.: [SG*5678901234*0002*CR] Which library would you rather choose? 1) https://github.com/davidfowl/BedrockFramework/tree/main 2) https://github.com/Azure/DotNetty David Fowler pretty much explained why a library is better to use rather than implementing it all yourself https://speakerdeck.com/davidfowl/project-bedrock
2 replies
CC#
Created by Hulkstance on 12/25/2022 in #help
Swagger Doc failed
28 replies
CC#
Created by Hulkstance on 12/7/2022 in #help
❔ ProtoBuf cannot find imports
I've been struggling for a few hours with ProtoBuf.net imports. protobuf-net.BuildTools should technically generate the c# code, but it's unable to find the imports for some reason. What might be wrong? https://github.com/Hulkstance/aquarius/blob/main/src/Sts.Aquarius.Connector/Sts.Aquarius.Connector.csproj#L18
2 replies
CC#
Created by Hulkstance on 12/6/2022 in #help
❔ DynamoDB records are not being added
Do you see anything wrong because CreateAsync returns status code 200, but nothing really is being added. DI
services.AddSingleton<IDynamoDbClientFactory>(provider =>
{
var cfg = provider.GetRequiredService<IOptions<DynamoDbConfiguration>>();

return configuration.GetValue<bool>($"{Constants.AwsDynamoDb}:UseDynamoDbLocal")
? new DynamoDbLocalClientFactory(cfg)
: new DynamoDbClientFactory.DynamoDbClientFactory(configuration);
});

services.AddSingleton<IUserTradeRepository, DynamicClientUserTradeRepositoryAdapter>();
services.AddSingleton<IDynamoDbClientFactory>(provider =>
{
var cfg = provider.GetRequiredService<IOptions<DynamoDbConfiguration>>();

return configuration.GetValue<bool>($"{Constants.AwsDynamoDb}:UseDynamoDbLocal")
? new DynamoDbLocalClientFactory(cfg)
: new DynamoDbClientFactory.DynamoDbClientFactory(configuration);
});

services.AddSingleton<IUserTradeRepository, DynamicClientUserTradeRepositoryAdapter>();
Code is a bit long and unable to paste it here: https://hastebin.com/huwixopaja.csharp
4 replies
CC#
Created by Hulkstance on 11/27/2022 in #help
RangeKey
I'm doing Pulumi IaC. I don't like RangeKey = _rangeKey != null ? (Input<string>?)_rangeKey : null. looks kinda ugly. Is there a better way?
public sealed class DynamoDbTableBuilder
{
private string? _name;
private string? _hashKey;
private string? _rangeKey;

private readonly List<TableAttributeArgs> _tableAttributes = new();

public DynamoDbTableBuilder WithName(string name)
{
_name = name;
return this;
}

public DynamoDbTableBuilder WithTableAttribute(string name, string type)
{
_tableAttributes.Add(new TableAttributeArgs
{
Name = name,
Type = type
});
return this;
}

public DynamoDbTableBuilder WithHashKey(string hashKey)
{
_hashKey = hashKey;
return this;
}

public DynamoDbTableBuilder WithRangeKey(string rangeKey)
{
_rangeKey = rangeKey;
return this;
}

public Table Build()
{
if (string.IsNullOrEmpty(_name))
{
throw new InvalidOperationException("Table name is not set");
}

if (string.IsNullOrEmpty(_hashKey))
{
throw new InvalidOperationException("Hash key is not set");
}

return new Table(_name, new TableArgs
{
Attributes = _tableAttributes,
BillingMode = "PAY_PER_REQUEST",
TableClass = "STANDARD",
StreamEnabled = false,
HashKey = _hashKey,
RangeKey = _rangeKey != null ? (Input<string>?)_rangeKey : null
});
}
}
public sealed class DynamoDbTableBuilder
{
private string? _name;
private string? _hashKey;
private string? _rangeKey;

private readonly List<TableAttributeArgs> _tableAttributes = new();

public DynamoDbTableBuilder WithName(string name)
{
_name = name;
return this;
}

public DynamoDbTableBuilder WithTableAttribute(string name, string type)
{
_tableAttributes.Add(new TableAttributeArgs
{
Name = name,
Type = type
});
return this;
}

public DynamoDbTableBuilder WithHashKey(string hashKey)
{
_hashKey = hashKey;
return this;
}

public DynamoDbTableBuilder WithRangeKey(string rangeKey)
{
_rangeKey = rangeKey;
return this;
}

public Table Build()
{
if (string.IsNullOrEmpty(_name))
{
throw new InvalidOperationException("Table name is not set");
}

if (string.IsNullOrEmpty(_hashKey))
{
throw new InvalidOperationException("Hash key is not set");
}

return new Table(_name, new TableArgs
{
Attributes = _tableAttributes,
BillingMode = "PAY_PER_REQUEST",
TableClass = "STANDARD",
StreamEnabled = false,
HashKey = _hashKey,
RangeKey = _rangeKey != null ? (Input<string>?)_rangeKey : null
});
}
}
3 replies
CC#
Created by Hulkstance on 11/17/2022 in #help
❔ Lazy initialization question
Lazy initialization is usually used whereby you only load or initialize an object when you first need it. The question is how do I know if I need it here: _jetStreamFactory = new Lazy<IJetStream>(() => _connection.CreateJetStreamContext());?
public sealed class NatsPublisher : IPublisher
{
private readonly ProducerConfiguration _configuration;
private readonly IJetStream _jetStream;

public NatsPublisher(IOptions<ProducerConfiguration> options, IConnection connection)
{
_configuration = options.Value;

JetStreamUtils.CreateStreamOrUpdateSubjects(connection, _configuration.Stream, _configuration.Subject);
_jetStream = connection.CreateJetStreamContext();
}

public async ValueTask<PublishAck> Publish<T>(T payload)
where T : class
{
var data = JsonSerializer.SerializeToUtf8Bytes(payload);

var msg = new Msg(_configuration.Subject, null, null, data);

return await _jetStream.PublishAsync(msg);
}

public async ValueTask<PublishAck> PublishAsync(byte[] payload)
{
var msg = new Msg(_configuration.Subject, null, null, payload);
return await _jetStream.PublishAsync(msg);
}

public async ValueTask<PublishAck> PublishAsync(byte[] payload, IEnumerable<(string, string)> headers)
{
var msg = new Msg(_configuration.Subject, null, null, payload);

foreach (var (header, val) in headers)
{
msg.Header[header] = val;
}

return await _jetStream.PublishAsync(msg);
}

public async ValueTask<PublishAck> PublishWithDeduplicationIdAsync(byte[] payload, string id)
{
var msg = new Msg(_configuration.Subject, null, null, payload)
{
Header = { ["Nats-Msg-Id"] = id }
};

return await _jetStream.PublishAsync(msg);
}
}
public sealed class NatsPublisher : IPublisher
{
private readonly ProducerConfiguration _configuration;
private readonly IJetStream _jetStream;

public NatsPublisher(IOptions<ProducerConfiguration> options, IConnection connection)
{
_configuration = options.Value;

JetStreamUtils.CreateStreamOrUpdateSubjects(connection, _configuration.Stream, _configuration.Subject);
_jetStream = connection.CreateJetStreamContext();
}

public async ValueTask<PublishAck> Publish<T>(T payload)
where T : class
{
var data = JsonSerializer.SerializeToUtf8Bytes(payload);

var msg = new Msg(_configuration.Subject, null, null, data);

return await _jetStream.PublishAsync(msg);
}

public async ValueTask<PublishAck> PublishAsync(byte[] payload)
{
var msg = new Msg(_configuration.Subject, null, null, payload);
return await _jetStream.PublishAsync(msg);
}

public async ValueTask<PublishAck> PublishAsync(byte[] payload, IEnumerable<(string, string)> headers)
{
var msg = new Msg(_configuration.Subject, null, null, payload);

foreach (var (header, val) in headers)
{
msg.Header[header] = val;
}

return await _jetStream.PublishAsync(msg);
}

public async ValueTask<PublishAck> PublishWithDeduplicationIdAsync(byte[] payload, string id)
{
var msg = new Msg(_configuration.Subject, null, null, payload)
{
Header = { ["Nats-Msg-Id"] = id }
};

return await _jetStream.PublishAsync(msg);
}
}
2 replies
CC#
Created by Hulkstance on 11/16/2022 in #help
Decorator vs Adapter pattern
I wonder what the difference between the two is
Match interfaces of different classes
Decorator: Add responsibilities to objects dynamically
16 replies
CC#
Created by Hulkstance on 11/12/2022 in #help
❔ Turn off VS 2022 Preview
How do I turn off VS 2022 Preview? It is showing on the splash screen and everywhere. I turned it on to try .NET 7 features but it's out right now, so I want to turn it off.
6 replies
CC#
Created by Hulkstance on 11/9/2022 in #help
❔ Data Annotations Configuration Validation
Pretty self explanatory, how do I validate the configuration I'm getting from the AWS section based on Data Annotations?
public static IServiceCollection AddDynamoDb(this IServiceCollection services)
{
ArgumentNullException.ThrowIfNull(services);

services.AddSingleton<IAmazonDynamoDB>(provider =>
{
var configuration = provider.GetRequiredService<IConfiguration>();

// TODO: Validate
var dbConfiguration = configuration.GetSection("AWS:DynamoDB").Get<DynamoDbConfiguration>();

if (dbConfiguration is null)
{
throw new ArgumentNullException(nameof(dbConfiguration), "Please configure DynamoDB");
}

return new AmazonDynamoDBClient(
new BasicAWSCredentials(dbConfiguration.AccessKey, dbConfiguration.SecretKey),
new AmazonDynamoDBConfig
{
ServiceURL = dbConfiguration.ServiceUrl,
AuthenticationRegion = dbConfiguration.Region
});
});

return services;
}

public class DynamoDbConfiguration
{
[Required]
public required string AccessKey { get; init; }

[Required]
public required string SecretKey { get; init; }

[Required]
[RegularExpression(@"[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)")]
public required string ServiceUrl { get; init; }

[Required]
[RegularExpression(@"(us(-gov)?|ap|ca|cn|eu|sa)-(central|(north|south)?(east|west)?)-\d+")]
public required string Region { get; init; }
}
public static IServiceCollection AddDynamoDb(this IServiceCollection services)
{
ArgumentNullException.ThrowIfNull(services);

services.AddSingleton<IAmazonDynamoDB>(provider =>
{
var configuration = provider.GetRequiredService<IConfiguration>();

// TODO: Validate
var dbConfiguration = configuration.GetSection("AWS:DynamoDB").Get<DynamoDbConfiguration>();

if (dbConfiguration is null)
{
throw new ArgumentNullException(nameof(dbConfiguration), "Please configure DynamoDB");
}

return new AmazonDynamoDBClient(
new BasicAWSCredentials(dbConfiguration.AccessKey, dbConfiguration.SecretKey),
new AmazonDynamoDBConfig
{
ServiceURL = dbConfiguration.ServiceUrl,
AuthenticationRegion = dbConfiguration.Region
});
});

return services;
}

public class DynamoDbConfiguration
{
[Required]
public required string AccessKey { get; init; }

[Required]
public required string SecretKey { get; init; }

[Required]
[RegularExpression(@"[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)")]
public required string ServiceUrl { get; init; }

[Required]
[RegularExpression(@"(us(-gov)?|ap|ca|cn|eu|sa)-(central|(north|south)?(east|west)?)-\d+")]
public required string Region { get; init; }
}
6 replies