# मार्किग के लिए रिमोट मार्क लॉकर बनाया जा रहा है

<!--category-- Markdown, AI-Article,  MarkDig, ASP.NET Core, C#, API, Nuget, FetchExtension-->
<datetime class="hidden">2025-11-07T10:00</datetime>

# परिचय

इस ब्लॉग का निर्माण करते वक्‍त मुझे कई चुनौतियों का सामना करना पड़ा ।

मैं अपनी GiB से फ़ाइलों को पढ़ना चाहता था, जिसमें अन्य परियोजनाओं से दस्तावेज शामिल हैं, और सब कुछ स्वतः तुल्यकालित करता हूँ.`Mostlylucid.Markdig.FetchExtension`इसका हल?

> एक मनपसंद निशानीय विस्तार जो रिमोट निशान को प्रदान करता है वह समय व कैश को बुद्धिमान बना देता है.

> इस पोस्ट में, मैं मैं मैं कैसे बनाया के माध्यम से आप चलना होगा

> **- लाने के लिए पूरा समाधान**ध्यान दीजिए: यह अभी भी पहले की बात है लेकिन मैं इसे वहाँ से बाहर पाना चाहता था.`disable="true"`मज़ा लें लेकिन यह अभी तक काम नहीं कर सकता.

> **इस लेख में एआई उत्पन्न किया गया है - सेला कोड इस्तेमाल किया गया है जिसने मुझे इस विशेषता को बनाने में भी मदद दी ।**यूपीसी`[TOC]`जोड़ा गया:

