u/deleteAllfromUsersJk

85 post karma · 248 comment karma · joined Dec 2018

  1. 1points

    DEXA said 10.4% body fat. Accurate? (self.fit) [Physique Critique]

    3 comments

  2. 2points

    Could a staffing agency’s non-solicit agreement cause issues during a background check? (self.consulting)

    3 comments

  3. 1points

    Could a staffing agency’s non-solicit agreement cause a new employer to back off after a background check? (self.careerguidance)

    0 comments

  4. 0points

    Reloading IConfiguration on change to json file in Azure Functions App (local) (self.dotnet)

    19 comments

deleteAllfromUsersJk

I have been going with a spec first approach for several years now and quite enjoy it.

Our workflow is 1) change the spec (we still call it swagger.yaml), 2) generate controllers/models with openapi-generator-cli (alternatives exist), 3) incorporate codegen output and implement the service.

So to answer your questions:

  • How do you track endpoint changes? Since we start with writing the spec and it is in our repo, it is tracked in git just like our source code. Easy.
  • Do you ever struggle with inconsistent or incomplete docs? Nope (this is a pro of spec first), but we do sometimes err in the opposite direction: over-specifying and then never used some fields/endpoints we thought we would when we initially wrote the spec.
  • What’s your biggest pain when maintaining API documentation? When editing a single, growing yaml spec, we can lose track of the schemas we've already written when doing something new. There've been times this has led me to specify less than ideal models (redundance, inconsistent naming, wonky inheritance)

We use this same spec to generate our SPA client code which lets us dogfood it early. I know you didn't say your current approach, but I’m guessing you implement first and then write the spec (which is totally fine). Switching to spec-first flips the trade-offs: you get tracking and documentation for free, but you give up some control over the generated code.

For anyone who hasn't tried spec first api development, I recommend trying it out to see if you like it.

deleteAllfromUsersJk

+1 for the .NET meetup. I’ve picked up several practices there that I now use at work.

deleteAllfromUsersJk

That link was my own repro. I've tested everything I've stated.

deleteAllfromUsersJk

More about local.settings.json here.

deleteAllfromUsersJk

I've done my homework, I promise.

As I answered in our previous thread, this is a function app and uses local.settings.json.

My use case is for local development only, as explained in the original post.

deleteAllfromUsersJk

Had to come back to this after some time, but finally figured it out: Turns out that function apps don't look at the source files, but rather the published files when monitoring for changes. Never occurred to me to think about publish vs source files since it just works by default in web apps. Posted a comment w/ solution.

deleteAllfromUsersJk

UPDATE/SOLUTION:

For anyone reading this in the future, I've found a solution for reloading configuration based on file changes during local development.

It appears that dotnet's AddJsonFile extension method behaves slightly differently in a function app vs a web app. When using this method in development in a web app, the source file is monitored for changes, while in a function app, the published file is monitored.

This change to my Program.cs above achieves the desired functionality in development:

...

if (hostingContext.HostingEnvironment.IsDevelopment())
{
    var root = hostingContext.HostingEnvironment.ContentRootPath;

    // use file in source directory instead of bin
    var path = Path.Combine(root, @"../../../", "local.settings.json");

    config.AddJsonFile(path, optional: false, reloadOnChange: true);
}

...

Without combining local.settings.json with "../../../", the builder uses the published file, which is ./bin/Debug/netx.x/local.settings.json. This makes sense, since this is where the app is running from. It never occured to me that a web app must be doing something to intentionally monitor the source file. This doesn't happen automatically in a function app, so the Path.Combine(...) does this as long as the project uses the default publish output settings.

The documentation for AddJsonFile states that the path argument to this method is, "Path relative to the base path stored in Properties of builder." My guess is that there are implementation differences in the IConfigurationBuilder used depending on the project type. When working with IHostBuilder in a function app's Program.cs, the builder has an IConfigurationBuilder property named Configuration that calls AddJsonFile, while WebApplicationBuilder's Configuration property that calls the same method is a concrete ConfigurationManager type that implements IConfigurationBuilder.

deleteAllfromUsersJk

When I’m not traveling, I’ll set my GMT hand to UTC, which is the standard for dealing with timestamps in computing. It’s occasionally useful when debugging something in real time.

deleteAllfromUsersJk

One more thing I spotted: Something I'm removing from my service collection that you are not is typeof(DbContextOptions<MyDbContext>).

DbContextOptions<TContext> is the type passed to the base DbContext when you contruct your app's db context, so that could possibly affect how your test db context is doing auth, depending on what else you are doing in your setup.

deleteAllfromUsersJk

I use mssql test containers on a Microsoft provided ubuntu agent without any issues. Here's a sanitized version of my setup:

