<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      C# Web開發教程(十一)后臺主動服務

      托管服務(HostedService,也稱為"后臺服務")

      • 托管服務,這個翻譯是不準確的,我覺得應該翻譯成主動服務:服務器自己主動發起的服務(任務)[相對于客戶端發起請求,服務端才響應]

        • 它是一種在應用啟動后自動運行、無需外部觸發的服務
      • 使用場景

      - 代碼運行在后臺,比如服務器啟動的時候在后臺預先加載數據到緩存,每天凌晨3點把數據導出到備份數據庫,或者每隔5秒鐘在兩張表之間同步一次數據。
      
      - 定時任務
      
      • 代碼實現流程
      - 實現IHostedService接口(用起來麻煩),一般編寫從BackgroundService繼承的類(用起來簡單).
      - 注冊服務: services.AddHostedService<DemoBgService>();
      
      • 注意事項
      - 一旦托管服務的代碼有錯,整個項目就無法啟動
      	- 可以把HostOptions.BackgroundServiceExceptionBehavior設置為Ignore,程序會忽略異常,而不是停止程序.
      	- 不過不推薦這么搞,因為“異常應該被妥善的處理,而不是被忽略”.
      	- 要在ExecuteAsync方法中把代碼用try....catch包裹起來,當發生異常的時候,記錄日志中或發警報等.
      	
      - 而在.net6.0之前的版本中,托管代碼就算有異常,項目也可以啟動起來(舊的設計其實不好,所以新的版本修復了)
      
      // HostedServiceDemo1.cs
      
      namespace WebApplicationAboutJWTConfigRun
      {
          public class HostedServiceDemo1 : BackgroundService
          {
              protected override async Task ExecuteAsync(CancellationToken stoppingToken)
              {
                  Console.WriteLine("HostedService1啟動");
                  await Task.Delay(3000);
                  string txt = await File.ReadAllTextAsync("d:/text.txt");
                  Console.WriteLine("文件讀取中...");
                  await Task.Delay(10000);
                  Console.WriteLine(txt);
                  Console.WriteLine("HostedService1啟動任務結束!");
      
              }
          }
      }
      
      // Program.cs
      ......
      builder.Services.AddSwaggerGen(c =>
      {
         ......
      });
      // 主動服務(注冊)
      builder.Services.AddHostedService<HostedServiceDemo1>();
      
      • 故意觸發異常的效果
      namespace WebApplicationAboutJWTConfigRun
      {
          public class HostedServiceDemo1 : BackgroundService
          {
              protected override async Task ExecuteAsync(CancellationToken stoppingToken)
              {
      
                  try
                  {
                      Console.WriteLine("HostedService1啟動");
                      await Task.Delay(3000);
                      string txt = await File.ReadAllTextAsync("d:/text.ext"); // 故意寫錯
                      Console.WriteLine("文件讀取中...");
                      await Task.Delay(10000);
                      Console.WriteLine(txt);
                      Console.WriteLine("HostedService1啟動任務結束!");
                  }
                  catch(Exception Ex)
                  {
                      Console.WriteLine("啟動代碼異常" + Ex);
                  }
              }
          }
      }
      
      - 測試效果: 項目正常跑起來了,日志記錄異常
      ......
      啟動代碼異常System.IO.FileNotFoundException: Could not find file 'd:\text.ext'.
      File name: 'd:\text.ext'
      ......
      
      • 托管服務中使用DI(依賴注入(DI)限制)
      - 托管服務是以單例的生命周期注冊到依賴注入容器中的。因此不能注入生命周期為范圍或者瞬態的服務(不能直接注入 Scoped 或 Transient 服務(如 DbContext))。
        比如注入FCore的上下文的話,程序就會拋出異常。
          
      - 可以通過構造方法注入一個IServiceScopeFactory服務,它可以用來創建一個IServiceScope對象,這樣我們
        就可以通過IServiceScope來創建短生命周期的服務了(記得在Dispose中釋放IServiceScope)。
      
      
      
      • 異常實例演示
      // HostedServiceDemo2.cs
      
      namespace WebApplicationAboutJWTConfigRun
      {
          public class HostedServiceDemo2
          {
              public int Add(int a,int b)
              {
                  return a + b;
              }
                  
          }
      }
      
      // Program.cs
      ......
      // 主動服務
      builder.Services.AddHostedService<HostedServiceDemo1>();
      // 注入生命周期為范圍或者瞬態的服務
      builder.Services.AddScoped<HostedServiceDemo2>();
      
      // HostedServiceDemo1.cs 測試
      namespace WebApplicationAboutJWTConfigRun
      {
          public class HostedServiceDemo1 : BackgroundService
          {
              private readonly HostedServiceDemo2 hostedServiceDemo2;
      		
      		// 依賴注入
              public HostedServiceDemo1(HostedServiceDemo2 hostedServiceDemo2)
              {
                  this.hostedServiceDemo2 = hostedServiceDemo2;
              }
      
              protected override async Task ExecuteAsync(CancellationToken stoppingToken)
              {
      
                  try
                  {
                      Console.WriteLine("HostedService1啟動");
                      // 執行邏輯,這里會觸發異常
                      Console.WriteLine("執行HostedService2服務"+hostedServiceDemo2.Add(1,1));
                      ......
                  }
                  catch(Exception Ex)
                  {
                      Console.WriteLine("啟動代碼異常" + Ex);
                  }
                  
      
              }
          }
      }
      
      
      
      
      • 現在,修復上面的異常
      namespace WebApplicationAboutJWTConfigRun
      {
          public class HostedServiceDemo1 : BackgroundService
          {
              //private readonly HostedServiceDemo2 hostedServiceDemo2;
      
              //public HostedServiceDemo1(HostedServiceDemo2 hostedServiceDemo2)
              //{
              //    this.hostedServiceDemo2 = hostedServiceDemo2;
              //}
      		
      		// 聲明 IServiceScope
              private IServiceScope serviceScope;
      		
      		// 依賴注入
              public HostedServiceDemo1(IServiceScopeFactory serviceScopeFactory)
              {
                  this.serviceScope = serviceScopeFactory.CreateScope();
              }
      
              protected override async Task ExecuteAsync(CancellationToken stoppingToken)
              {
      
                  try
                  {
                      var testService = serviceScope.ServiceProvider.GetRequiredService<HostedServiceDemo2>();
                      Console.WriteLine("HostedService1啟動");
                      Console.WriteLine("執行HostedService2服務"+ testService.Add(1,1));
                      await Task.Delay(3000);
                      string txt = await File.ReadAllTextAsync("d:/text.txt");
                      Console.WriteLine("文件讀取中...");
                      await Task.Delay(10000);
                      Console.WriteLine(txt);
                      Console.WriteLine("HostedService1啟動任務結束!");
                  }
                  catch(Exception Ex)
                  {
                      Console.WriteLine("啟動代碼異常" + Ex);
                  }
                  
      
              }
      
              public override void Dispose()
              {
                  this.serviceScope.Dispose();
                  base.Dispose();
              }
          }
      }
      
      

      ? 總結要點

      要點 說明
      用途 后臺任務、定時任務、數據同步等
      實現方式 繼承 BackgroundService
      注冊方式 AddHostedService()
      異常處理 必須用 try-catch 包裹
      DI 限制 不能直接注入 Scoped/Transient 服務
      解決方案 使用 IServiceScopeFactory 創建作用域

      連接數據庫示例: 使用托管服務(BackgroundService)實現的定時數據導出任務

      - 安裝工具包
      
      <Project Sdk="Microsoft.NET.Sdk.Web">
      
      ......
      
        <ItemGroup>
          <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.0" />
          <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
      
      	<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.0" />
      	<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
      	<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.0" />
      	<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0">
      		<PrivateAssets>all</PrivateAssets>
      		<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      	</PackageReference>
      	  
        </ItemGroup>
      
      </Project>
      
      
      
      
      // MyUser.cs
      
      using Microsoft.AspNetCore.Identity;
      
      namespace WebApplicationAboutJWTConfigRun
      {
          public class MyUser:IdentityUser<long>
          {
              public string? WeiXinAccount { get; set; }
          }
      }
      
      // MyRole.cs
      
      using Microsoft.AspNetCore.Identity;
      
      namespace WebApplicationAboutJWTConfigRun
      {
          public class MyRole:IdentityRole<long>
          {
          }
      }
      
      // MyDbContext.cs
      
      using Microsoft.AspNetCore.Identity;
      using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
      using Microsoft.EntityFrameworkCore;
      
      namespace WebApplicationAboutJWTConfigRun
      {
          public class MyDbContext : IdentityDbContext<MyUser,MyRole,long>
          {
              public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
              {
              }
          }
      }
      
      // Program.cs
      ......
      builder.Services.AddDbContext<MyDbContext>(opt =>
      {
          opt.UseSqlServer("Server=.;Database=idtest2;Trusted_Connection=True;");
      });
      
      - 作遷移和更新db
      
      // ScheduledService.cs
      
      using Microsoft.EntityFrameworkCore;
      
      namespace WebApplicationAboutJWTConfigRun
      {
          public class ScheduledService : BackgroundService
          {
          	// 解決單例服務無法直接使用Scoped服務的問題
              private readonly IServiceScope serviceScope;
      
              public ScheduledService(IServiceScopeFactory serviceScopeFactory)
              {
                  // 創建獨立的作用域來獲取DbContext
                  this.serviceScope = serviceScopeFactory.CreateScope();
              }
      
      
              protected override async Task ExecuteAsync(CancellationToken stoppingToken)
              {
                  try
                  {
                      var dbCtx = serviceScope.ServiceProvider.GetRequiredService<MyDbContext>();
                      // 檢查取消令牌:stoppingToken.IsCancellationRequested - 應用關閉時自動停止
                      while (!stoppingToken.IsCancellationRequested)
                      {
                          long c = await dbCtx.Users.LongCountAsync();
                          await File.WriteAllTextAsync("d:/text.txt",c.ToString());
                          await Task.Delay(5000);
                      }
                      
                      Console.WriteLine("導出成功" + DateTime.Now);
                  }
                  catch (Exception ex)
                  {
                      Console.WriteLine($"出錯了: {ex.Message}, 堆棧跟蹤: {ex.StackTrace}");
                  }
              }
      
              public override void Dispose()
              {
              	// 手動釋放作用域:避免內存泄漏
                  this.serviceScope.Dispose();
                  // 調用基類Dispose:確保BackgroundService正確清理
                  base.Dispose();
              }
          }
      }
      
      // Program.cs
      ......
      // 主動服務
      builder.Services.AddHostedService<ScheduledService>();
      builder.Services.AddDbContext<MyDbContext>(opt =>
      {
          ......  
      }
      
      
      
      posted @ 2025-11-03 11:55  清安寧  閱讀(7)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲色大成网站www久久九| 国产二区三区不卡免费| 极品人妻少妇一区二区三区| 天天澡日日澡狠狠欧美老妇| 欧美国产精品啪啪| 高清有码国产一区二区| 国产精品无码dvd在线观看| 国产精品美女www爽爽爽视频 | 久久人妻公开中文字幕| 亚洲AV日韩AV激情亚洲| 激情综合网激情激情五月天 | 亚洲日韩精品一区二区三区| 亚洲av与日韩av在线| 九九热免费精品视频在线| 国产成人一区二区三区在线| 国产又黄又爽又不遮挡视频| 高清无码18| 久久九九精品国产免费看小说| 中文字幕精品亚洲无线码二区| 精品国产粉嫩一区二区三区| 国产成人精品无码播放| 日产精品久久久久久久| 天啦噜国产精品亚洲精品| 日韩乱码视频一区二区三区| 国产av一区二区三区| 99网友自拍视频在线| 日本A级视频在线播放| 午夜成人性爽爽免费视频| 亚洲欧美精品一中文字幕| 国产成人欧美一区二区三区在线| 国产成人精品无码播放| 欧美人与zoxxxx另类| 欧美黑人大战白嫩在线| 99中文字幕国产精品| 夜夜添狠狠添高潮出水| 国产又黄又湿又刺激网站| 枣强县| 蜜臀av一区二区国产精品| 91热在线精品国产一区| 亚洲精品国产自在现线最新| 国产av无码国产av毛片|