# एक समाचार पत्र सदस्यता सेवा भाग 2 | - | सेवाओं को फिर से संशोधित करना

# परिचय

में [इस श्रृंखला का भाग 1](/blog/anewslettersubscriptionservicept1) मैंने आपको दिखाया है कि कैसे मैं एक नया Newsletter सदस्यता पृष्ठ बनाया।

[TOC]

<!--category-- ASP.NET, Hangfire, Email Newsletter -->
<datetime class="hidden">2024-09-23T10:45</datetime>

# परियोजनाएं

मूलतः मेरे पास केवल एक ही एकल परियोजना थी, जो वेबसाइट के लिए सभी कोड को समाहित करती है।
इसके अतिरिक्त अनुसूचक सेवा का विभाजन मेरे लिए तर्कसंगत है क्योंकि मैं इसे एक अलग डॉकर कन्टेनर के रूप में विस्थापित कर सकता हूँ जो मुझे बिना अनुसूचितकर्ता को पुनः आरंभ करने के बिना वेबसाइट को अद्यतन करने की अनुमति देता है

इसे करने के लिए मैंने तर्कसंगत रूप से चिंताओं को समूहित किया है 5 परियोजनाएं. यह ASP में एक आम दृष्टिकोण है

अब मैं इन में से प्रत्येक के लिए परीक्षण परियोजना जोड़ सकता है और उन्हें अलग-अलग परीक्षण कर सकते हैं

## अधिक स्पष्ट

यह परियोजना एक ASP है

## सर्वाधिक स्पष्ट.Dbसंबंध

यह मेरी मुख्य संदर्भ परिभाषा है जिसका उपयोग डाटाबेस के साथ आदान-प्रदान के लिए किया जाता है

## सर्वाधिक स्पष्ट सेवाएँ

यह मुख्य वर्ग लाइब्रेरी परियोजना है जो सभी सेवाओं को रखता है जो डाटाबेस के साथ बातचीत करता है

## सर्वाधिक स्पष्ट.Shared

यह एक वर्ग लाइब्रेरी परियोजना है जो सभी साझा मॉडलों को रखता है, जो दोनों Mostlylucid और Mostly Lucid के द्वारा उपयोग किए जाते हैं

## सर्वाधिक स्पष्ट.अनुसूचक सेवा

यह एक वेब अनुप्रयोग है जो हंगफायर सेवा को नियंत्रित करता है जो अनुसूचित कार्यों के साथ ही ईमेल भेजने के लिए अंतबिंदुओं को चलाता है

इसका ढांचा नीचे दिखाया गया है
आप देख सकते हैं कि SchedulerService और मुख्य Mostlylucid केवल आरंभिक सेटअप के लिए DbContext वर्ग का उपयोग करते हैं

```mermaid
graph LR
  Mostlylucid[Mostlylucid] --> |Intialize| Mostlylucid_DbContext[Mostlylucid.DbContext]
  Mostlylucid[Mostlylucid] --> Mostlylucid_Shared[Mostlylucid.Shared]
  Mostlylucid[Mostlylucid] --> Mostlylucid_Services[Mostlylucid.Services]
  
  Mostlylucid_DbContext[Mostlylucid.DbContext] --> Mostlylucid_Shared[Mostlylucid.Shared]
  
  Mostlylucid_Services[Mostlylucid.Services] --> Mostlylucid_DbContext[Mostlylucid.DbContext]
  Mostlylucid_Services[Mostlylucid.Services] --> Mostlylucid_Shared[Mostlylucid.Shared]
  


  Mostlylucid_SchedulerService[Mostlylucid.SchedulerService] --> Mostlylucid_Shared[Mostlylucid.Shared]
  Mostlylucid_SchedulerService[Mostlylucid.SchedulerService] --> |Intialize|Mostlylucid_DbContext[Mostlylucid.DbContext]
  Mostlylucid_SchedulerService[Mostlylucid.SchedulerService] --> Mostlylucid_Services[Mostlylucid.Services]

```

जैसा कि आमतौर पर मैं डाटाबेस स्थापित करने के लिए प्रविष्टि बिंदु के रूप में एक विस्तार विधि का उपयोग करता हूँ

```csharp
public static class Setup
{
    public static void SetupDatabase(this IServiceCollection services, IConfiguration configuration,
        IWebHostEnvironment env, string applicationName="mostlylucid")
    {
        services.AddDbContext<IMostlylucidDBContext, MostlylucidDbContext>(options =>
        {
            if (env.IsDevelopment())
            {
                options.EnableDetailedErrors(true);
                options.EnableSensitiveDataLogging(true);
            }
            var connectionString = configuration.GetConnectionString("DefaultConnection");
            var connectionStringBuilder = new NpgsqlConnectionStringBuilder(connectionString)
            {
                ApplicationName = applicationName
            };
            options.UseNpgsql(connectionStringBuilder.ConnectionString);
        });
    }
}
```

