Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Membuat Layanan Windows menggunakan
Pengembang .NET Framework mungkin terbiasa dengan aplikasi Windows Service. Sebelum .NET Core dan .NET 5+, pengembang yang mengandalkan .NET Framework dapat membuat Layanan Windows untuk melakukan tugas latar belakang atau menjalankan proses yang berjalan lama. Fungsionalitas ini masih tersedia dan Anda dapat membuat Layanan Pekerja yang berjalan sebagai Layanan Windows.
Dalam tutorial ini, Anda akan mempelajari cara:
- Publikasikan aplikasi pekerja .NET sebagai sebuah file tunggal yang dapat dijalankan.
- Membuat Layanan Windows.
- Buat aplikasi
BackgroundService
sebagai Layanan Windows. - Mulai dan hentikan Layanan Windows.
- Lihat log peristiwa.
- Hapus Layanan Windows.
Tips
Semua kode sumber contoh "Pekerja di .NET" tersedia di Browser Sampel untuk diunduh. Untuk informasi selengkapnya, lihat Menelusuri sampel kode: Pekerja di .NET.
Penting
Menginstal .NET SDK juga menginstal Microsoft.NET.Sdk.Worker
dan templat kerja. Dengan kata lain, setelah menginstal .NET SDK, Anda dapat membuat pekerja baru dengan menggunakan perintah dotnet new worker. Jika Anda menggunakan Visual Studio, templat disembunyikan hingga ASP.NET opsional dan beban kerja pengembangan web diinstal.
Prasyarat
- .NET 8.0 SDK atau yang lebih baru
- Sistem Operasi Windows
- Lingkungan pengembangan terintegrasi .NET (IDE)
- Jangan ragu untuk menggunakan Visual Studio
Membuat proyek baru
Untuk membuat proyek Layanan Pekerja baru dengan Visual Studio, Anda akan memilih File>Baru>Proyek.... Dari dialog Buat proyek baru mencari "Layanan Pekerja", dan pilih templat "Layanan Pekerja". Jika Anda lebih suka menggunakan .NET CLI, buka terminal favorit Anda di direktori kerja. Jalankan perintah dotnet new
, dan ganti <Project.Name>
dengan nama proyek yang Anda inginkan.
dotnet new worker --name <Project.Name>
Untuk informasi selengkapnya tentang perintah proyek layanan pekerja baru .NET CLI, lihat dotnet new worker.
Tips
Jika Anda menggunakan Visual Studio Code, Anda dapat menjalankan perintah .NET CLI dari terminal terintegrasi. Untuk informasi selengkapnya, lihat Visual Studio Code: Terminal Terintegrasi.
Menginstal paket NuGet
Untuk berinteraksi dengan layanan Windows bawaan dari implementasi .NET IHostedService, Anda harus menginstal paket NuGet Microsoft.Extensions.Hosting.WindowsServices
.
Untuk menginstal ini dari Visual Studio, gunakan dialog Kelola Paket NuGet . Cari "Microsoft.Extensions.Hosting.WindowsServices", dan instal. Jika Anda lebih suka menggunakan .NET CLI, jalankan perintah berikut. (Jika Anda menggunakan versi SDK .NET 9 atau yang lebih lama, gunakan formulir sebagai gantinya dotnet add package
.)
dotnet package add Microsoft.Extensions.Hosting.WindowsServices
Untuk informasi selengkapnya, lihat penambahan paket dotnet.
Setelah berhasil menambahkan paket, file proyek Anda sekarang harus berisi referensi paket berikut:
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.6" />
</ItemGroup>
Memperbarui file proyek
Proyek pekerja ini menggunakan jenis referensi yang dapat bernilai null dari C#. Untuk mengaktifkannya untuk seluruh proyek, perbarui file proyek yang sesuai:
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>App.WindowsService</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.6" />
</ItemGroup>
</Project>
Perubahan file proyek sebelumnya menambahkan node <Nullable>enable<Nullable>
. Untuk informasi selengkapnya, lihat Mengatur konteks nullable.
Membuat layanan
Tambahkan kelas baru ke proyek bernama JokeService.cs, dan ganti kontennya dengan kode C# berikut:
namespace App.WindowsService;
public sealed class JokeService
{
public string GetJoke()
{
Joke joke = _jokes.ElementAt(
Random.Shared.Next(_jokes.Count));
return $"{joke.Setup}{Environment.NewLine}{joke.Punchline}";
}
// Programming jokes borrowed from:
// https://github.com/eklavyadev/karljoke/blob/main/source/jokes.json
private readonly HashSet<Joke> _jokes = new()
{
new Joke("What's the best thing about a Boolean?", "Even if you're wrong, you're only off by a bit."),
new Joke("What's the object-oriented way to become wealthy?", "Inheritance"),
new Joke("Why did the programmer quit their job?", "Because they didn't get arrays."),
new Joke("Why do programmers always mix up Halloween and Christmas?", "Because Oct 31 == Dec 25"),
new Joke("How many programmers does it take to change a lightbulb?", "None that's a hardware problem"),
new Joke("If you put a million monkeys at a million keyboards, one of them will eventually write a Java program", "the rest of them will write Perl"),
new Joke("['hip', 'hip']", "(hip hip array)"),
new Joke("To understand what recursion is...", "You must first understand what recursion is"),
new Joke("There are 10 types of people in this world...", "Those who understand binary and those who don't"),
new Joke("Which song would an exception sing?", "Can't catch me - Avicii"),
new Joke("Why do Java programmers wear glasses?", "Because they don't C#"),
new Joke("How do you check if a webpage is HTML5?", "Try it out on Internet Explorer"),
new Joke("A user interface is like a joke.", "If you have to explain it then it is not that good."),
new Joke("I was gonna tell you a joke about UDP...", "...but you might not get it."),
new Joke("The punchline often arrives before the set-up.", "Do you know the problem with UDP jokes?"),
new Joke("Why do C# and Java developers keep breaking their keyboards?", "Because they use a strongly typed language."),
new Joke("Knock-knock.", "A race condition. Who is there?"),
new Joke("What's the best part about TCP jokes?", "I get to keep telling them until you get them."),
new Joke("A programmer puts two glasses on their bedside table before going to sleep.", "A full one, in case they gets thirsty, and an empty one, in case they don’t."),
new Joke("There are 10 kinds of people in this world.", "Those who understand binary, those who don't, and those who weren't expecting a base 3 joke."),
new Joke("What did the router say to the doctor?", "It hurts when IP."),
new Joke("An IPv6 packet is walking out of the house.", "He goes nowhere."),
new Joke("3 SQL statements walk into a NoSQL bar. Soon, they walk out", "They couldn't find a table.")
};
}
readonly record struct Joke(string Setup, string Punchline);
Kode sumber layanan humor sebelumnya mengekspos satu fungsi tunggal, metode GetJoke
. Ini adalah metode pengembalian string
yang mewakili lelucon pemrograman acak. Bidang _jokes
berskala kelas digunakan untuk menyimpan daftar cerita lucu. Lelucon acak dipilih dari daftar dan dikembalikan.
Menulis ulang kelas Worker
Ganti Worker
yang ada dari templat dengan kode C# berikut, dan ganti nama file menjadi WindowsBackgroundService.cs:
namespace App.WindowsService;
public sealed class WindowsBackgroundService(
JokeService jokeService,
ILogger<WindowsBackgroundService> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
while (!stoppingToken.IsCancellationRequested)
{
string joke = jokeService.GetJoke();
logger.LogWarning("{Joke}", joke);
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
catch (OperationCanceledException)
{
// When the stopping token is canceled, for example, a call made from services.msc,
// we shouldn't exit with a non-zero exit code. In other words, this is expected...
}
catch (Exception ex)
{
logger.LogError(ex, "{Message}", ex.Message);
// Terminates this process and returns an exit code to the operating system.
// This is required to avoid the 'BackgroundServiceExceptionBehavior', which
// performs one of two scenarios:
// 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
// 2. When set to "StopHost": will cleanly stop the host, and log errors.
//
// In order for the Windows Service Management system to leverage configured
// recovery options, we need to terminate the process with a non-zero exit code.
Environment.Exit(1);
}
}
}
Dalam kode sebelumnya, JokeService
disuntikkan bersama dengan ILogger
. Keduanya tersedia untuk kelas sebagai variabel. Dalam metode ExecuteAsync
, layanan lelucon meminta lelucon dan menuliskannya kepada pencatat. Dalam hal ini, logger diimplementasikan menggunakan Log Peristiwa Windows - Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider. Log ditulis ke dan tersedia untuk dilihat diEvent Viewer.
Nota
Secara bawaan, tingkat keparahan Log Peristiwa adalah Warning. Ini dapat dikonfigurasi, namun untuk tujuan demonstrasi, WindowsBackgroundService
mencatat menggunakan metode ekstensi LogWarning. Untuk menargetkan tingkat EventLog
secara khusus, tambahkan entri di appsetting . {Environment}.json, atau berikan nilai EventLogSettings.Filter.
{
"Logging": {
"LogLevel": {
"Default": "Warning"
},
"EventLog": {
"SourceName": "The Joke Service",
"LogName": "Application",
"LogLevel": {
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
}
Untuk informasi selengkapnya tentang mengonfigurasi tingkat log, lihat penyedia pengelogan di .NET: Mengonfigurasi Windows EventLog.
Menulis ulang kelas Program
Ganti templat Program.cs konten file dengan kode C# berikut:
using App.WindowsService;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Logging.EventLog;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddWindowsService(options =>
{
options.ServiceName = ".NET Joke Service";
});
LoggerProviderOptions.RegisterProviderOptions<
EventLogSettings, EventLogLoggerProvider>(builder.Services);
builder.Services.AddSingleton<JokeService>();
builder.Services.AddHostedService<WindowsBackgroundService>();
IHost host = builder.Build();
host.Run();
Metode ekstensi AddWindowsService
mengonfigurasi aplikasi agar berfungsi sebagai Layanan Windows. Nama layanan diatur ke ".NET Joke Service"
. Layanan yang dihosting terdaftar untuk penerapan dependensi.
Untuk informasi selengkapnya tentang mendaftarkan layanan, lihat Injeksi Dependensi di .NET.
Menerbitkan aplikasi
Untuk membuat aplikasi .NET Worker Service sebagai Layanan Windows, disarankan agar Anda menerbitkan aplikasi sebagai file tunggal yang dapat dieksekusi. Lebih aman dari kesalahan untuk memiliki file eksekusi mandiri, karena tidak ada file pendukung yang tersebar di sistem file. Tetapi Anda dapat memilih modalitas penerbitan yang berbeda, yang dapat diterima dengan sempurna, selama Anda membuat file *.exe yang dapat ditargetkan oleh Pengelola Kontrol Layanan Windows.
Penting
Pendekatan penerbitan alternatif adalah membangun *.dll (bukan *.exe), dan ketika Anda menginstal aplikasi yang telah diterbitkan menggunakan Windows Service Control Manager, Anda mendelegasikan proses ke .NET CLI dan meneruskan DLL-nya. Untuk informasi selengkapnya, lihat .NET CLI: perintah dotnet.
sc.exe create ".NET Joke Service" binpath= "C:\Path\To\dotnet.exe C:\Path\To\App.WindowsService.dll"
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>App.WindowsService</RootNamespace>
<OutputType>exe</OutputType>
<PublishSingleFile Condition="'$(Configuration)' == 'Release'">true</PublishSingleFile>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.6" />
</ItemGroup>
</Project>
Baris sebelumnya yang disorot dari file proyek menentukan perilaku berikut:
-
<OutputType>exe</OutputType>
: Membuat aplikasi konsol. -
<PublishSingleFile Condition="'$(Configuration)' == 'Release'">true</PublishSingleFile>
: Mengaktifkan penerbitan file tunggal. -
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
: Menentukan RID dariwin-x64
. -
<PlatformTarget>x64</PlatformTarget>
: Tentukan platform target CPU 64-bit.
Untuk menerbitkan aplikasi dari Visual Studio, Anda dapat membuat profil penerbitan yang dipertahankan. Profil penerbitan berbasis XML dan memiliki ekstensi file .pubxml. Visual Studio menggunakan profil ini untuk menerbitkan aplikasi secara implisit, sedangkan jika Anda menggunakan .NET CLI, Anda harus secara eksplisit menentukan profil penerbitan untuk digunakan.
Klik kanan pada proyek di Penjelajah Solusi, dan pilih Terbitkan. Kemudian, pilih Tambah profil publikasi untuk membuat profil. Dari dialog Terbitkan, pilih Folder sebagai TargetAnda.
Biarkan default lokasi , lalu pilih Selesai. Setelah profil dibuat, pilih Tampilkan semua pengaturan, dan verifikasi pengaturan Profil Anda.
Pastikan bahwa pengaturan berikut ditentukan:
- Mode penyebaran: Mandiri
- Hasilkan file tunggal: dicentang
- Aktifkan kompilasi ReadyToRun: diperiksa
- Pangkas rakitan yang tidak digunakan (dalam pratinjau): tidak dicentang
Terakhir, pilih Terbitkan. Aplikasi ini dikompilasi, dan file .exe yang dihasilkan diterbitkan ke direktori output /publish.
Atau, Anda dapat menggunakan .NET CLI untuk menerbitkan aplikasi:
dotnet publish --output "C:\custom\publish\directory"
Untuk informasi selengkapnya, lihat dotnet publish
.
Penting
Dengan .NET 6, jika Anda mencoba men-debug aplikasi dengan pengaturan <PublishSingleFile>true</PublishSingleFile>
, Anda tidak akan dapat men-debug aplikasi. Untuk informasi selengkapnya, lihat Tidak dapat mengaitkan dengan CoreCLR ketika melakukan debug pada aplikasi .NET 6 'PublishSingleFile'.
Membuat Layanan Windows
Jika Anda tidak terbiasa menggunakan PowerShell dan Anda lebih suka membuat alat penginstal untuk layanan Anda, lihat Membuat penginstal Layanan Windows. Jika tidak, gunakanlah perintah buatlah dari Windows Service Control Manager bawaan (sc.exe) untuk membuat Layanan Windows. Jalankan PowerShell sebagai Administrator.
sc.exe create ".NET Joke Service" binpath= "C:\Path\To\App.WindowsService.exe"
Tips
Jika Anda perlu mengubah akar konten konfigurasi host , Anda dapat meneruskannya sebagai argumen baris perintah saat menentukan binpath
:
sc.exe create "Svc Name" binpath= "C:\Path\To\App.exe --contentRoot C:\Other\Path"
Anda akan melihat pesan output:
[SC] CreateService SUCCESS
Untuk informasi selengkapnya, lihat sc.exe membuat.
Mengonfigurasi Layanan Windows
Setelah layanan dibuat, Anda dapat secara opsional mengonfigurasinya. Jika Anda baik-baik saja dengan pengaturan bawaan layanan, lewati ke bagian Memverifikasi fungsionalitas layanan.
Layanan Windows menyediakan opsi konfigurasi pemulihan. Anda dapat mengkueri konfigurasi saat ini menggunakan perintah sc.exe qfailure "<Service Name>"
(di mana <Service Name>
adalah nama layanan Anda) untuk membaca nilai konfigurasi pemulihan saat ini:
sc qfailure ".NET Joke Service"
[SC] QueryServiceConfig2 SUCCESS
SERVICE_NAME: .NET Joke Service
RESET_PERIOD (in seconds) : 0
REBOOT_MESSAGE :
COMMAND_LINE :
Perintah akan menghasilkan konfigurasi pemulihan, yang merupakan nilai default—karena belum dikonfigurasi.
Untuk mengonfigurasi pemulihan, gunakan sc.exe failure "<Service Name>"
di mana <Service Name>
adalah nama layanan Anda:
sc.exe failure ".NET Joke Service" reset= 0 actions= restart/60000/restart/60000/run/1000
[SC] ChangeServiceConfig2 SUCCESS
Tips
Untuk mengonfigurasi opsi pemulihan, sesi terminal Anda perlu dijalankan sebagai Administrator.
Setelah berhasil dikonfigurasi, Anda bisa mengkueri nilai sekali lagi menggunakan perintah sc.exe qfailure "<Service Name>"
:
sc qfailure ".NET Joke Service"
[SC] QueryServiceConfig2 SUCCESS
SERVICE_NAME: .NET Joke Service
RESET_PERIOD (in seconds) : 0
REBOOT_MESSAGE :
COMMAND_LINE :
FAILURE_ACTIONS : RESTART -- Delay = 60000 milliseconds.
RESTART -- Delay = 60000 milliseconds.
RUN PROCESS -- Delay = 1000 milliseconds.
Anda akan melihat nilai hidupkan ulang yang dikonfigurasi.
Opsi pemulihan layanan dan instans .NET BackgroundService
Dengan .NET 6, perilaku penanganan pengecualian hosting baru telah ditambahkan ke .NET. Enum BackgroundServiceExceptionBehavior ditambahkan ke namespace Microsoft.Extensions.Hosting
, dan digunakan untuk menentukan perilaku layanan saat pengecualian dilemparkan. Tabel berikut ini mencantumkan opsi yang tersedia:
Pilihan | Deskripsi |
---|---|
Ignore | Abaikan pengecualian yang dilemparkan dalam BackgroundService . |
StopHost |
IHost akan dihentikan ketika terjadi pengecualian yang tidak tertangani. |
Perilaku bawaan sebelum .NET 6 adalah Ignore
, yang mengakibatkan proses zombie (proses berjalan yang tidak melakukan apa pun). Dengan .NET 6, perilaku default adalah StopHost
, yang menyebabkan host dihentikan ketika pengecualian dilemparkan. Tetapi berhenti dengan sempurna, yang berarti bahwa sistem pengelolaan layanan Windows tidak akan memulai ulang layanan. Untuk mengizinkan layanan dimulai ulang dengan benar, Anda dapat memanggil Environment.Exit dengan kode keluar bukan nol. Pertimbangkan blok catch
yang disorot berikut:
namespace App.WindowsService;
public sealed class WindowsBackgroundService(
JokeService jokeService,
ILogger<WindowsBackgroundService> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
while (!stoppingToken.IsCancellationRequested)
{
string joke = jokeService.GetJoke();
logger.LogWarning("{Joke}", joke);
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
catch (OperationCanceledException)
{
// When the stopping token is canceled, for example, a call made from services.msc,
// we shouldn't exit with a non-zero exit code. In other words, this is expected...
}
catch (Exception ex)
{
logger.LogError(ex, "{Message}", ex.Message);
// Terminates this process and returns an exit code to the operating system.
// This is required to avoid the 'BackgroundServiceExceptionBehavior', which
// performs one of two scenarios:
// 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
// 2. When set to "StopHost": will cleanly stop the host, and log errors.
//
// In order for the Windows Service Management system to leverage configured
// recovery options, we need to terminate the process with a non-zero exit code.
Environment.Exit(1);
}
}
}
Memverifikasi fungsionalitas layanan
Untuk melihat aplikasi yang dibuat sebagai Layanan Windows, buka layanan . Pilih tombol Windows (atau Ctrl + Esc), dan cari dari "Layanan". Dari aplikasi Layanan, Anda harus dapat menemukan layanan Anda berdasarkan namanya.
Penting
Secara default, pengguna reguler (non-admin) tidak dapat mengelola layanan Windows. Untuk memverifikasi bahwa aplikasi ini berfungsi seperti yang diharapkan, Anda harus menggunakan akun Admin.
Untuk memverifikasi bahwa layanan berfungsi seperti yang diharapkan, Anda perlu:
- Memulai layanan
- Lihat log
- Menghentikan layanan
Penting
Untuk men-debug aplikasi, pastikan Anda tidak mencoba men-debug executable yang berjalan secara aktif dalam proses Layanan Windows.
Memulai Layanan Windows
Untuk memulai Layanan Windows, gunakan perintah sc.exe start
:
sc.exe start ".NET Joke Service"
Anda akan melihat output yang mirip dengan yang berikut ini:
SERVICE_NAME: .NET Joke Service
TYPE : 10 WIN32_OWN_PROCESS
STATE : 2 START_PENDING
(NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x7d0
PID : 37636
FLAGS
Status layanan akan beralih dari START_PENDING
ke Menjalankan.
Lihat log
Untuk melihat log, buka Event Viewer. Pilih tombol Windows (atau Ctrl + Esc), dan cari "Event Viewer"
. Pilih Penampil Peristiwa (Lokal)>Log Windows>simpul Aplikasi. Anda akan melihat entri tingkat Peringatan dengan Sumber yang cocok dengan namespace aplikasi. Klik dua kali entri, atau klik kanan dan pilih Properti Peristiwa untuk melihat detailnya.
Setelah melihat log di Log Peristiwa , Anda harus menghentikan layanan. Ini dirancang untuk mencatat lelucon acak sekali per menit. Ini adalah perilaku yang disengaja tetapi tidak praktis untuk layanan produksi.
Hentikan Layanan Windows
Untuk menghentikan Layanan Windows, gunakan perintah sc.exe stop
:
sc.exe stop ".NET Joke Service"
Anda akan melihat output yang mirip dengan yang berikut ini:
SERVICE_NAME: .NET Joke Service
TYPE : 10 WIN32_OWN_PROCESS
STATE : 3 STOP_PENDING
(STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
Status layanan akan beralih dari STOP_PENDING
ke Dihentikan.
Menghapus Layanan Windows
Untuk menghapus Layanan Windows, gunakan perintah penghapusan Windows Service Control Manager (sc.exe) asli. Jalankan PowerShell sebagai Administrator.
Penting
Jika layanan tidak dalam status Dihentikan, layanan tidak akan segera dihapus. Pastikan bahwa layanan dihentikan sebelum mengeluarkan perintah hapus.
sc.exe delete ".NET Joke Service"
Anda akan melihat pesan output:
[SC] DeleteService SUCCESS
Untuk informasi selengkapnya, lihat sc.exe menghapus.
Lihat juga
- Buat penginstal Layanan Windows
- Layanan Pekerja di .NET
- Membuat Layanan Antrian
-
Gunakan layanan terlingkup dalam
BackgroundService
-
Menerapkan antarmuka
IHostedService