``` using DotNet.Testcontainers.Builders; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Testcontainers.MsSql; using MyNamespace.Api // for IApiMarker / WebApplicationFactory pattern using MyNamespace.Data.EF; // My EF Context

namespace MyNamespace.Test.Setup { public class MyApiFactory : WebApplicationFactory<IApiMarker>, IAsyncLifetime { // Need to pin specific sql server version. See https://github.com/testcontainers/testcontainers-dotnet/issues/1220 private readonly MsSqlContainer _dbContainer = new MsSqlBuilder() .WithImage("mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04") .WithWaitStrategy(Wait.ForUnixContainer().UntilCommandIsCompleted("/opt/mssql-tools18/bin/sqlcmd", "-C", "-Q", "SELECT 1;")) .Build();

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {

        Dictionary<string, string> inMemoryConfig = TestConstants.InMemoryConfig; // specific for my tests

        // This is a workaround. See https://github.com/dotnet/aspnetcore/issues/37680.
        var config = new ConfigurationBuilder()
            .AddInMemoryCollection(inMemoryConfig).Build();

        builder
            .UseConfiguration(config)
            .ConfigureTestServices(services =>
            {
                var masterConn = _dbContainer.GetConnectionString();
                var dbConn = masterConn.Replace("Database=master", "Database=MyAppDb");

                services.RemoveAll(typeof(DbContextOptions<MyDbContext>));

                services.AddDbContext<MyDbContext>(options
                    => options.UseSqlServer(dbConn));

                // ... other stuff specific to my app
            });
    }

    public async Task InitializeAsync()
    {
        await _dbContainer.StartAsync();
        using var scope = Services.CreateScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
        await dbContext.Database.MigrateAsync();
    }

    async Task IAsyncLifetime.DisposeAsync() => await _dbContainer.DisposeAsync();
}

} ```

I'd think the only relevant code for comparison would be inside IWebHostBuilder.ConfigureTestServices(). The only thing I can see that I do that you don't is replace the master db in the container provided connection string with my app db. Which I don't think would affect auth to the server. Aside from that, I'm not specifying anything outside the defaults (no password, ports, etc.) and that's enough for my integration tests to run. That just makes my EF context use sql auth to the standard port, I'm fairly certain.

Can't say why your CI is trying Kerberos, just sharing my setup in case there is something in here that helps you.

edit: spelling

deleteAllfromUsersJk

This has always been an unresolved question in the back of my mind, I assumed because I haven't researched it correctly. Like ok, I can take ibuprofen to reduce inflammation for mediocre pain relief, but does that do anything whatsoever to promote healing and tissue repair?

deleteAllfromUsersJk

It does. I'm looking at it now. We're talking about an Azure Functions app here, not an ASP.NET Core app. local.settings.json is the file used by an Azure Functions app by default for local development. You'll see this file if you create any Azure Functions project from a template in Visual Studio, here it is in Github. The app config setup code I'm using above is also fairly common for function apps (here and here).

I only mentioned a web api above (an ASP.NET Core app that uses the convention you described) to show that I confirmed my assumptions for how it's supposed to work (in this case, change appsettings.Development.json, save file, configuration values are updated at runtime). My question is specifically whether this same result is possible in a dotnet isolated worker function app.

deleteAllfromUsersJk

I'm sure. All of my configurations from this file are loaded on startup, they just don't update.

deleteAllfromUsersJk

In my web api, I sent a request 1 - 2 secs after changing the file and the value in IConfiguration/IOptionsMonitor was updated.

In my functions app, I can change the file and nothing is updated after seconds, minutes.

I didn't wait longer than probably 5 minutes before making changes and restarting my app because I wouldn't think that cache time for the config provider would be the specific difference between api / function app.

deleteAllfromUsersJk

Understood. To be clear, I am only injecting IConfiguration to debug my setup to observe the values from the configuration providers directly, isolated from my IOptions bindings. I can inject either IConfiguration or IOptionsMonitor in an ASPNET Web API and get the changes, but neither detects a change from a local json file in my function app.

deleteAllfromUsersJk

If this is for work and you need to satisfy the business w/ time efficiency in mind: I’d say pick Dapper if you know you need the extra performance AND know how to SQL good enough to get said performance. Otherwise, go EF.

If you’re learning/experimenting, use both. Start with EF if you’re new to .NET. Start with/ Dapper if you need to be forced to learn SQL. Maybe do your reads w/ Dapper and writes w/ EF.

deleteAllfromUsersJk

To answer you IIS / cmd line question: Assuming you’re referencing the option to launch your app w/ IIS Express, that’s the launch profile Visual Studio seems to default to on Windows and that launch profile tells your system to use IIS Express (a local web server for development) to run your app. From command line, if you’re using any .NET version from the past something years, it’s probably using Kestrel. Command line has to use Kestrel (a cross platform web server), not IIS, bc IIS is specifically a Windows program. Well, dotnet is cross platform, so when you use the cli, like ‘dotnet run’ it’s going to use Kestrel.

deleteAllfromUsersJk

Yeah, I think anyone who is aware of sql permissions is going to have a different service principal / sql login that runs the app than the one that applies migrations to the servers.

That said, have I run ‘Update-Database’ from VS against a live QA server before? Not telling you.

deleteAllfromUsersJk

+1 for mentioning IaC as a way of learning Azure. These days the resource I learn the most from is the Terraform Azure provider docs while I'm researching how to provision *exactly* what I need via tf scripts. I presume the experience with Bicep and alternatives is similar.

deleteAllfromUsersJk

I’ll be on the lookout and keep my eyes open, especially when I’m around “the” highway. Look out for my letter.

deleteAllfromUsersJk

If you’re serving the react app separately from the api, like with webpack server or something, make sure you’re not calling your front end server instead of your api. Check that port 4444 is in fact your .net app.