NOTE: Apart from
(and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.
Friday, 13 September 2024
//Less than a minute
هذه مقاله صغيره غبيه لأنني كنت مرتبكه قليلاً حول كيفية ضمان IHostedService
في حالة واحدة. ظننت أن الأمر أكثر تعقيداً مما هو عليه في الواقع لذا فكرت أن أكتب مقالاً صغيراً عن ذلك. فقط في حالة أي شخص آخر كان مرتبك حول ذلك.
في الـ قبل سنة، قمنا بتغطية كيفية إنشاء خدمة خلفية IHostedService
واجهة لـ إرسال بريد إلكتروني. ستغطي هذه المقالة كيفية ضمان IHostedService
هو مثال واحد.
قد يكون هذا واضحا للبعض، لكنه ليس للآخرين (ولم يكن مباشرة بالنسبة لي!)ع(
[رابعاً -
حسناً، إنها قضية مثل معظم المواد خارج هذه تغطي كيفية استخدام IHostedService
لكنها لا تغطي كيفية ضمان أن الخدمة هي حالة واحدة. هذا مهم كما أنك لا تريد حالات متعددة من الخدمة تعمل في نفس الوقت.
ما الذي أعنيه؟ حسناً في ASP.net الطريق إلى تسجيل خدمة IHOstedService أو خدمة تشغيل خدمة (أو أساساً نفس مع المزيد من التجاوزات لإدارة دورة دورة الحياة) يمكنك استخدام هذا
services.AddHostedService(EmailSenderHostedService);
ما يقوم به هو نداءات في هذا الرمز الخلفي:
public static IServiceCollection AddHostedService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THostedService>(this IServiceCollection services)
where THostedService : class, IHostedService
{
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, THostedService>());
return services;
}
و الذي هو جيد و رائع لكن ماذا لو أردت أن تنشر رسالة جديدة مباشرة إلى هذه الخدمة Controller
(أ) هل يمكن أن يكون هذا الفعل فعلاً أم فعلاً؟
public class ContactController(EmailSenderHostedService sender,ILogger<BaseController> logger) ...
{
[HttpPost]
[Route("submit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Submit([Bind(Prefix = "")] ContactViewModel comment)
{
ViewBag.Title = "Contact";
//Only allow HTMX requests
if(!Request.IsHtmx())
{
return RedirectToAction("Index", "Contact");
}
if (!ModelState.IsValid)
{
return PartialView("_ContactForm", comment);
}
var commentHtml = commentService.ProcessComment(comment.Comment);
var contactModel = new ContactEmailModel()
{
SenderEmail = string.IsNullOrEmpty(comment.Email) ? "Anonymous" : comment.Email,
SenderName = string.IsNullOrEmpty(comment.Name) ? "Anonymous" : comment.Name,
Comment = commentHtml,
};
await sender.SendEmailAsync(contactModel);
return PartialView("_Response",
new ContactViewModel() { Email = comment.Email, Name = comment.Name, Comment = commentHtml });
return RedirectToAction("Index", "Home");
}
}
لا تحتاج إلى إنشاء واجهة تقوم هي نفسها بتنفيذ IHostedService
ثم اتصل بالطريق على ذلك أو عليك أن تتأكد من أن الخدمة هي حالة واحدة. وهذه هي الطريقة الأسهل للقيام بذلك (على الرغم من أنه قد يكون من الأفضل الاعتماد على سيناريوك لاختبار طريقة الواجهة).
أنت سَتُلاحظُ هنا بأنّه يُسجّلُ الخدمة كa IHostedService
هذا يتعلق بإدارة دورة حياة هذه الخدمة حيث أن إطار ASP.NET سوف يستخدم هذا التسجيل لتحريض أحداث هذه الخدمة (ASP.NET)StartAsync
وقد عقد مؤتمراً بشأن StopAsync
(الخدمات المخصصة) (الخدمات المخصصة) انظر أدناه، IHostedlifeCycleService
هو مجرد نسخة أكثر تفصيلاً من الخدمة المُخصصة.
/// <summary>
/// Defines methods for objects that are managed by the host.
/// </summary>
public interface IHostedService
{
/// <summary>
/// Triggered when the application host is ready to start the service.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous Start operation.</returns>
Task StartAsync(CancellationToken cancellationToken);
/// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// </summary>
/// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous Stop operation.</returns>
Task StopAsync(CancellationToken cancellationToken);
}
namespace Microsoft.Extensions.Hosting
{
/// <summary>
/// Defines methods that are run before or after
/// <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StartAsync(System.Threading.CancellationToken)" /> and
/// <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)" />.
/// </summary>
public interface IHostedLifecycleService : IHostedService
{
/// <summary>
/// Triggered before <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StartAsync(System.Threading.CancellationToken)" />.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
Task StartingAsync(CancellationToken cancellationToken);
/// <summary>
/// Triggered after <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StartAsync(System.Threading.CancellationToken)" />.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
Task StartedAsync(CancellationToken cancellationToken);
/// <summary>
/// Triggered before <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)" />.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
Task StoppingAsync(CancellationToken cancellationToken);
/// <summary>
/// Triggered after <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)" />.
/// </summary>
/// <param name="cancellationToken">Indicates that the stop process has been aborted.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
Task StoppedAsync(CancellationToken cancellationToken);
}
}
منهج الواجهة قد يكون أسهل اعتماداً على سيناريوك. هنا يمكنك إضافة واجهة ترث من IHostedService
ومن ثم اضف طريقة لذلك الواجهة التي يمكنك الاتصال بها من متحكمك.
ملاحظة: لا يزال من الضروري إضافته كخدمة مضيفة في ASP.net لكي تعمل الخدمة بالفعل.
public interface IEmailSenderHostedService : IHostedService, IDisposable
{
Task SendEmailAsync(BaseEmailModel message);
}
كل ما نحتاجه بعد ذلك هو تسجيل هذا كأفردتون ومن ثم استخدام هذا في متحكمنا.
services.AddSingleton<IEmailSenderHostedService, EmailSenderHostedService>();
services.AddHostedService<IEmailSenderHostedService>(provider => provider.GetRequiredService<IEmailSenderHostedService>());
SP.net سوف ترى أن هذا يحتوي على الوصلة البينية الصحيحة مزينة وسوف تستخدم هذا التسجيل لتشغيل IHostedService
.
معلومات أخرى لضمان أن IHostedService
هو a حالة منفردة هو إلى استخدام AddSingleton
إلى تسجيل الخدمة الخاصة بك ثم تمر IHostedService
كـ "طريقة اعتبارية". هذا سيضمن أن حالة واحدة فقط من الخدمة الخاصة بك يتم إنشاؤها واستخدامها طوال عمر الطلب.
services.AddSingleton<EmailSenderHostedService>();
services.AddHostedService(provider => provider.GetRequiredService<EmailSenderHostedService>());
كما ترون هنا أنا أولاً أسجل IHostedService
(في IHostedLifeCycleService
(كأحادي طن واحد ومن ثم أستخدم AddHostedService
طريقة تسجيل الخدمة كطريقة تصنيع. وسيكفل ذلك إنشاء واستخدام حالة واحدة فقط من الخدمة طوال مدة تقديم الطلب.
كالعادة هناك طريقتان لسلخ قطة طريقة العمل في المصنع هي أيضاً طريقة جيدة للتأكد من أن الخدمة الخاصة بك هي حالة واحدة. الأمر عائد لكِ منهجكِ الذي تأخذينه. أرجو أن تكون هذه المادة قد ساعدتك على فهم كيفية ضمان IHostedService
هو مثال واحد.