इस विधि को बुलाने के अलावा कोई भी शीर्ष स्तर परियोजना DbContext परियोजना पर कोई निर्भरता नहीं है

नोट: DbContext आरंभ करने के दौरान एक रेस स्थिति से बचने के लिए यह महत्वपूर्ण है ' विशेष रूप से यदि आप माइग्रेशन चलाते हैं तो ) इसे करने के लिए यहाँ मैं सिर्फ अपने Docker Compose फ़ाइल में एक निर्भरता जोड़ा था ताकि सुनिश्चित किया जा सके कि मुख्य Mostlylucid परियोजना मेरे अनुसूचक सेवा प्रारंभ करने से पहले चालू है।

```dockerfile
  depends_on:
      - mostlylucid 
  healthcheck:
      test: ["CMD", "curl", "-f", "http://mostlylucid:80/healthy"]
      interval: 30s
      timeout: 10s
      retries: 5
```

आप इसे अन्य तरीके से भी पूरा कर सकते हैं [रेडिस](https://redis.io/) एक लॉक फ्लैग सेट करने के लिए, जो प्रत्येक इंस्टैंस माइग्रेशन चलाने से पहले जाँच करता है / अन्य कार्य सुनिश्चित करते हैं कि एक समय में केवल एक इंस्टींस चला जा सकता है

बेशक रेडिस के कई अन्य उपयोग भी हैं, विशेष रूप से ASP के साथ `IDistributedCache` इंटरफेस जो आपको एक कैश प्रदाता के रूप में रेडिस का उपयोग करने की अनुमति देता है टैग आधारित समाप्ति के साथ

# हैंगफायर सेटअप

मैंने अपने scheduling को संभालने के लिए Hangfire का उपयोग करना चुना है क्योंकि इसका ASP .NET Core के साथ सुविधाजनक एकीकरण है और उपयोग करने में आसान है

इसके लिए मैं अनुसूचक सेवा परियोजना में निम्नलिखित NuGet पैकेजों को जोड़ा

```bash
dotnet add package Hangfire.AspNetCore
dotnet add package Hangfire.PostgreSql
```

इसका मतलब है कि मैं निर्धारित कार्यों के लिए Postgres को मेरे भंडार के रूप में उपयोग कर सकता हूँ

अब मेरे पास हैंगफायर सेटअप है मैं अपने अनुसूचित कार्यों को जोड़ने के लिए शुरू कर सकता हूँ

```csharp
public static class JobInitializer
{
    private const string AutoNewsletterJob = "AutoNewsletterJob";
    private const string DailyNewsletterJob = "DailyNewsletterJob";
    private const string WeeklyNewsletterJob = "WeeklyNewsletterJob";
    private const string MonthlyNewsletterJob = "MonthlyNewsletterJob";
    public static void InitializeJobs(this IApplicationBuilder app)
    {
      var scope=  app.ApplicationServices.CreateScope();
        var recurringJobManager = scope.ServiceProvider.GetRequiredService<RecurringJobManager>();
      
        recurringJobManager.AddOrUpdate<NewsletterSendingService>(AutoNewsletterJob,  x =>  x.SendNewsletter(SubscriptionType.EveryPost), Cron.Hourly);
        recurringJobManager.AddOrUpdate<NewsletterSendingService>(DailyNewsletterJob,  x =>  x.SendNewsletter(SubscriptionType.Daily), "0 17 * * *");
        recurringJobManager.AddOrUpdate<NewsletterSendingService>(WeeklyNewsletterJob,  x =>  x.SendNewsletter(SubscriptionType.Weekly), "0 17 * * *");
        recurringJobManager.AddOrUpdate<NewsletterSendingService>(MonthlyNewsletterJob,  x => x.SendNewsletter(SubscriptionType.Monthly), "0 17 * * *");
    }
}
```

यहाँ आप देख सकते हैं मैं प्रत्येक के लिए एक काम है `SubscriptionType` जो एक फ्लैग है कि कितनी बार समाचार पत्र भेजा जाता है निर्धारित करता है `Cron` कार्य की आवृत्ति सेट करने के लिए वर्ग.

चूंकि मेरे पास मासिक और साप्ताहिक दोनों सदस्यताओं के लिए दिन का विकल्प है, इसलिए मैं समय 17:00 (5 pm | ) को सेट करता हूँ क्योंकि यह समाचार पत्र भेजने का अच्छा समय है।
मैं एक काम भी करता हूँ जो हर घंटे चलता है कि उन लोगों को समाचार पत्र भेजने के लिए, जिन्होंने प्रत्येक पोस्ट में सदस्यता ली है

यह फिर एक में बुलाता है `SendNewsletter` विधि में `NewsletterSendingService` वर्ग. Which I'll go into more detail in a later post

# डीटोस और मानचित्रण

मैं उपयोग करता हूँ [डीटीओ](https://en.wikipedia.org/wiki/Data_transfer_object) मेरे अनुप्रयोग की परतों के बीच डेटा ले जाने के लिए . यह जटिलता जोड़ता है क्योंकि मुझे नक्शे बनाने की आवश्यकता होती है | ( | प्रायः दो बार |) | Entities को Dtos में और फिर Dtos को ViewModels पर \ ( |and back |

आप इस मैपिंग करने के लिए AutoMapper जैसे दृष्टिकोणों का उपयोग कर सकते हैं / Mapster आदि [क्वेरी योग्य विस्तार](https://docs.automapper.org/en/stable/Queryable-Extensions.html) विधि जो आपको क्वेरी में डीटो के लिए सीधे नक्शे बनाने की अनुमति देता है

लेकिन इस मामले में मैंने सिर्फ मैपर एक्सटेंशन जोड़ने का निर्णय किया जहां मैं आवश्यक था

```csharp
public static class BlogPostEntityMapper
{
   public static BlogPostDto ToDto(this BlogPostEntity entity, string[] languages = null)
    {
        return new BlogPostDto
        {
            Id = entity.Id.ToString(),
            Title = entity.Title,
            Language = entity.LanguageEntity?.Name,
            Markdown = entity.Markdown,
            UpdatedDate = entity.UpdatedDate.DateTime,
            HtmlContent = entity.HtmlContent,
            PlainTextContent = entity.PlainTextContent,
            Slug = entity.Slug,
            WordCount = entity.WordCount,
            PublishedDate = entity.PublishedDate.DateTime,
            Languages = languages ?? Array.Empty<string>()
        };
    }
}
```

यहाँ आप देख सकते हैं मैं मानचित्र `BlogPostEntity` मेरे मुख्य `BlogPostDto` जो मेरा स्थानांतरण वस्तु है  
लक्ष्य यह है कि फ्रंट एंड सेवाएँ अस्तित्व वस्तु के बारे में कुछ नहीं जानते हैं और आधारभूत डेटा संरचना से वंचित हैं

इन उच्च स्तरीय सेवाओं में मैं इन Dtos को दृश्य मॉडलों पर नक्शे बनाने के लिए कोड है जो बाद में नियंत्रकों में उपयोग किया जाता है

```csharp
    public static PostListModel ToPostListModel(this BlogPostDto dto)
    {
        return new PostListModel
        {
            Id = dto.Id,
            Title = dto.Title,
            Language = dto.Language,
            UpdatedDate = dto.UpdatedDate.DateTime,
            Slug = dto.Slug,
            WordCount = dto.WordCount,
            PublishedDate = dto.PublishedDate,
            Languages = dto.Languages
        };
    }
    
    public static BlogPostViewModel ToViewModel(this BlogPostDto dto)
    {
        return new BlogPostViewModel
        {
            Id = dto.Id,
            Title = dto.Title,
            Language = dto.Language,
            Markdown = dto.Markdown,
            UpdatedDate = dto.UpdatedDate.DateTime,
            HtmlContent = dto.HtmlContent,
            PlainTextContent = dto.PlainTextContent,
            Slug = dto.Slug,
            WordCount = dto.WordCount,
            PublishedDate = dto.PublishedDate,
            Languages = dto.Languages
        };
    }
```

एक मापर उपकरण का उपयोग करके यह बॉयलप्लेट कोड से बच सकता है (और त्रुटि कम कर सकते हैं, लेकिन मैं यह दृष्टिकोण मेरे लिए इस उदाहरण में अच्छा काम करता है

अब मैं इन सभी मॉडल सेटअप है मैं उन्हें प्रयोग करने के लिए कंट्रोलर विधि जोड़ने शुरू कर सकता हूँ
मैं इस प्रवाह को अगले भाग में कवर करेंगे

# अंत में

अब हमारे पास तंत्र है जो हमें अपने उपयोगकर्ताओं को newsletter सदस्यता प्रदान करने के लिए शुरू करने की अनुमति देता है
यह रीफॅक्टरिंग थोड़ा दर्दनाक था लेकिन यह-' अधिकांश परियोजनाओं के लिए जीवन का एक तथ्य है।
भावी पोस्ट्स इस ',' के बाकी को शामिल करेंगे जिसमें मेरी उपयोग पर एक अनुवर्तन [फ्लुएंटमेल] ईमेल और होस्टेड सेवा भेजने के लिए