🔧 Programmierung7 Common Python Mistakes to Avoid in AI Workflows(01.09.2026 um 14:00 Uhr)
🔧 ProgrammierungThis Python Library Can Run Pandas Workloads Up to 20x Faster(02.09.2026 um 16:00 Uhr)
🔧 Programmierung7 Async Patterns for Running Agents Concurrently in Python(11.08.2026 um 14:00 Uhr)
🔧 ProgrammierungManaging Small Context Windows in Language Models(18.08.2026 um 14:00 Uhr)
🔧 ProgrammierungLearn Vectorized Thinking in Python Through Examples(26.08.2026 um 14:00 Uhr)
🔧 ProgrammierungJavaScript obfuscation: From party trick to phishing kit(27.08.2026 um 12:00 Uhr)
⚠️ Malware / Trojaner / Viren2026-09-01: Essential macOS Stealer infection(04.09.2026 um 21:29 Uhr)
🕵️ SicherheitslückenExploits and vulnerabilities in Q2 2026(26.08.2026 um 12:00 Uhr)
🔧 Programmierung7 Common Python Mistakes to Avoid in AI Workflows(01.09.2026 um 14:00 Uhr)
🔧 ProgrammierungThis Python Library Can Run Pandas Workloads Up to 20x Faster(02.09.2026 um 16:00 Uhr)
🔧 Programmierung7 Async Patterns for Running Agents Concurrently in Python(11.08.2026 um 14:00 Uhr)
🔧 ProgrammierungManaging Small Context Windows in Language Models(18.08.2026 um 14:00 Uhr)
🔧 ProgrammierungLearn Vectorized Thinking in Python Through Examples(26.08.2026 um 14:00 Uhr)
🔧 ProgrammierungJavaScript obfuscation: From party trick to phishing kit(27.08.2026 um 12:00 Uhr)
⚠️ Malware / Trojaner / Viren2026-09-01: Essential macOS Stealer infection(04.09.2026 um 21:29 Uhr)
🕵️ SicherheitslückenExploits and vulnerabilities in Q2 2026(26.08.2026 um 12:00 Uhr)

🔧 Programmierung 🕛 kürzlich 18 Min Lesezeit
0

Preparing for Migration: Decoupling Your Function Logic

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

When does pushing logic out of the Function method actually pay off? The day the Consumption-plan batch runs past 10 minutes, the host kills the worker, and the same queue message is delivered again from the top. The trigger binding turns out to be a hosting contract, not a programming model: a queue trigger and a BackgroundService loop are two different shapes for the same job. Once the workload sits behind an injected service, swapping one shape for the other becomes a Program.cs change instead of a rewrite, and tests stop needing the Functions host.



