Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
No Pants
Dec 10, 2000


For where it goes, you usually put all that configuration and registration in your main assembly where it will execute once on application startup, somewhere sane people don't unit test. In a WPF application, that might be an overridden Application.OnStartup(StartupEventArgs).

You very probably do not want to put it in the constructor of a viewmodel, since that's not a viewmodel's responsibility, and it makes that viewmodel less testable.

Adbot
ADBOT LOVES YOU

No Pants
Dec 10, 2000

A big one is that classes don't inherit interface members (they implement them). That means to use a default implementation, you have to cast an instance of the class to the interface, similar to how you'd use explicitly implemented interface members.

No Pants
Dec 10, 2000

I don't think even MS-authored libraries pay attention to that guidance.

No Pants
Dec 10, 2000

TooMuchAbstraction posted:

I'm getting an error at the Add statement in Subscribe: "cannot convert from System.Action<T> to System.Action<PubsubEvent>". Which is strange, because the function has a constraint on it that T must be a PubsubEvent. Any idea what's going on here?

The type parameter of Action<T> is contravariant, not covariant (i.e. you may assign an instance of Action<TBase> to a variable of Action<TDerived>, but not the other way around). Read more about this here.

Without saying anything about your design, to get this to compile, store the delegates as plain objects and cast them to Action<T> in your Publish method.

No Pants
Dec 10, 2000

TooMuchAbstraction posted:

Thanks! Jabor gave a similar response in the General Programming thread. It's unfortunate, but the type-unsafety is kept to a small part of the code so I guess I can live with it.

If you'd care to, I'm curious what your opinions are about the design.

Without knowing more, there doesn't seem to be a need for any of PubSub's members to be static. Storing your state in an instance of your service would let the programmer decide how global that state should be, and it would be easier to test.

Having a delegate object be the identity of a subscription is a little suspect, especially when you're storing them in a HashSet<>. You'll need to be conscious of delegate equality and think about edge cases.

No Pants
Dec 10, 2000

This blog post came out last month, and it's good reading if you have any interest in what ConfigureAwait does.

No Pants
Dec 10, 2000

Whatever serializer is running there might be expecting a JSON array of numbers.

No Pants
Dec 10, 2000

Polio Vax Scene posted:

I'm lost and need some guidance. We're building an integration with a third party that uses JWTs to authenticate users. The tokens are signed using an x509 cert.
When I use the System.IdentityModel.Tokens.Jwt package to generate a token, it adds an x5t header to the token. We need this to be an x5c header instead, as the third party will not support x5t signed tokens. I've been struggling to find a way to change how the token is signed with no luck.

There isn't a way to force the JwtSecurityToken class to add the x5c header. The good (?) news is that JwtHeader is a subclass of Dictionary<string, object>, so you can add the x5c header yourself as an array of strings, and it will be serialized correctly when you turn the token object into a JWT.

No Pants
Dec 10, 2000

rarbatrol posted:

You could wrap the sync method in something like await Task.Run(stuff) or put an await Task.Yield just before it and it may behave a little better for you.

await Task.Yield(); might be useful in an environment without a synchronization context, but here, if there's something running synchronously behind the scenes in HttpClient or something (which I've heard tell is the case in at least some versions of .NET), it won't do anything because the synchronization context will schedule the continuation on the UI thread and might even give it a higher priority than processing inputs and window messages.

No Pants
Dec 10, 2000


David Fowler's writeup is also a nice introduction to this stuff.

No Pants
Dec 10, 2000

One of the objections I've heard even when the type is explicit on the right side of a declaration is that it can feel weird in some way for left-to-right readers. C# 9.0 adds target-typed new expressions, so pedants will have a new dimension of opinions to inflict on each other in a few days.
code:
Butt b = new Butt();
var b = new Butt();
Butt b = new();

No Pants
Dec 10, 2000

please stop building distributed monoliths, i'm dying

No Pants
Dec 10, 2000

edit: maybe misinterpreted the problem :shrug:

what if you...threw an exception for an error :unsmigghh:

No Pants fucked around with this message at 13:01 on Feb 28, 2021

No Pants
Dec 10, 2000

You usually only find out that they're incompatible during runtime when your poo poo breaks because a method is missing somewhere.

No Pants
Dec 10, 2000

It gives the number you wanted in your example at least, on .NET 5. You only need G9 to round-trip a float, so it seems to be formatting as much as it can. Maybe. :shrug:

No Pants
Dec 10, 2000

The new extension methods are for fairly simple scenarios. They're just calling HttpResponseMessage.EnsureSuccessStatusCode(), which only gives you the status code, so you're on the right track.