पैरामीटर ताकि हम अब टैग को ठीक से निर्धारित कर सकते हैं बिना उन्हें संसाधित किया जा रहा है![UPCT (Nv 7, 2025)](https://github.com/scottgal/mostlylucidweb/tree/main/Mostlylucid.Markdig.FetchExtension).

[![: विषय - वस्तु (टीओसी) की तालिका जोड़ा गया है!](https://img.shields.io/nuget/v/mostlylucid.Markdig.FetchExtension.svg)](https://www.nuget.org/packages/mostlylucid.Markdig.FetchExtension)
[![प्रयोक्ता](https://img.shields.io/nuget/dt/mostlylucid.Markdig.FetchExtension.svg)](https://www.nuget.org/packages/mostlylucid.Markdig.FetchExtension)

[TOC]

# दस्तावेज़ शीर्षकों से विषय- वस्तु की तालिका को स्वचलित बनाने के लिए अपने निशान को नीचे रखता है.

यहाँ इसके लिए स्रोत देखें

1. **इस साइट के लिए GiHHHब पर**लागू नहीं
2. **लागू नहीं**यह क्यों करें?
3. **तकनीकी विवरणों में परिवर्तन करने से पहले, मैं समस्या को समझा देती हूँ ।**मेरे पास कई अनुभव हैं जहाँ मुझे बाहरी निशान सामग्री शामिल करनी है:
4. **पैकेज पढ़ें**: जब मैं एक नुए पैकेज के बारे में लिखता हूँ जो मैंने प्रकाशित किया है, मैं इसे सीधे गाब से पढ़ना चाहता हूँ

एपीआई प्रलेखन

- : बाहरी एपीआई खुराक जो प्रायः सिंक में रहने की ज़रूरत होती है
- साझेदारी विषयवस्तु
- : दस्तावेज़ीकरण जो एक भंडार में रहता है लेकिन अनेक स्थानों में प्रकट होने की जरूरत है
- परफार्मेंस

: मैं हर पृष्ठ लोड पर इस सामग्री को लाने के लिए नहीं करना चाहता - यह धीमा और सीमित होगा

# विपरीत ओर का प्रयोग एक एचटीटीपी क्लाएंट को खोजने के लिए किया जाएगा जब भी आपको इसकी आवश्यकता होगी.

लेकिन यह समस्या है:

```mermaid
graph TD
    A[Markdown with fetch tags] --> B[MarkdownFetchPreprocessor]
    B --> C{Check Cache}
    C -->|Fresh| D[Return Cached Content]
    C -->|Stale/Missing| E[Fetch from Remote URL]
    E -->|Success| F[Update Cache]
    E -->|Failure| G{Has Cached?}
    G -->|Yes| H[Return Stale Cache]
    G -->|No| I[Return Error Comment]
    F --> J[Replace fetch with Content]
    D --> J
    H --> J
    I --> J
    J --> K[Processed Markdown]
    K --> L[Your Markdig Pipeline]
    L --> M[Final HTML]

```

हर निवेदन दूरस्थ सर्वर को हिट करता है**नेटवर्क देर से प्रभाव पृष्ठ लोड करता है**कोई ऑफ़लाइन समर्थन नहीं

1. अस्थायी रूप से छोड़ने का कोई नियंत्रण नहीं`<fetch>`मुझे एक स्मार्ट व्यक्‍ति की ज़रूरत थी: एक बार लाने के लिए, आराम से, स्वचालित रूप से तरोताज़ा, और असफलताओं को संभालिए ।
2. ओवरव्यू
3. यह विस्तार एक पूर्वोभाषिक जाँच का भाग होने के बजाय, संभव रूप से निकट आता है ।
4. यह महत्वपूर्ण है क्योंकि इसका अर्थ है आपके सम्पूर्ण मार्किडेंट के माध्यम से सामग्री को प्रवाहित करता है, अपने सभी मनपसंद विस्तार, वाक्य एक्सटेंशन, और ध्यान पाने का मतलब है.

मुख्य अन्तर्दृष्टि यहाँ है

# प्री- प्रोसेसिंग



```markdown
# My Documentation

<fetch markdownurl="https://raw.githubusercontent.com/user/repo/main/README.md"
       pollfrequency="24" disable="true"/>
```

अपने निशान नीचे निशान हिट से पहले, हम:

- के लिए स्कैन करें
- टैग
- सामग्री को ठीक करें ( कैश या रिमोट से)
- टैग को वास्तविक चिह्न नीचे से बदलें

# उसके बाद मरकुस को सब कुछ एकसाथ करने दो

यह निश्‍चित करता है कि सुसंगतता - सभी चिह्नों को उसके स्रोत के बावजूद भी समान उपचार हो जाता है ।**मूल सिंटेक्स**विस्तार का उपयोग करना सरल है.

```mermaid
graph LR
    A[IMarkdownFetchService Interface] --> B[InMemoryMarkdownFetchService]
    A --> C[FileBasedMarkdownFetchService]
    A --> D[PostgresMarkdownFetchService]
    A --> E[SqliteMarkdownFetchService]
    A --> F[SqlServerMarkdownFetchService]
    A --> G[YourCustomService]

    B --> H[ConcurrentDictionary]
    C --> I[File System + SemaphoreSlim]
    D --> J[PostgreSQL Database]
    E --> K[SQLite Database]
    F --> L[SQL Server Database]
    G --> M[Your Storage Backend]

```

## अपने निशान नीचे:

यह बात है!`IMarkdownFetchService`:

```csharp
public interface IMarkdownFetchService
{
    Task<MarkdownFetchResult> FetchMarkdownAsync(
        string url,
        int pollFrequencyHours,
        int blogPostId = 0);

    Task<bool> RemoveCachedMarkdownAsync(
        string url,
        int blogPostId = 0);
}
```

एक्सटेंशन गुम है

## Git से पढ़ें

24 घंटे के लिए यह कैश`ConcurrentDictionary`:

```csharp
public class InMemoryMarkdownFetchService : IMarkdownFetchService
{
    private readonly ConcurrentDictionary<string, CacheEntry> _cache = new();
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly ILogger<InMemoryMarkdownFetchService> _logger;

    public async Task<MarkdownFetchResult> FetchMarkdownAsync(
        string url,
        int pollFrequencyHours,
        int blogPostId)
    {
        var cacheKey = GetCacheKey(url, blogPostId);

        // Check cache
        if (_cache.TryGetValue(cacheKey, out var cached))
        {
            var age = DateTimeOffset.UtcNow - cached.FetchedAt;
            if (age.TotalHours < pollFrequencyHours)
            {
                _logger.LogDebug("Returning cached content for {Url}", url);
                return new MarkdownFetchResult
                {
                    Success = true,
                    Content = cached.Content
                };
            }
        }

        // Fetch fresh content
        var fetchResult = await FetchFromUrlAsync(url);

        if (fetchResult.Success)
        {
            _cache[cacheKey] = new CacheEntry
            {
                Content = fetchResult.Content,
                FetchedAt = DateTimeOffset.UtcNow
            };
        }
        else if (cached != null)
        {
            // Fetch failed, return stale cache
            _logger.LogWarning("Fetch failed, returning stale cache for {Url}", url);
            return new MarkdownFetchResult
            {
                Success = true,
                Content = cached.Content
            };
        }

        return fetchResult;
    }

    private static string GetCacheKey(string url, int blogPostId)
        => $"{url}_{blogPostId}";
}
```

के बाद के निवेदन पर कैश सामग्री वापस करें

1. जब कैश का समय बीत जाए तो स्वचालित ताज़ा करें
2. भंडारण प्रदाता
3. मेरे बाद डिज़ाइन के सिद्धांतों में से एक था
4. पेश की जा रही है
5. 
6. अलग अनुप्रयोगों की अलग - अलग ज़रूरतें होती हैं ।
7. एक छोटे से डेमो एप्पल की आवश्यकता नहीं है, लेकिन एक बहु-सर्वर उत्पादन तैनात करता है।

तो मैं एक प्लग-मिक्षित भंडारण संरचना का निर्माण किया:**कोर इंटरफेस**सभी लागू करें

## सरल और साफ.

हर कार्यान्वयन अपने तरीके से प्राप्त करता है, लेकिन इंटरफेस लगातार रहता है ।

```csharp
public class FileBasedMarkdownFetchService : IMarkdownFetchService
{
    private readonly string _cacheDirectory;
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly ILogger<FileBasedMarkdownFetchService> _logger;
    private readonly SemaphoreSlim _fileLock = new(1, 1);

    public async Task<MarkdownFetchResult> FetchMarkdownAsync(
        string url,
        int pollFrequencyHours,
        int blogPostId)
    {
        var cacheKey = ComputeCacheKey(url, blogPostId);
        var cacheFile = GetCacheFilePath(cacheKey);

        await _fileLock.WaitAsync();
        try
        {
            // Check if file exists and is fresh
            if (File.Exists(cacheFile))
            {
                var fileInfo = new FileInfo(cacheFile);
                var age = DateTimeOffset.UtcNow - fileInfo.LastWriteTimeUtc;

                if (age.TotalHours < pollFrequencyHours)
                {
                    var cached = await File.ReadAllTextAsync(cacheFile);
                    return new MarkdownFetchResult
                    {
                        Success = true,
                        Content = cached
                    };
                }
            }

            // Fetch fresh
            var fetchResult = await FetchFromUrlAsync(url);

            if (fetchResult.Success)
            {
                await File.WriteAllTextAsync(cacheFile, fetchResult.Content);
            }
            else if (File.Exists(cacheFile))
            {
                // Return stale on fetch failure
                var stale = await File.ReadAllTextAsync(cacheFile);
                return new MarkdownFetchResult
                {
                    Success = true,
                    Content = stale
                };
            }

            return fetchResult;
        }
        finally
        {
            _fileLock.Release();
        }
    }

    private string GetCacheFilePath(string cacheKey)
        => Path.Combine(_cacheDirectory, $"{cacheKey}.md");

    private static string ComputeCacheKey(string url, int blogPostId)
    {
        var combined = $"{url}_{blogPostId}";
        using var sha256 = SHA256.Create();
        var bytes = Encoding.UTF8.GetBytes(combined);
        var hash = sha256.ComputeHash(bytes);
        return Convert.ToHexString(hash);
    }
}
```

भंडार में: डेमो के लिए सही

1. आसान कार्यान्वयन उपयोग`SemaphoreSlim`जैसा कि आप इसे देख सकते हैं निम्नलिखित करता है:
2. यूआरएल तथा ब्लॉग पोस्ट आईडी से एक कैश कुंजी बनाता है
3. यदि हम कैश सामग्री मिला है और यह ताजा है तो चेक करें
4. यदि कैश ताज़ा है, इसे तत्काल लौटाता है
5. यदि नसीहत किया जाता है, नए सामग्री को लाने की कोशिश करता है

## सफलता पर, कैश अद्यतन करें

कैश सामग्री के साथ असफलता पर, सादा कैश लौटाता है (सर्व- Al- revping!)

```csharp
public class PostgresMarkdownFetchService : IMarkdownFetchService
{
    private readonly MarkdownCacheDbContext _dbContext;
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly ILogger<PostgresMarkdownFetchService> _logger;

    public async Task<MarkdownFetchResult> FetchMarkdownAsync(
        string url,
        int pollFrequencyHours,
        int blogPostId)
    {
        var cacheKey = GetCacheKey(url, blogPostId);

        // Query cache
        var cached = await _dbContext.MarkdownCache
            .FirstOrDefaultAsync(c => c.CacheKey == cacheKey);

        if (cached != null)
        {
            var age = DateTimeOffset.UtcNow - cached.LastFetchedAt;
            if (age.TotalHours < pollFrequencyHours)
            {
                return new MarkdownFetchResult
                {
                    Success = true,
                    Content = cached.Content
                };
            }
        }

        // Fetch fresh
        var fetchResult = await FetchFromUrlAsync(url);

        if (fetchResult.Success)
        {
            if (cached == null)
            {
                cached = new MarkdownCacheEntry
                {
                    CacheKey = cacheKey,
                    Url = url,
                    BlogPostId = blogPostId
                };
                _dbContext.MarkdownCache.Add(cached);
            }

            cached.Content = fetchResult.Content;
            cached.LastFetchedAt = DateTimeOffset.UtcNow;
            await _dbContext.SaveChangesAsync();
        }
        else if (cached != null)
        {
            // Return stale
            return new MarkdownFetchResult
            {
                Success = true,
                Content = cached.Content
            };
        }

        return fetchResult;
    }
}
```

कैश के बगैर असफलता पर, त्रुटि लौटाता है

```sql
CREATE TABLE markdown_cache (
    id SERIAL PRIMARY KEY,
    cache_key VARCHAR(128) NOT NULL UNIQUE,
    url VARCHAR(2048) NOT NULL,
    blog_post_id INTEGER NOT NULL,
    content TEXT NOT NULL,
    last_fetched_at TIMESTAMP WITH TIME ZONE NOT NULL,
    CONSTRAINT ix_markdown_cache_cache_key UNIQUE (cache_key)
);

CREATE INDEX ix_markdown_cache_url_blog_post_id
    ON markdown_cache(url, blog_post_id);
```

यह पैटर्न -

```mermaid
graph TB
    subgraph "Load Balancer"
        LB[Load Balancer]
    end

    subgraph "Application Servers"
        A1[App Server 1<br/>FetchExtension]
        A2[App Server 2<br/>FetchExtension]
        A3[App Server 3<br/>FetchExtension]
    end

    subgraph "Shared Cache"
        PG[(PostgreSQL<br/>markdown_cache table)]
    end

    subgraph "External Content"
        R1[Remote URL 1]
        R2[Remote URL 2]
        R3[Remote URL 3]
    end

    LB --> A1
    LB --> A2
    LB --> A3

    A1 <-->|Read/Write Cache| PG
    A2 <-->|Read/Write Cache| PG
    A3 <-->|Read/Write Cache| PG

    A1 -.->|Fetch if cache miss| R1
    A2 -.->|Fetch if cache miss| R2
    A3 -.->|Fetch if cache miss| R3

```

के- डिफ- एनईसी

# - भरोसेमंद होने के लिए महत्वपूर्ण है.

अगर GiHb नीचे है, तो भी आपकी साइट में कैश्ड सामग्री के साथ काम करती रहती है ।

```bash
dotnet add package mostlylucid.Markdig.FetchExtension
```

फ़ाइल- बेस- भंडारण: सादा लगन

```bash
# For in-memory (demos/testing)
# Already included in base package

# For file-based storage
# Already included in base package

# For PostgreSQL
dotnet add package mostlylucid.Markdig.FetchExtension.Postgres

# For SQLite
dotnet add package mostlylucid.Markdig.FetchExtension.Sqlite

# For SQL Server
dotnet add package mostlylucid.Markdig.FetchExtension.SqlServer
```

## एकल सर्वर कर्मों के लिए, फ़ाइल- आधारित भंडारण महान:

कुंजी पाइंट्स यहाँ:`Program.cs`:

```csharp
using Mostlylucid.Markdig.FetchExtension;

var builder = WebApplication.CreateBuilder(args);

// Option 1: In-Memory (simplest)
builder.Services.AddInMemoryMarkdownFetch();

// Option 2: File-Based (persists across restarts)
builder.Services.AddFileBasedMarkdownFetch("./markdown-cache");

// Option 3: PostgreSQL (multi-server)
builder.Services.AddPostgresMarkdownFetch(
    builder.Configuration.GetConnectionString("MarkdownCache"));

// Option 4: SQLite (single server with DB)
builder.Services.AddSqliteMarkdownFetch("Data Source=markdown-cache.db");

// Option 5: SQL Server (enterprise)
builder.Services.AddSqlServerMarkdownFetch(
    builder.Configuration.GetConnectionString("MarkdownCache"));

var app = builder.Build();

// If using database storage, ensure schema exists
if (app.Environment.IsDevelopment())
{
    app.Services.EnsureMarkdownCacheDatabase();
}

// Configure the extension with your service provider
FetchMarkdownExtension.ConfigureServiceProvider(app.Services);

app.Run();
```

## उपयोग

थ्रेड सुरक्षित फाइल पहुँच के लिए

```csharp
public class MarkdownRenderingService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly MarkdownFetchPreprocessor _preprocessor;
    private readonly MarkdownPipeline _pipeline;

    public MarkdownRenderingService(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
        _preprocessor = new MarkdownFetchPreprocessor(serviceProvider);

        _pipeline = new MarkdownPipelineBuilder()
            .UseAdvancedExtensions()
            .UseSyntaxHighlighting()
            .UseToc()  // Add TOC support for [TOC] markers
            .UseYourCustomExtensions()
            .Build();
    }

    public string RenderMarkdown(string markdown)
    {
        // Step 1: Preprocess to handle fetch tags
        var processed = _preprocessor.Preprocess(markdown);

        // Step 2: Run through your normal Markdig pipeline
        return Markdown.ToHtml(processed, _pipeline);
    }
}
```

रिक्त फ़ाइलनाम बनाने के लिए यूआरएल + ब्लॉग पोस्ट पोस्ट आईडी है

1. ताजाता निर्धारित करने के लिए फ़ाइल परिवर्धन समय का उपयोग करता है`<fetch>`चल छवि को फिर से चालू करें
2. एक ही सादा- एन- सादा पैटर्न
3. डाटाबेस भंडारण: उत्पाद- पढ़ें
4. विक्रेताओं के लिए, विशेष रूप से बहु-सर्वर सेटअप के लिए, आप एक साझा कैश चाहते हैं.

# यही कारण है कि जहां डाटाबेस प्रबंधक आते हैं:

## डाटाबेस प्रसंग सरल है:

कई सर्वरों में, यह आपको सभी शर्तों पर कैशी देता है:**सभी सर्वर एक ही कैश साझा करते हैं.**जब सर्वर 1 पढ़ने लायक होता है, तो सर्वर 2 और 3 तुरंत उस कैशी सामग्री से फायदा होता है.

**एक्सटेंशन अद्यतन किया जा रहा है**

शुरू करना सीधा है.`[TOC]`प्रथम, बेस पैकेज संस्थापित करें:

```markdown
# My Document

[TOC]

# Introduction
Content here...

# Getting Started
More content...

## Installation
Details...
```

फिर अपने भंडार प्रदाता चुनें:

```html
<nav class="ml_toc" aria-label="Table of Contents">
  <ul>
    <li><a href="#introduction">Introduction</a></li>
    <li><a href="#getting-started">Getting Started</a>
      <ul>
        <li><a href="#installation">Installation</a></li>
      </ul>
    </li>
  </ul>
</nav>
```

**एनईएसईपी में कॉन्फ़िगरेशन. नेटटीसी कोर**

अपने में

```markdown
[TOC cssclass="my-custom-toc"]
```

अपने मार्क रेंडरिंग के साथ संयोजन

```html
<nav class="my-custom-toc" aria-label="Table of Contents">
  <!-- TOC content -->
</nav>
```

**कुंजी पूर्व प्रक्रिया चरण है**

1. **मैं इसे अपने ब्लॉग में विस्तार कैसे है:**प्रवाह है:

2. **अपने निशान नीचे रखता है**टैग
   
   - प्री- प्रोसेसर उन्हें वास्तविक चिह्न नीचे करने का संकल्प करता है`id="getting-started"`
   - सम्मिलित चिह्न चिह्न चिह्न चिह्नों के माध्यम से चला जाता है`id="api-reference"`

3. **सब कुछ अपने मनपसंद विस्तार, scting, आदि हो जाता है.**विस्तृत विशेषताएँ`ul/li`विषय - सूची बनाना

**पैकेज में अब एक शामिल है**

अलग

```csharp
var pipeline = new MarkdownPipelineBuilder()
    .UseAdvancedExtensions()
    .UseToc()  // Add TOC support - position in pipeline doesn't matter!
    .Use<YourOtherExtensions>()
    .Build();
```

**विषय- वस्तु तालिका (टीओसी) विस्तार!**यह लाने के साथ पैकेज किया गया है, यह पूरी तरह से स्वतंत्र है और अपने पर उपयोग किया जा सकता है.**आप अपने दस्तावेज़ के शीर्षकों से विषय- वस्तुओं को स्वचलित तैयार कर सकते हैं.**बेसिक उपयोग:

- सरल जोड़
- कहीं भी अपने निशान नीचे:

यह एंकर कड़ियों के साथ सभी शीर्षकों की घोंसला सूची बनाता है:`.UseToc()`कस्टम सीएसएस वर्ग:

**आप उपयोग के लिए कस्टम सीएसएस क्लास उल्लेखित कर सकते हैं:**यह आपकी मनपसंद क्लास के साथ इस तरह अनुवाद करता है:

- यह कैसे काम करता है:
- अपने आप पता लगाएँ
- : अपने दस्तावेज़ में न्यूनतम शीर्षक स्तर का पता लगाने तथा उसके अनुसार समायोजन करने के लिए.

**यदि आपका दस्तावेज़ H2 से शुरू होता है, तो विषय-सी ऊपरी स्तर के रूप में H2 के साथ व्यवहार करता है.**आईडी तैयार करना`[TOC]`: शीर्षक स्वचालित रूप से एंकर लिंक के लिए दिया गया है:`.UseToc()`"से शुरू हो रही है" फ़्लोर

## " API संदर्भ" QXml

नेबुले स्ट्रक्चर

```markdown
<fetch markdownurl="https://raw.githubusercontent.com/user/repo/main/docs/README.md"
       pollfrequency="24"
       transformlinks="true" disable="true"/>
```

: दान करनेवाला सही तरह से घोंसला बनाता है

- `./CONTRIBUTING.md` → `https://github.com/user/repo/blob/main/docs/CONTRIBUTING.md`
- `../images/logo.png` → `https://github.com/user/repo/blob/main/images/logo.png`
- स्ट्रक्चर जो आपके दस्तावेज़ पदक्रम को दिखाता है.

कोसी समर्थन सक्षम किया जा रहा है:

```csharp
public class MarkdownLinkRewriter
{
    public static string RewriteLinks(string markdown, string sourceUrl)
    {
        var document = Markdown.Parse(markdown);
        var baseUri = GetBaseUri(sourceUrl);

        foreach (var link in document.Descendants<LinkInline>())
        {
            if (IsRelativeLink(link.Url))
            {
                link.Url = ResolveRelativeLink(baseUri, link.Url);
            }
        }

        using var writer = new StringWriter();
        var renderer = new NormalizeRenderer(writer);
        renderer.Render(document);
        return writer.ToString();
    }

    private static bool IsRelativeLink(string url)
    {
        if (string.IsNullOrEmpty(url)) return false;
        if (url.StartsWith("http://") || url.StartsWith("https://")) return false;
        if (url.StartsWith("#")) return false;  // Anchor
        if (url.StartsWith("mailto:")) return false;
        return true;
    }
}
```

## अपने मार्कडिग्रेसन को कॉन्फ़िगर करते समय, विषय- अतिरिक्त विस्तार जोड़ें:

पाइपलाइन स्थिति:

```markdown
<fetch markdownurl="https://api.example.com/status.md"
       pollfrequency="1"
       showsummary="true" disable="true"/>
```

किसी मार्किड एक्सटेंशन के विपरीत, COM विस्तार

> _परवाह नहीं है जहाँ आप इसे जोड़ते हैं[कि तुम लोग ज़रूर एक सख्ती के बाद दूसरी सख्ती में झगड़ा करो](https://api.example.com/status.md)एक्सटेंशन स्वचालितः_

पारसर सूची के प्रारंभ में इसका पारसर शामिल करता है (स्थिति 0)

```markdown
<fetch markdownurl="https://example.com/docs.md"
       pollfrequency="24"
       showsummary="true"
       summarytemplate="Last updated: {retrieved:long} | Status: {status} | Next refresh: {nextrefresh:relative}" disable="true"/>
```

पूरी व्याख्या के बाद रेंडर करता है, संपूर्ण दस्तावेज़ में से शीर्षक जमा करता है

> तो आप जोड़ सकते हैं

किसी भी हाल में, मध्य, या अंत में अपने एमिलिक कॉन्फ़िगरेशन के अंत.

- `{retrieved:format}`आयात
- `{age}`BC एक्सटेंशन लाने का विस्तार पूरी तरह से स्वतंत्र है.
- `{url}`वे सिर्फ सुविधा के लिए एक साथ पैकेज किया जाता है.
- `{nextrefresh:format}`आप कर सकते हैं:
- `{pollfrequency}`बिना प्राप्त किए विषय का प्रयोग करें
- `{status}`प्रतिसी के बगैर प्राप्त करें

## दोनों साथ उपयोग करें

टिप्पणीः`disable="true"`प्रतिसी मार्कर आपके मुख्य निशान नीचे फ़ाइलें में काम करता है तथा रिमोट सामग्री को ले जाता है.

```markdown
<!-- This will be processed and fetch content -->
<fetch markdownurl="https://example.com/README.md" pollfrequency="24"/>

<!-- This will NOT be processed - useful for documentation -->
<fetch markdownurl="https://example.com/README.md" pollfrequency="24" disable="true"/>
```

यदि आप GiB से एक प्रकाशन लाते हैं जिसमें है

- , यह दस्तावेज़ के शीर्षकों से सामग्री की सामग्री स्वचालित रूप से तैयार करेगा (जैसे कि आपने जोड़ा है)
- कि तुम भी तुला में सीमा का उल्लंघन न करो
- लिंक रूपांतरण

जब रिमोट निशान नीचे लाया जाता है (संभवतः GiHब से), सापेक्ष लिंक टूट जाते हैं.`<fetch>`एक्सटेंशन उन्हें स्वचालित फिर से लिख सकता है:`<fetch-summary>`यह रूपांतरण:

```markdown
<fetch-summary url="https://example.com/api/status.md" disable="true"/>
```

## निरपेक्ष यूआरएल तथा लंगर संरक्षित करता है

कार्यान्वयन चिह्नों को फिर से लिखने के लिए प्रयोग करता है:

```csharp
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddPostgresMarkdownFetch(connectionString);

        var sp = services.BuildServiceProvider();
        var eventPublisher = sp.GetRequiredService<IMarkdownFetchEventPublisher>();

        // Subscribe to events
        eventPublisher.FetchBeginning += (sender, args) =>
        {
            Console.WriteLine($"Fetching {args.Url}...");
        };

        eventPublisher.FetchCompleted += (sender, args) =>
        {
            var source = args.WasCached ? "cache" : "remote";
            Console.WriteLine($"Fetched {args.Url} from {source} in {args.Duration.TotalMilliseconds}ms");
        };

        eventPublisher.FetchFailed += (sender, args) =>
        {
            Console.WriteLine($"Failed to fetch {args.Url}: {args.ErrorMessage}");
        };
    }
}
```

मेटाडाटा सारांश प्राप्त करें

```mermaid
sequenceDiagram
    participant MD as Markdown Processor
    participant EP as Event Publisher
    participant FS as Fetch Service
    participant ST as Storage Backend
    participant L as Your Listeners

    MD->>EP: FetchBeginning
    EP->>L: Notify FetchBeginning
    EP->>FS: FetchMarkdownAsync(url)
    FS->>ST: Check Cache
    alt Cache Fresh
        ST-->>FS: Cached Content
        FS->>EP: FetchCompleted (cached=true)
    else Cache Stale/Missing
        FS->>FS: HTTP GET
        alt Success
            FS->>ST: Update Cache
            ST-->>FS: OK
            FS->>EP: FetchCompleted (cached=false)
        else Failure
            FS->>EP: FetchFailed
            EP->>L: Notify FetchFailed
        end
    end
    EP->>L: Notify FetchCompleted
    EP->>L: Notify ContentUpdated
    FS-->>MD: MarkdownFetchResult

    Note over L: Listeners can be: Logging, Metrics, Telemetry, Webhooks
```

# आप पाठकों को दिखा सकते हैं जब तक कि सामग्री अंतिम बार प्राप्त नहीं की गई थी:

यह पाद- सूचना के साथ अनुवाद करता है:

```mermaid
stateDiagram-v2
    [*] --> CheckCache: Fetch Request

    CheckCache --> Fresh: Cache exists & age < pollFrequency
    CheckCache --> Stale: Cache exists & age >= pollFrequency
    CheckCache --> Missing: No cache entry

    Fresh --> ReturnCached: Return cached content
    ReturnCached --> [*]

    Stale --> FetchRemote: Attempt HTTP GET
    Missing --> FetchRemote: Attempt HTTP GET

    FetchRemote --> UpdateCache: Success
    FetchRemote --> HasStale: Failure

    UpdateCache --> ReturnFresh: Return new content
    ReturnFresh --> [*]

    HasStale --> ReturnStale: Return stale cache
    HasStale --> ReturnError: No cache available

    ReturnStale --> [*]
    ReturnError --> [*]

    note right of Fresh
        pollFrequency = 0
        means always stale
    end note

    note right of HasStale
        Stale-while-revalidate
        pattern ensures uptime
    end note
```

सामग्री से लिया गया

1. **http://a.example.com/smd**06 यान 2025 में (2 घंटे पहले)
2. **टैम्प्लेट को मनपसंद बनाएँ:**आउटपुट: भली-भाँति संपादित करें (E)
3. **अंतिम बार अद्यतन: 06 जनवरी 2025: 30**उपलब्धस्थान्स: (v)
4. **- अंतिम प्राप्त तारीख़/ समय**- पाने के बाद से मानव द्वारा पढ़ा जाने योग्य समय

- स्रोत यूआरएल**- जब सामग्री ताज़ा की जाएगी**- कैश अवधि घंटे में

# - कैश स्थिति (शंश/केप)

दस्तावेज़ीकरण के लिए प्रक्रिया अक्षम किया जा रहा है

```csharp
public class MarkdownController : Controller
{
    private readonly IMarkdownFetchService _fetchService;

    public async Task<IActionResult> InvalidateCache(string url)
    {
        var removed = await _fetchService.RemoveCachedMarkdownAsync(url);

        if (removed)
        {
            return Ok(new { message = "Cache invalidated" });
        }

        return NotFound(new { message = "No cache entry found" });
    }
}
```

जब लाने के बारे में दस्तावेज़ लिखा जाए (जैसा कि इस लेख में दिखाया गया है), तो आपको टैगों को बिना प्रक्रमित किए दिखाने के लिए एक रास्ता चाहिए ।

```csharp
// GitHub webhook notifies of README update
app.MapPost("/webhooks/github", async (
    GitHubWebhookPayload payload,
    IMarkdownFetchService fetchService) =>
{
    if (payload.Repository?.FullName == "user/repo" &&
        payload.Commits?.Any(c => c.Modified?.Contains("README.md") == true) == true)
    {
        var url = "https://raw.githubusercontent.com/user/repo/main/README.md";
        await fetchService.RemoveCachedMarkdownAsync(url);

        return Results.Ok(new { message = "Cache invalidated" });
    }

    return Results.Ok(new { message = "No action needed" });
});
```

# उपयोग

गुण:

```csharp
public class MarkdownFetchServiceTests
{
    [Fact]
    public async Task FetchMarkdownAsync_CachesContent()
    {
        // Arrange
        var services = new ServiceCollection();
        services.AddLogging();
        services.AddInMemoryMarkdownFetch();
        var sp = services.BuildServiceProvider();

        var fetchService = sp.GetRequiredService<IMarkdownFetchService>();
        var url = "https://raw.githubusercontent.com/user/repo/main/README.md";

        // Act - First fetch (from network)
        var result1 = await fetchService.FetchMarkdownAsync(url, 24, 0);

        // Act - Second fetch (from cache)
        var result2 = await fetchService.FetchMarkdownAsync(url, 24, 0);

        // Assert
        Assert.True(result1.Success);
        Assert.True(result2.Success);
        Assert.Equal(result1.Content, result2.Content);
    }

    [Fact]
    public async Task FetchMarkdownAsync_ReturnsStaleOnFailure()
    {
        // Arrange
        var services = new ServiceCollection();
        services.AddLogging();
        services.AddInMemoryMarkdownFetch();
        var sp = services.BuildServiceProvider();

        var fetchService = sp.GetRequiredService<IMarkdownFetchService>();
        var url = "https://httpstat.us/200?sleep=100";

        // Act - First fetch succeeds
        var result1 = await fetchService.FetchMarkdownAsync(url, 0, 0);

        // Change URL to fail
        var badUrl = "https://httpstat.us/500";

        // Act - Second fetch fails, should return stale
        var result2 = await fetchService.FetchMarkdownAsync(badUrl, 0, 0);

        // Assert
        Assert.True(result1.Success);
        // Even though fetch failed, we return success with stale content
        Assert.True(result2.Success);
    }
}
```

# अक्षम टैग चिह्न के रूप में, के लिए सही में रहता है:

एक्सटेंशन के बारे में दस्तावेज़ को स्वयं लिखा जा रहा है

1. **शिक्षण पाठ में उदाहरण बनाया जा रहा है**ट्रिगर प्राप्ति के बगैर टैग सिंटेक्स दिखाएँ`ConcurrentDictionary`यह दोनों के लिए काम करता है
2. **और**टैग:
3. **मॉनीटर के लिए घटना सिस्टम**सभी विकल्पों को लाने के लिए एक्सटेंशन घटनाओं को प्रकाशित करता है:
4. **यह अनुप्रयोग इंसाइट ऑन द स्क्रिप्चर्स्‌, या आपके लॉगिंग को बढ़ाने के लिए आसान बनाता है:**विस्तृत विवरण में कौशल को कैश कर रहा है`IHttpClientFactory`कॉलिंग व्यवहार एक राज्य मशीन पैटर्न का अनुसरण करता है:
5. **मुख्य अन्तर्दृष्टि यहाँ:**ताजा कैश

- तुरंत वापस आओ, कोई नेटवर्क हिट नहीं

- स्टेल कैश
- - ताजा लाने के लिए कोशिश करें, लेकिन अगर लाने में असफल हो
- कैश गुम है
- - पाने के लिए या वापसी त्रुटि

# शून्य पोल आवृत्ति

- हमेशा ताजा लाने के लिए ( परीक्षण के लिए आवश्यक)

```yaml
name: Publish Markdig.FetchExtension

on:
  push:
    tags:
      - 'fetchextension-v*.*.*'

permissions:
  id-token: write
  contents: read

jobs:
  build-and-publish:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: '9.0.x'

    - name: Extract version from tag
      id: get_version
      run: |
        TAG=${GITHUB_REF#refs/tags/fetchextension-v}
        echo "VERSION=$TAG" >> $GITHUB_OUTPUT

    - name: Build
      run: dotnet build Mostlylucid.Markdig.FetchExtension/Mostlylucid.Markdig.FetchExtension.csproj --configuration Release -p:Version=${{ steps.get_version.outputs.VERSION }}

    - name: Pack
      run: dotnet pack Mostlylucid.Markdig.FetchExtension/Mostlylucid.Markdig.FetchExtension.csproj --configuration Release --no-build -p:PackageVersion=${{ steps.get_version.outputs.VERSION }} --output ./artifacts

    - name: Login to NuGet (OIDC)
      id: nuget_login
      uses: NuGet/login@v1
      with:
        user: 'mostlylucid'

    - name: Publish to NuGet
      run: dotnet nuget push ./artifacts/*.nupkg --api-key ${{ steps.nuget_login.outputs.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
```

इस पैटर्न को कहा जाता है

# के- डिफ- एनईसी

और यह वफ़ादारी के लिए उत्कृष्ट है.

```markdown
# My NuGet Package Documentation

Here's the official README from GitHub:

<fetch markdownurl="https://raw.githubusercontent.com/scottgal/mostlylucidweb/main/Umami.Net/README.md"
       pollfrequency="24"
       transformlinks="true"
       showsummary="true"
       summarytemplate="*Fetched {age} from GitHub*" disable="true"/>

# Installation

The package is available on NuGet...
```

अगर आपका स्रोत नीचे जाता है, तो भी आपकी साइट कैशे की मरम्मत करती रहती है ।

# कैश मिटाना व प्रबंधन

कभी कभी आपको अवैध कैश को दस्ती रूप से हटाने की आवश्यकता है:

1. **जब सामग्री परिवर्तन हो तो या के द्वारा:**एक्सटेंशन जाँचें
2. **एक्सटेंशन में विस्तार जाँच शामिल हैं.**मैं उन्हें संरचना है कि कैसे है:
3. **परफ़ॉर्मेंस पर ध्यान दें**एक्सटेंशन प्रदर्शन के लिए बनाया गया है:
4. **मौज़ूदा शब्दकोश को कॉनकर करें (u)**- इनहर्मरी कार्यान्वयन उपयोग में
5. **थ्रेड सुरक्षित पहुँच के लिए**केरोफ़ॉयर
6. **- फ़ाइल आधारित उपयोग एट स्थिति स्थिति से बाहर निकलने के लिए**डाटाबेस निर्देशिका

- सभी डाटाबेस फ़ीडों में कैश कुंजी पर उचित निर्देशिकाएं हैं

- [एचटीटीपी क्लाएंट पूलिंग](https://www.nuget.org/packages/mostlylucid.Markdig.FetchExtension)
- [- उपयोग](https://www.nuget.org/packages/mostlylucid.Markdig.FetchExtension.Postgres)
- [कुशल कनेक्शन रीकर के लिए](https://www.nuget.org/packages/mostlylucid.Markdig.FetchExtension.Sqlite)
- [सभी मार्ग को बन्द करें](https://www.nuget.org/packages/mostlylucid.Markdig.FetchExtension.SqlServer)

- कोई ब्लॉक कॉल नहीं, सब कुछ अतुल्यकालिक रूप से है[मेरे घर सर्वर पर विशिष्ट प्रदर्शन संख्या:](https://github.com/scottgal/mostlylucidweb/tree/main/Mostlylucid.Markdig.FetchExtension)

कैश हिट: < 1ms