The pattern shows up most clearly in a working sample. The gets killed, and the same message comes back from scratch.

  • Changing the failure-rate threshold means redeploying the Function. Nothing in the loop is testable without spinning up the host.

  • Replacing the dead-letter queue with Service Bus is a method rewrite. The branching, the serialization, and the SDK call are tangled at the call site.






  • After: the contract in code



    Push the loop into IPaymentSettler and the trigger collapses to its three jobs. From . The body of the workload sits behind IPaymentSettler.SettleAsync(SettlementBatch, IProgress<SettlementProgress>?, CancellationToken). The CancellationToken propagates through.






    What does not belong, with evidence from the sample



    Each rule below would show up as code in SettlementFunction.cs if it had been violated. None of them is.





    • Business rules. No validation, no per-payment branching, no calculation in Run. The accept/reject branch and the rejection log live in . From Settlement.AppService/Program.cs:




      CODE
      builder.Services.AddAzureClients(clientBuilder =>
      {
      if (!string.IsNullOrWhiteSpace(queueConnection))
      {
      clientBuilder.AddQueueServiceClient(queueConnection);
      }
      else if (!string.IsNullOrWhiteSpace(queueServiceUri))
      {
      clientBuilder.AddQueueServiceClient(new Uri(queueServiceUri));
      clientBuilder.UseCredential(new DefaultAzureCredential());
      }
      else
      {
      throw new InvalidOperationException(
      "Queue:ConnectionString or Queue:ServiceUri must be configured.");
      }
      });






      Two branches, one shared registration. The ConnectionString branch is for local development against Azurite; the ServiceUri branch uses . That is a hosting change, not a code change.



      Keep the asymmetry in mind when reading the three Program.cs files side by side. The App Service and Container App hosts manage the queue client themselves because their BackgroundService is the activation surface. The Function host hands that job to the binding.






      Building portable service classes



      A service class is portable when three rules hold. Each one rules out a specific failure I have watched bite during migration.






      Rule 1: zero references to Microsoft.Azure.Functions.* in the project file



      The portability claim has to survive grep. In MigrationDemo:




      CODE
      $ grep -r "Microsoft.Azure.Functions" Settlement.Core/
      (no matches)

      $ dotnet list package --include-transitive --project Settlement.Core/Settlement.Core.csproj | grep -i Functions
      (no matches)






      Settlement.Core.csproj declares three direct references, all Microsoft.Extensions.*: DI abstractions, logging abstractions, options. If anything in that list grows a transitive Microsoft.Azure.Functions.* dependency, the library is no longer host-agnostic and the migration story stops working. Make this check part of CI so it does not regress quietly.






      Rule 2: configuration via IOptions<T> with validated binding



      PaymentSettler reads its only knob through PaymentSettlerOptions:




      CODE
      public sealed class PaymentSettlerOptions
      {
      public const string SectionName = "PaymentSettler";

      [Range(1, 100_000)]
      public int ProgressReportInterval { get; init; } = 1;
      }






      Every host binds the same options the same way:




      CODE
      builder.Services
      .AddOptions<PaymentSettlerOptions>()
      .Bind(builder.Configuration.GetSection(PaymentSettlerOptions.SectionName))
      .ValidateDataAnnotations()
      .ValidateOnStart();






      What changes between hosts is the source of the value, not the shape:




      • Functions: PaymentSettler__ProgressReportInterval as an app setting, or Values:PaymentSettler:ProgressReportInterval in local.settings.json. The double underscore is the cross-platform .

      • The Container App host hits the same root-scope rule because its activation surface is also a BackgroundService.



      The fix is to inject IServiceScopeFactory into the BackgroundService and call CreateScope() per message, then resolve the scoped dependency from the scope. The Function host does not need that wrapping. Flag this before the first scoped dependency lands, or the App Service and Container App hosts will diverge from Functions in a way that only shows up at runtime.






      Testing without the Functions runtime



      A unit test that spins up the Functions host to verify a discount calculation takes four seconds to start, and goes red every time the worker SDK ships a new version. The discount calculation has nothing to do with the trigger. Push it behind IPaymentSettler and the test becomes plain xUnit construction against the service class.



      PaymentSettler takes three constructor parameters, all in Microsoft.Extensions.*. The unit test resolves them by hand:




      CODE
      [Fact]
      public async Task SettleAsync_with_all_accepting_gateway_reports_full_settlement()
      {
      var batch = new SettlementBatch(
      BatchId: "test-1",
      CutoffUtc: DateTimeOffset.UtcNow,
      Payments: Enumerable.Range(0, 10)
      .Select(i => new Payment($"p-{i}", 100m, "EUR"))
      .ToList());

      var settler = new PaymentSettler(
      gateway: new AlwaysAcceptingGateway(),
      options: Options.Create(new PaymentSettlerOptions { ProgressReportInterval = 1 }),
      logger: NullLogger<PaymentSettler>.Instance);

      var result = await settler.SettleAsync(batch, progress: null, CancellationToken.None);

      Assert.Equal(10, result.Settled);
      Assert.Equal(0, result.Failed);
      }

      private sealed class AlwaysAcceptingGateway : ISettlementGateway
      {
      public Task<SettlementResponse> SubmitAsync(Payment payment, CancellationToken ct) =>
      Task.FromResult(new SettlementResponse(payment.PaymentId, Accepted: true, ReasonCode: null));
      }






      No TestHostBuilder, no local.settings.json, no func start. The test runs in milliseconds. The hand-rolled AlwaysAcceptingGateway is the lever that makes the assertion deterministic: the FakeSettlementGateway shipped with the sample uses a hash-and-threshold check that is great for reproducible demos and inconvenient when the question is "what happens when this specific batch is all accepted?". Microsoft's . Azurite supports Blob and Queue in GA; the Table emulator is in preview, which is only worth flagging if the suite grows a Table dependency.



      The Functions variant has no first-party in-process test host for the isolated worker. The integration story there is "run func start once per build and stimulate the queue." Microsoft documents one in-process Functions test fixture, Microsoft.DurableTask.InProcessTestHost, in the and made the cost ceilings visible by walking the Consumption, Premium, and Dedicated plans against real workloads. enumerated the four signals that justify a move: timeout walls, sprawl, coupling patterns, cost crossover. This article closes the loop by making the move mechanical. Once the trigger is a thin controller and the workload sits behind IPaymentSettler, the diff between Function App, App Service, and Container App is a Program.cs change.



      Three places to go from here:





      1. Series 3 (forthcoming) integrates .NET Aspire into the Functions workflow: AppHost orchestration, Service Bus, Storage, and Redis as Aspire resources, and azd deployment to Container Apps. The decoupled Settlement.Core library carries straight across; only the composition root learns about Aspire.


      2. The (still relevant as the decoupling that makes any further migration tractable) and the broader (Parts 1-9)




        Vollständiger Original-Bericht
        Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
        ↗ Original-Artikel auf dev.to lesen
    Wie bewertest du diesen Beitrag?
    1 Klick Feedback
    Teilen mit Netzwerk & Team:

    Community-Analysen & Experten-Meinungen 0

    Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
    Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
    Community Pulse: Relevanz-Einschätzung
    1 Klick Experten-Votum
    🔴 Akute Relevanz 37%
    🟡 In Evaluierung 22%
    🟢 Keine Auswirkung 19%
    Spannende Innovation 22%
    Verwandte Story-Cluster & Quellen (Vektor-KI)
    Port 8095 Engine
    1 Quelle
    The State of Ransomware: August 2026
    1 Quelle
    Applying Zero Trust Principles to Agents - Kieran Human - ASW #397
    1 Quelle
    Connecting Cyber Risks to Board Outcomes & BHUSA interviews from Mimecast & Zscaler - Leslie Nielsen, Brett Stone-Gross, Dan Bowden - BSW #462
    Ähnliche Beiträge
    🔍 Verwandte News

    Auch interessante Nachrichten Preparing for Migration: Decoupling Your Function Logic

    Thematisch verwandte Begriffe: Preparing, Migration, Decoupling, Your · 6 Treffer

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...