Mind, you can still use GetFromJsonAsync() and have retries based on response headers if you feel up to creating a custom retry policy using Polly.

No Pants fucked around with this message at 04:48 on Jan 6, 2022

No Pants
Dec 10, 2000

Rocko Bonaparte posted:

I had seen your response yesterday but I had to double check something today. I also have a similar with where I use a List<> and I thought Equals() was comparing contents there. But I checked it today and bam, I am manually comparing contents. So that is what I get for 2AM coding.

if this is still for unit testing, testing frameworks usually have an equality assertion method that compares the contents of collections.

No Pants
Dec 10, 2000

If what's in System.Globalization isn't what you're after, you probably need to go directly to Normalizer2 from ICU. Here's an example using icu.net:
C# code:
using Icu;
using Icu.Normalization;

Wrapper.Init();
var input = "der Fluß";
var output = Normalizer2.GetNFKCCasefoldInstance().Normalize(input);
Wrapper.Cleanup();
Console.WriteLine(output);
// outputs: der fluss

No Pants
Dec 10, 2000

I can see that being adequate if it's being passed around, especially if you aren't adding keys. The AsyncLocal<>.Value property's get/set methods retrieve and store the actual references to the values on the current ExecutionContext, so there shouldn't be a memory leak concern. (Maybe if you're doing something weird with the event handler delegate, as that's the only state stored by an AsyncLocal instance.)

No Pants
Dec 10, 2000

nielsm posted:

Case folding is a particular algorithm/transformation specified by the Unicode standard, yes, and it isn't quite the same as an "uppercase" or "lowercase" function, which is by definition human language sensitive.

Searching I find this: https://github.com/dotnet/corefxlab/pull/2637
But I can't figure out where this is supposed to be implemented or available. The PR says it's merged nearly 3 years ago, but I don't see any trace of it in the .NET 5 or 6 library. Maybe System.Text.CaseFolding is available as a NuGet package?

Case folding is tracked by this issue, and there's some more discussion here. The gist is that the .NET team is reluctant to maintain globalization functionality themselves, and if they were going to expose APIs for case folding, they'd likely call into the ICU library provided by the OS, probably with some workarounds for older versions of Windows.

No Pants
Dec 10, 2000

fuf posted:

man I still have such issues with EF and saving related entities. I just don't get it at all.

[...]

So then I think ok, maybe I need to attach the Filters in the db to the current context so that it doesn't try to insert duplicates? Like this:

C# code:
using var db = contextFactory.CreateDbContext();
var Filters = db.Filters.ToList();
db.Playlists.Add(playlist);
db.SaveChanges();
But then the error is:
"The instance of entity type 'Filter' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked."

I think this is because the context now has two versions of the same Filter object: one from the DB and one attached to the Playlist object.

How can I tell the context that they're the same object??

Assuming this is EF Core, you're on the right track. You can re-attach the Filter object with DbContext.Update():
C# code:
using var db = contextFactory.CreateDbContext();
db.Update(playlist.Filter);
db.Playlists.Add(playlist);
db.SaveChanges();
The documentation goes over some different scenarios.

No Pants fucked around with this message at 17:14 on Feb 5, 2022

No Pants
Dec 10, 2000

The compiler (pre- and post-.NET 6) will convert an interpolated string to a string literal or a string.Concat call if the parameters line up, so it isn't all bad. The main thing in .NET 6 is fewer heap allocations for the non-trivial cases (edit: and exposing some types that allow methods to consume interpolated strings and do stuff themselves).

No Pants fucked around with this message at 19:47 on Mar 3, 2022

No Pants
Dec 10, 2000

insta posted:

I know the compiler converts simple, compile-time string concats into a string.Concat call -- but I'm seriously surprised it can't figure out how to do that for the Interpolated example in my code.

Yeah, I was talking about $""-type string interpolation. The earlier behavior where $"" is a shortcut for string.Format was more desirable because using string.Concat would allocate an extra string for the formatted int.

No Pants
Dec 10, 2000

The C# 10 compiler can lower an interpolated string to using the DefaultInterpolatedStringHandler type, too.

Like this
C# code:
using System;

var num = 100;
var thing = "butt";
var s = $"hello {num} {thing}s";
Console.WriteLine(s);
turns into this (unoptimized)
C# code:
[CompilerGenerated]
internal static class <Program>$
{
    private static void <Main>$(string[] args)
    {
        int value = 100;
        string value2 = "butt";
        DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(7, 2);
        defaultInterpolatedStringHandler.AppendLiteral("hello ");
        defaultInterpolatedStringHandler.AppendFormatted(value);
        defaultInterpolatedStringHandler.AppendLiteral(" ");
        defaultInterpolatedStringHandler.AppendFormatted(value2);
        defaultInterpolatedStringHandler.AppendLiteral("s");
        string value3 = defaultInterpolatedStringHandler.ToStringAndClear();
        Console.WriteLine(value3);
    }
}
The docs and this blog post are helpful if anyone's burning with curiosity about it.

No Pants
Dec 10, 2000

epswing posted:

Am I missing something obvious here?

The documentation for what you're trying to do is hidden here. After adding the Microsoft.Extensions.Logging.AzureAppServices package, these are the relevant parts in .NET 6:
C# code:
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddAzureWebAppDiagnostics();

builder.Services.Configure<AzureBlobLoggerOptions>(options =>
{
    options.BlobName = "blah.txt";
});

No Pants
Dec 10, 2000

epswing posted:

I'm having more fun with AspNetCore logging.

Log levels for App Service file logging are controlled by a file that's apparently normally updated through the Azure Portal. Looking through the source code, it's a setting called "AzureBlobTraceLevel" in %HOME%/site/diagnostics/settings.json. The provider also doesn't do category-based filtering, so if you want that, you need to use a more robust logging provider.

No Pants
Dec 10, 2000

epswing posted:

Ah you're right, the settings.json file is controlled by the Azure "App Service logs" UI page


Here's the before/after changing from Warning to Information in the UI


Of course, when it's set to Information I see Microsoft's noise, and when set to Warning, I don't see my Information logs.

I wish this was more obvious. There's all this documentation about how to configure and fine-tune logging via filters, and none of it matters at all if you're using Microsoft.Extensions.Logging.AzureAppServices? So basically anyone doing anything of substance is using NLog/log4net/serilog/etc? Unless I'm misunderstanding something, what sense does it make for the Azure logging provider to not reference and filter the categories set in the project's appsettings.json file.

Edit: Found it here :sigh:
I think a lot of it is PaaS offerings like App Service and Function Apps showing their age and little pressure (or maybe just budget) to get them back up to where .NET is heading.

Where I work, I've mostly seen services exporting to Application Insights/Log Analytics or an internal OpenTelemetry thing, which only requires configuring logging providers like you were doing.

No Pants
Dec 10, 2000

NihilCredo posted:

C# code:
var thing = fetchSome(usershit);

if (thing is { SomeProperty = null }) {
   log("your poo poo is hosed");
   return;
}

DoSomething(thing.SomeProperty);

The guard clause desugars to "thing?.SomeProperty == null", so it will catch a null 'thing', which is the right choice. But having a specific property being null is a different error than the entire object being null (the former means the client hosed up, the latter could be the fetchSome function that hosed up).

I might be missing something, but that code block will throw a NullReferenceException if thing is null. A property pattern only matches an expression if the expression result is not null.

No Pants
Dec 10, 2000

Red Mike posted:

Property patterns won't throw if it's null, it'll just early quit and act as if it's not a match.

From https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/patterns#property-pattern.

Yeah, that's what I posted. It'll throw an exception when it gets to DoSomething(thing.SomeProperty);

Adbot
ADBOT LOVES YOU

No Pants
Dec 10, 2000

Munkeymon posted:

I'm trying to hook up Azure App Services running a Framework ASP monolith up to an App Configuration service and I've almost got it working with just web.config changes, but I can't figure out how to load the connectionString (or endpoint for that matter) from the environment/App Service's Configuration. Is there some "magic" way to tell the config parser to grab a string from the environment's connection strings or... something? Configuration builders section of the config for reference:

code:
  <configBuilders>
    <builders>
      <add name="AzureConfigurationSerivice" connectionString="halp what do here?" mode="Greedy" type="Microsoft.Configuration.ConfigurationBuilders.AzureAppConfigurationBuilder, Microsoft.Configuration.ConfigurationBuilders.AzureAppConfiguration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      <add name="Environment" mode="Greedy" type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Environment, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </builders>
  </configBuilders>
That works as long as I crack open the live config and paste in a connection string, but obviously that's not gonna cut it for automated deploys and eventually I'd want to either put the string in a keyVault or, better, just pass it the endpoint and let IAM do its thing. Anyone here made this work?

You've probably already figured it out, but there's a quickstart that shows how to grab the connection string from configuration. Adding AzureKeyVaultConfigBuilder to the mix is probably not too difficult.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply