مهاجرت به .NET Standard 2.1

انتقال یک برنامه Doconut.NETStandard موجود از 26.7.0 به 26.8.0

Doconut دو ادغام متمایز .NET Standard دارد. آن‌ها از همان نام بسته Doconut.NETStandard استفاده می‌کنند، بنابراین قبل از تغییر بسته‌ها، راه‌اندازی، مجوزها یا منابع مرورگر، نسل را از نسخه و APIهای موجود در برنامه شناسایی کنید.

نسخه نقطهٔ تقاطع است

هر دو نسل تحت یک شناسه بسته یکسان منتشر می‌شوند. فقط نسخه آن‌ها را از هم متمایز می‌کند:

نسخههدفAPI
26.7.0 و پایین‌ترnetstandard2.0API قبلی
26.8.0 و بالاترnetstandard2.1API فعلی

افزایش شماره نسخه، ارتقا است. بستهٔ جداگانه‌ای برای نصب وجود ندارد و شیم سازگاری هم نیست — 26.8.0 یک سطح API متفاوت است. پس از ارتقا خطاهای کامپایل را انتظار داشته باشید؛ بقیهٔ این راهنما فهرست آن‌هاست.

کدام ادغام را استفاده می‌کنید؟

اگر پروژه شامل…نسل
new Viewer(_cache, _accessor, ...)قبلی
viewer.OpenDocument(...) (همزمان)قبلی
viewer.InitCache()قبلی
UseDoconut(new DoconutOptions { ... })قبلی
UseDoconutWebFarm(...) / WebFarmOptionsقبلی
Viewer.DoconutLicense(...) یا Viewer.SetLicensePlugin(...)قبلی
viewer.Converter.GetConverter()قبلی
using Doconut.Configs; / using Doconut.Models;قبلی
به‌صورت دستی کپی شده docViewer.js، documentLinks.js یا docViewer.UI.jsقبلی
builder.Services.AddDoconut(...)فعلی
app.UseDoconutResources() به‌همراه app.UseDoconut() بدون آرگومانفعلی
Viewer ارائه‌شده توسط تزریق وابستگیفعلی
await viewer.OpenDocumentAsync(...)فعلی

اگر هر دو ستون در یک برنامه ظاهر شوند، مهاجرت را ناقص در نظر بگیرید. توکن سند را از طریق منابع یا میدلور نسل دیگر ارسال نکنید.

ابتدا چارچوب میزبان را بررسی کنید

این یک مانع سخت است، نه یک ترجیح. بستهٔ فعلی هدف netstandard2.1 دارد:

میزبانقابلیت استفاده از 26.8.0
.NET Core 3.0 یا بعد از آن (شامل .NET 5/6/7/8)بله
.NET Framework، هر نسخه‌ایخیر

چارچوب .NET Framework نمی‌تواند به کتابخانهٔ netstandard2.1 ارجاع دهد. اگر برنامه بر روی .NET Framework اجرا می‌شود، در اینجا متوقف شوید و به جای آن بستهٔ Doconut.NETFramework را استفاده کنید؛ از نسخهٔ 26.9.0 به .NET Framework 4.7.2 نیاز دارد. هر دو برنامهٔ مرجع برای این انتشار هدف net8.0 دارند.

قبل از مهاجرت

  1. یک شاخه و یک نسخهٔ پشتیبان قابل استقرار از برنامهٔ موجود ایجاد کنید.
  2. نسخه‌های دقیق بستهٔ هسته و افزونه را ثبت کنید.
  3. تمام فراخوانی‌های new Viewer(...)، فراخوانی InitCache()، فراخوانی بارگذاری مجوز، نگاشت UseDoconutWebFarm، کنترل‌کنندهٔ تبدیل، اسکریپت کپی‌شدهٔ Doconut، عمل سفارشی نوار ابزار، بازگشت‌خوان Viewer و نقطهٔ انتهایی باز کردن سند را فهرست کنید.
  4. فایل‌های .lic فعلی و رازهای استقرار را خارج از کنترل نسخه نگه دارید.
  5. یک مجموعهٔ نماینده از اسناد PDF، Office، تصویر، CAD، ایمیل، DICOM، قابل جستجو، محافظت‌شده با رمز عبور و حاشیه‌نویسی‌شده را جمع‌آوری کنید.
  6. زمان‌سنجی نشست فعلی، رفتار امنیتی، قلم‌ها و تنظیمات پلتفرم را ثبت کنید.

قبل از تغییر به محیط تولید، یک محیط را مهاجرت کنید. ادغام فعلی طول عمر سرویس، مسیریابی درخواست، مالکیت نشست، تحویل منابع مشتری و — اگر یک فارم اجرا می‌کنید — نحوهٔ دسترسی صفحات به مرورگر را تغییر می‌دهد.

1. فضاهای نام: تقریباً همه چیز به ریشه منتقل شد

انواع پیکربندی در چهار فضای نام پخش شده بودند. اکنون همهٔ آن‌ها در Doconut هستند.

قبلحال
using Doconut.Configs.View;using Doconut;
using Doconut.Models;using Doconut;
using Doconut.Configs;using Doconut;
using Doconut.Configs.Cloud;using Doconut.Clouds;
using Doconut.Configs.Conversion;بدون تغییر
using Doconut.Middleware;بدون تغییر

نام‌های نوع تغییر نکرده‌اند: WordConfig، PdfConfig، ExcelConfig، CssConfig، ScriptConfig، CloudUploadConfig، AzureConfig، CDNConfig، FileShareConfig و بقیه همچنان موجود هستند. در بیشتر فایل‌ها ویرایش کامل حذف دو خط using است.

Doconut.Configs.Editor (BaseConfigEditor، PdfConfigEditor) حذف شده و جایگزینی در این بسته ندارد. اگر برنامهٔ شما به آن نوع‌ها ارجاع می‌داد، آن بخش را جداگانه برنامه‌ریزی کنید و آن را بخشی از این مهاجرت در نظر نگیرید.

2. سازگاری بسته و مجوز

به‌صورت عمدی بستهٔ هسته‌ای را جایگزین یا به‌روزرسانی کنید؛ به شناسهٔ بستهٔ یکسان برای انتخاب API جدید وابسته نشوید. فرمان پیش‌فرض آخرین نسخهٔ پایدار را نصب می‌کند:

bash
dotnet add package Doconut.NETStandard

برای یک مهاجرت قابل بازتولید به نسخه‌ای که توسط این راهنما بررسی شده است، نسخه را به‌عنوان گزینه‌ای جداگانه پاس دهید:

bash
dotnet add package Doconut.NETStandard --version 26.8.0

تمام افزونه‌های Doconut را با همان نسخهٔ بستهٔ هسته‌ای نگه دارید — Doconut.NETStandard.Converter و Doconut.NETStandard.Dicom. یکپارچه‌سازی فعلی مجوزها را یک‌بار در طول AddDoconut() بارگذاری می‌کند، با استفاده از این اولویت:

text
LicenseStream > LicenseContent > LicensePath > automatic discovery

کشف خودکار به دنبال فایل‌های Doconut.Viewer.lic و فایل‌های همراه Doconut.Viewer.<Capability>.lic می‌گردد. فراخوانی‌های ایستا قبلی Viewer.DoconutLicense(...) و Viewer.SetLicensePlugin(...) دیگر مکانیزم راه‌اندازی فعلی نیستند، و همچنین List<string> مسیرهای مجوز که سازندهٔ قدیمی Viewer می‌پذیرفت، نیز نیست. مجوز را به DoconutOptions منتقل کنید، فایل‌های همراه را هنگام استفاده از کشف خودکار با هم نگه دارید، پس از تغییر مجوز دوباره راه‌اندازی کنید، و قابلیت‌ها را از طریق IDoconutLicenseService تأیید کنید.

فرض نکنید حضور یک مجوز افزونهٔ قدیمی نشان‌دهندهٔ حق استفاده برای ساخت افزونهٔ فعلی است. Viewer، Search، Annotation، Converter و DICOM را به‌صورت جداگانه با آرتیفکت‌های نسخهٔ تأییدشده تست کنید.

3. راه‌اندازی و تزریق وابستگی

قبلاً Viewer برای هر درخواست ساخته می‌شد و گزینه‌ها به میدلور پاس داده می‌شد:

csharp
// یکپارچه‌سازی قبلی — فقط برای مقایسه؛ این را در برابر SDK فعلی کامپایل نکنید.
var viewer = new Viewer(_cache, _accessor);
// …
appBranch.UseDoconut(new DoconutOptions { UnSafeMode = false, ShowDoconutInfo = false });

یکپارچه‌سازی فعلی Doconut را یک‌بار ثبت می‌کند و Viewer را از تزریق وابستگی دریافت می‌کند:

csharp
builder.Services.AddDoconut(options =>
{
    options.UnsafeMode      = false;   // توجه داشته باشید تغییر املایی: UnSafeMode → UnsafeMode
    options.ShowDoconutInfo = false;
    options.LicensePath     = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // حالت جلسه به یک کش نیاز دارد (MVC/Razor Pages قبلاً یک کش اضافه می‌کند)
builder.Services.AddSession();

app.UseSession();
app.UseDoconutResources();
app.UseDoconut();

Viewer یک سرویس موقت است. مدیر جلسهٔ سند و کش آن، وضعیت سند طولانی‌مدت را در اختیار دارند، نه نمونهٔ خاص تزریق‌شدهٔ Viewer.

سه مورد برای انتقال:

  • new Viewer(cache, accessor) حذف شده است. Doconut.Viewer را به کنترلر خود تزریق کنید، یا در یک نمای Razor از @inject Doconut.Viewer Viewer استفاده کنید.
  • UseDoconutResources() جایگزین اتصال دستی app.UseMiddleware<EmbeddedResourceMiddleware>() می‌شود و باید قبل از شاخهٔ سند اجرا شود.
  • UnSafeMode → UnsafeMode. تغییر یک حرفی که کامپایلر آن را تشخیص می‌دهد.

زمانی که UnsafeMode برابر false باشد، SDK توکن جلسه را نسبت به درخواست اصلی اعتبارسنجی می‌کند، بنابراین AddSession() / UseSession() باید همچنان در خط لوله باشند، UseSession() قبل از شاخه.

متدهایی که دیگر وجود ندارند

قبلحال
viewer.OpenDocument(path, config, options)await viewer.OpenDocumentAsync(path, config, options)
viewer.InitCache()حذف شد — هیچ چیزی برای گرم‌کردن دستی وجود ندارد؛ فراخوانی را حذف کنید و هر پرچم «آیا قبلاً مقداردهی اولیه کرده‌ام؟» را نیز حذف کنید
viewer.ExportToPng(sharedPath)به بخش §7 مراجعه کنید
viewer.ExportToCloud(cloudConfig)به بخش §7 مراجعه کنید
using var v = new Viewer(...); v.CloseDocument(t)viewer.CloseDocument(token) در نمونهٔ تزریق‌شده

4. میدلور و مسیردهی منابع

UseDoconut() اکنون هیچ آرگومانی نمی‌گیرد. شکل شاخهٔ MapWhen خودش نیازی به تغییر ندارد — فقط میدلور داخل آن:

csharp
app.UseDoconutResources();   // قبل از شاخه

app.MapWhen(
    ctx => ctx.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
    branch => branch.UseDoconut());

در خط لولهٔ فعلی:

  1. فراخوانی UseSession() قبل از Doconut در حالی که امنیت جلسه فعال است؛
  2. فراخوانی UseDoconutResources() قبل از UseDoconut()؛
  3. ResourcesPath، URLهای تولید شدهٔ منابع، و ResPath کلاینت را هم‌راستا نگه دارید؛
  4. شاخه‌ای که نقشه می‌کشید و BasePath کلاینت را هم‌راستا نگه دارید.

MiddlewarePath یک پیکربندی معتبر است؛ به‌تنهایی یک شاخهٔ ASP.NET Core ایجاد نمی‌کند.

5. ساخت Viewer و طول عمر

کش‌های متعلق به برنامه برای اشیاء Viewer را حذف کنید. در یک Viewer چیزی برای نگه‌داشتن باقی نمی‌ماند: هیچ وضعیت سندی را حمل نمی‌کند و کش نشست همهٔ مواردی را که پس از یک درخواست باقی می‌مانند، مالکیت می‌کند. آن را در یک نقطهٔ انتهایی، صفحهٔ Razor، کنترلر یا سرویس برنامهٔ scoped تزریق کنید:

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    var token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

توکن بازگردانده‌شده یک نشست سند سمت‑سرور را شناسایی می‌کند. آن را همانند یک اعتبار‌نامهٔ حامل در نظر بگیرید: لاگ نکنید، ذخیره نکنید و در تجزیه و تحلیل‌ها قرار ندهید.

6. باز کردن و بستن اسناد

متد همزمان OpenDocument(...) را با OpenDocumentAsync(...) جایگزین کنید:

csharp
// یکپارچه‌سازی فعلی: Viewer از DI دریافت می‌شود و باز کردن سند به صورت ناهمزمان است.
var token = await viewer.OpenDocumentAsync(path, new PdfConfig { AllowSearch = true });

بارگذاری‌های فعلی می‌توانند مسیر فایل یا جریان، یک پیکربندی فرمت اختیاری، DocOptions اختیاری و یک توکن لغو را بپذیرند. وقتی مرورگر دیگر به جلسه نیاز ندارد، نشست سرور را به‌صورت صریح ببندید:

csharp
viewer.CloseDocument(token);

هر فراخوانی InitCache() و پرچم محافظ آن را حذف کنید — دیگر نیازی به گرم‌کردن دستی نیست. پس از تغییر کتابخانه، توکنی که قبلاً ساخته شده بود را دوباره استفاده نکنید؛ هر سند را دوباره از طریق API جدید باز کنید.

7. مزرعه وب → توزیع‌شده: بزرگ‌ترین تغییر

UseDoconutWebFarm و WebFarmOptions دیگر وجود ندارند. اگر برنامهٔ شما از آن‌ها استفاده می‌کند، این بخش ارتقاء شماست؛ اگر نه، به بخش §8 بروید.

قبلاً یک میدل‌ویر همه‌چیز را انجام می‌داد: تصاویر پیش‌ساخته را از یک پوشهٔ مشترک سرو می‌کرد و پرس‌و‌جو می‌کرد در حالی که گرهٔ دیگری هنوز آن‌ها را می‌نوشت.

csharp
// یکپارچه‌سازی قبلی — یک فراخوانی، هماهنگی ضمنی از طریق سیستم‌فایل.
appBranch.UseDoconutWebFarm(new WebFarmOptions
{
    Path                = Path.Combine(env.WebRootPath, "webfarm"),
    PageWaitTimeSeconds = 180,
    StartWaitFromPage   = 5,
});

کار همان‌طور به دو نیمهٔ صریح تقسیم شده است. سمت نوشتن — گره‌ای که سند را باز می‌کند، تمام مجموعهٔ آثار (صفحات، اطلاعات سند، ایندکس جستجو، فراداده) را در اشتراک می‌گذارد:

csharp
var result = await _publisher.PublishAsync(          // DistributedDocumentPublisher، تزریق‌شده از DI
    pathToFile,
    new FileShareConfig
    {
        Location              = CloudLocation.FileShare,
        RootPath              = @"\\fileserver\doconut",   // همان پوشه‌ای که امروز استفاده می‌کنید
        UsePerTokenSubfolders = true,
    },
    config,
    documentOptions,
    new DistributedDocumentPublishOptions
    {
        CleanUpStagingDirectory = true,
        FirstPagePriority       = true,
    });

return Content(result.Token);

سمت خواندن — هر گره‌ای از اشتراک سرو می‌کند:

csharp
app.MapWhen(
    ctx => ctx.Request.Path.Value?.EndsWith("DocImage.axd", StringComparison.OrdinalIgnoreCase) == true,
    branch => branch.UseDoconutCloud<FileShareCloudHandler>(options =>
    {
        options.Location          = CloudLocation.FileShare;
        options.CloudUploadConfig = /* همان FileShareConfig همان‌طور که در بالا است */;
    }, "/"));
قبلاکنون
WebFarmOptions.PathFileShareConfig.RootPath
PageWaitTimeSeconds / StartWaitFromPageحذف شده — جایگزین شده توسط FirstPagePriority در سمت نوشتن به‌علاوه FileShareConfig.ReadReadyPollMs در سمت خواندن
viewer.OpenDocument(...) سپس viewer.ExportToPng(share)یک فراخوانی PublishAsync(...)
DocOptions.IsWebfarm / WebfarmPathهنوز وجود دارد، به صورت IsWebFarm / WebFarmPath — اما در مسیر انتشار نیازی به آن نیست

توپولوژی شما تغییر نمی‌کند. RootPath را به همان اشتراک UNC اشاره دهید و هر گره همان‌طور که امروز کار می‌کند ادامه می‌دهد. چیزی که تغییر می‌کند این است که انتشار یک عملیات صریح است که شما فراخوانی می‌کنید، نه یک اثر جانبی رندر.

هر دو نیمه باید توافق کنند. اگر RootPath یا UsePerTokenSubfolders بین ناشر و هندلر متفاوت باشد، گره دیگر 404 برمی‌گرداند. پیکربندی را در یک کمک‌کننده بسازید و از هر دو سمت صدا بزنید.

PageWaitTimeSeconds = 180 به معنای «بلوک تا سه دقیقه تا ظاهر شدن صفحه» بود. جایگزین آن صریح است: با FirstPagePriority = true، صفحهٔ ۱ بلافاصله سرو می‌شود و صفحات بعدی تا زمان نوشتن HTTP 202 Accepted برمی‌گردانند. Viewer بسته‌شده به‌صورت خودکار وقتی retryOn409 تنظیم شده باشد، دوباره تلاش می‌کند. کلاینتی که دوباره تلاش نمی‌کند، برای صفحاتی که هنوز در حال رندر هستند، کاشی‌های خراب نشان می‌دهد.

ترتیب کامل، شامل انتشار پس‌زمینه و بلیت‌های دسترسی، در استقرارهای توزیع‌شده موجود است.

۸. تبدیل یک افزونه است و بسیار کوچکتر

افزونه را ثبت کنید، سپس واسط DocumentConverter را تزریق کنید:

csharp
builder.Services.AddDoconut(o => o.AddPlugin<ConverterPlugin>());
csharp
// ادغام قبلی
var converter = viewer.Converter.GetConverter();
var config    = new WordConfigConverter { TargetFormat = WordConfigConverter.EnTargetFormat.PDF };
var result    = converter.Convert(stream, SourceFormatConverter.DOCX, config);

// ادغام فعلی
var result = await _converter.ConvertAsync(stream, ".docx", ConversionTarget.Pdf, null, ct);

آنچه جایگزین می‌شود:

  • SourceFormatConverter — حذف شد. پسوند فایل را به‌صورت رشته، شامل نقطه، پاس بدهید.
  • انواع *ConfigConverter مربوط به هر خانواده (WordConfigConverter، ExcelConfigConverter، …) که هر کدام دارای enum تو در تو EnTargetFormat بودند — حذف شد. اکنون یک enum ConversionTarget برای هر خانواده وجود دارد.
  • viewer.Converter.GetConverter() — حذف شد. Doconut.DocumentConverter را تزریق کنید.

ConvertAsync یک جریان قابل جستجو برمی‌گرداند، بنابراین return File(result, mime, name) به‌صورت مستقیم کار می‌کند — کپی MemoryStream که کد قبلی نیاز داشت، غیرضروری است. در نمونهٔ ارائه‌شده این باعث حذف یک سوئیچ ۶۸ موردی برای پسوندها و ده کنترلر تقریباً مشابه شد.

برای مشاهدهٔ تمام قابلیت‌ها به افزونهٔ مبدل مراجعه کنید.

۹. ارائه‌دهندگان ابری

UseDoconutCloud عمومی شد. ارائه‌دهنده به‌جای مقدار Location که داخل میدلور بررسی می‌شد، به‌عنوان آرگومان نوع مشخص می‌شود:

csharp
// ادغام قبلی
appBranch.UseDoconutCloud(new CloudOptions { Location = CloudLocation.CDN, ... });

// ادغام فعلی
branch.UseDoconutCloud<CDNCloudHandler>(options =>
{
    options.Location          = CloudLocation.CDN;
    options.CloudUploadConfig = new CDNConfig { DoconutCdnUrl = "..." };
}, "/");

کلاس‌های پیکربندی به‌جز تغییر فضای نام (Doconut.Configs.Cloud → Doconut.Clouds) تغییر نکرده‌اند: AzureConfig، AmazonConfig، GoogleCloudConfig، DropBoxConfig، RedisConfig، CDNConfig، FTPConfig، به‌علاوهٔ FileShareConfig جدید. هندلرها به‌صورت <Provider>CloudHandler نام‌گذاری می‌شوند.

هندلر CDN فقط‑خواندنی است — آنچه در CDN موجود است سرو می‌کند؛ آپلود نمی‌کند.

۱۰. اشیای پیکربندی که نامشان تغییر کرد

قبلحال
DoconutOptions.UnSafeModeUnsafeMode
DocOptions.IsWebfarmIsWebFarm
DocOptions.WebfarmPathWebFarmPath
DocOptions.ImageResolutionهنوز موجود است اما منسوخ است — به‌جای آن وضوح را در پیکربندی فرمت تنظیم کنید

DocOptions همچنین ویژگی IsSecured را اضافه می‌کند.

API فعلی همچنین نگرانی‌ها را نسبت به نسخهٔ قبلی به‌صورت دقیق‌تری جدا می‌کند، بنابراین اطمینان حاصل کنید که هر تنظیم در شیء‌ای قرار دارد که اکنون مالک آن است:

نگرانینوع فعلی
مسیرهای میدلور، مجوزها، ثبت افزونهDoconutOptions
رمز عبور، زمان‌سنجی، امنیت، واترمارکDocOptions
رندر فرمت و DPIPdfConfig، WordConfig، ExcelConfig و سایر انواع BaseConfig
پیش‌فرض‌های ویجت مرورگرViewerConfig یا گزینه‌های معادل جاوااسکریپت
CSS و اسکریپت‌های تولید‌شدهCssConfig و ScriptConfig
ذخیره‌سازی مشترک و ارائه‌دهندگان ابریFileShareConfig و سایر پیکربندی‌های موجود در Doconut.Clouds

DocOptions.ImageResolution را به‌عنوان کنترل رندر به‌کار نبرید. این گزینه منسوخ است؛ BaseConfig.ImageResolution را در پیکربندی مخصوص فرمت تنظیم کنید. به‌جای فرض اینکه پیکربندی قبلی همان رفتار را دارد، تمام پیش‌فرض‌ها را بازبینی کنید.

۱۱. نمایشگر سمت‌کلاینت — دو تغییر که به‌صورت ساکت شکست می‌خورند

هر دو این موارد سرور را به‌صورت کامل کار می‌گذارند. سند شما رندر می‌شود، صفحه درست به‌نظر می‌رسد و هیچ خطایی در کنسول مرورگر ظاهر نمی‌شود. حتی اگر کد C# شما در اولین بار کامپایل شود، این بخش را بخوانید.

فراخوانی‌های نمایشگر اکنون به‌عنوان گزینه هستند، نه توابع سراسری

نمایشگر قبلی هندلرهای شما را بر اساس نام صدا می‌زد، به‌طوری که نام تابع سراسری از شناسهٔ کانتینر استخراج می‌شد. تعریف function ctlDoc_OnViewerReady() در صفحه‌ای که <div id="div_ctlDoc"> دارد کافی بود. نمایشگر فعلی کال‌بک‌های صریح را در شیء گزینه‌ها می‌گیرد و دیگر به دنبال این توابع سراسری نمی‌گردد:

javascript
objctlDoc = $("#div_ctlDoc").docViewer({
    // ... گزینه‌های موجود شما ...
    onViewerBusy:     ctlDoc_OnViewerBusy,      // قبلاً با نام پیدا می‌شد
    onViewerReady:    ctlDoc_OnViewerReady,     // قبلاً با نام پیدا می‌شد
    onCopy:           ctlDoc_Copy,              // قبلاً ctlDoc_Copy(text)
    onAutoLoadStatus: ctlDoc_AutoLoadStatus     // قبلاً ctlDoc_AutoLoadStatus(page)
});

چرا این مشکل‌ساز است: فراخوانی قبلی در یک catch خالی بسته شده بود که همهٔ خطاها را نادیده می‌گرفت. اکنون توابع شما به‌سادگی اجرا نمی‌شوند و هیچ‌خطایی گزارش نمی‌شود. علامت معمول یک اسپینر مشغول است که هرگز متوقف نمی‌شود — چون تابعی که آن را مخفی می‌کرد ctlDoc_OnViewerReady بود. سند پشت آن درست است.

فهرست کامل گزینه‌ها عبارتند از onPageLoading، onPageLoaded، onThumbnailClicked، onPageClicked، onDoubleClick، onViewerBusy، onViewerReady، onViewerError، onCopy، onAnnSaved، onAnnSaveError، onAnnLoaded، onAnnClosed، onAutoLoadStatus، onThumbsShown و onError. کال‌بکی برای کلیک روی لینک وجود ندارد: مدیریت پیوندها به‌صورت داخلی ساخته شده و توسط showHyperlinks هدایت می‌شود.

ResPath باید به پیشوند منبع اشاره کند

javascript
ResPath: '',                      // BEFORE — منابع از ریشه برنامه می‌آمدند
ResPath: 'doconut-res/images',    // NOW    — پیشوندی که UseDoconutResources() سوار می‌کند

اگر پیشوند را از طریق DoconutOptions.ResourcesPath تغییر دادید، به جای آن از همان مقدار استفاده کنید.

12. نوار ابزار Viewer، جستجو و حاشیه‌نویسی

اسکریپت‌های قدیمی را یک به یک مهاجرت نکنید. برنامه‌های مرجع فعلی یک بسته صفحه کامل را ترکیب می‌کنند:

  1. CSS Viewer و CSS جستجو/حاشیه‌نویسی دارای مجوز را با ReferenceCss صادر کنید؛
  2. نوار ابزار Viewer متعلق به برنامه را رندر کنید؛
  3. searchBarMount، annBarMount و سوار کردن مورد نیاز Viewer را رندر کنید؛
  4. اسکریپت‌های Viewer و ماژول‌های دارای مجوز را با ReferenceScripts صادر کنید؛
  5. اسکریپت کمکی نوار ابزار خود برنامه را بارگذاری کنید؛
  6. یک objViewer را مقداردهی اولیه کنید؛
  7. Ribbonهای جستجو و حاشیه‌نویسی دارای مجوز را مقداردهی اولیه کنید؛
  8. روی هر Ribbon متد attach(objViewer) را فراخوانی کنید؛
  9. سند را باز کنید و متد objViewer.View(token) را فراخوانی کنید.

جستجو و حاشیه‌نویسی ماژول‌هایی هستند که به همان Viewer متصل می‌شوند، نه نوارهای ابزار مستقل. نوار ابزار اصلی متعلق به برنامه میزبان است؛ Ribbonهای جستجو و حاشیه‌نویسی به‌صورت توکار و با قابلیت‌های محدود شده هستند.

فایل‌های کپی‌شده دستی مانند documentLinks.js و docViewer.UI.js را فقط پس از این که صفحه فعلی با منابع صادرشده توسط ReferenceCss و ReferenceScripts کار کرد، حذف کنید.

13. ثبت افزونه

متدهای استاتیک مجوز افزونه‌ها افزونه‌های فعلی را ثبت نمی‌کنند. هر بسته منتشرشده را به‌صورت صریح نصب و ثبت کنید:

csharp
builder.Services.AddDoconut(options =>
{
    options.AddPlugin<Doconut.Plugins.Converter.ConverterPlugin>();
    options.AddPlugin<Doconut.Plugins.Dicom.DicomPlugin>();
});

AddDoconut() قابلیت‌های افزونه‌های ثبت‌شده را در زمان راه‌اندازی اعتبارسنجی می‌کند. Converter و DICOM افزونه‌های .NET Standard منتشرشده هستند. جستجو و حاشیه‌نویسی معمولی ویژگی‌های دارای مجوز داخلی هستند، نه بسته‌های AddPlugin<TPlugin>().

14. امنیت نشست و سند

یکپارچه‌سازی فعلی اسناد را به توکن‌های مبهم و نشست‌های کش‌شده متصل می‌کند. با مقدار پیش‌فرض UnsafeMode = false، UseDoconut() امنیت دسترسی به سند را اضافه می‌کند و میزبان باید نشست ASP.NET را پیکربندی کند:

csharp
builder.Services.AddDistributedMemoryCache(); // وضعیت نشست به یک کش نیاز دارد (MVC/Razor Pages قبلاً یک کش اضافه کرده‌اند)
builder.Services.AddSession();
app.UseSession();

DocOptions.IsSecured = true را نگه دارید مگر اینکه طراحی بازبینی‌شده نیاز به خلاف داشته باشد. هرگز UnsafeMode = true را به‌عنوان یک میان‌بر مهاجرت در یک گره واحد استفاده نکنید. درخواست‌ها را با توکن‌نداشته، توکن خراب، توکن منقضی‌شده و توکن از نشست مرورگر متفاوت تست کنید.

در یک فارم، درخواست صفحه می‌تواند به گره‌ای برسد که هرگز درخواست اولیه را ندیده است، بنابراین بررسی بر پایه نشست آن را رد می‌کند. این همان موردی است که در آن UnsafeMode = true صحیح است — و باید همراه با بلیت‌های دسترسی امضا‌شده از طریق AddDoconutDistributedDocumentSecurity باشد، نه به‌تنهایی. ببینید استقرارهای توزیع‌شده.

نکته نام‌گذاری

در تمام فریم‌ورک‌ها کلاس Viewer است. اگر DocumentViewer را در قطعات قدیمی یا مقالات شخص ثالث پیدا کردید، آن نوع هرگز در SDK وجود نداشته است.

15. ترتیب پیشنهادی کار

  1. تأیید کنید که فریم‌ورک میزبان می‌تواند netstandard2.1 را مصرف کند. اگر نمی‌تواند، متوقف شوید.
  2. پکیج را به 26.8.0 ارتقا دهید و بسازید. خطاهای کامپایل را برطرف کنید — بیشتر آنها خطوط using در بخش §1 هستند.
  3. راه‌اندازی را به AddDoconut() منتقل کنید و فراخوانی‌های میدل‌ویر را به ترتیب جدیدشان (§3، §4) منتقل کنید.
  4. Viewer را در جایی که آن را می‌ساختید تزریق کنید، OpenDocument را به صورت async کنید و InitCache() را حذف کنید (§5، §6).
  5. اگر از فارم وب استفاده می‌کنید، بخش §7 را انجام دهید — تنها بخشی که بازطراحی است نه تغییر نام.
  6. اگر اسناد را تبدیل می‌کنید، بخش §8 را انجام دهید.
  7. بخش §11 را انجام دهید — تغییرات سمت کلاینت. این‌ها همان مواردی هستند که خودشان را اعلام نمی‌کنند.
  8. یک سند واقعی را در مرورگر باز کنید و نشانگر مشغول را مشاهده کنید. یک ساخت سبز در اینجا هیچ چیز را ثابت نمی‌کند: مجوزدهی، ثبت افزونه، مسیر منبع و فراخوانی‌های Viewer فقط در زمان اجرا شکست می‌خورند. برای یک فارم، تأیید کنید که سندی که توسط یک گره منتشر شده توسط گره دیگری سرو می‌شود.

آزمایش مهاجرت

حداقل، موارد زیر را بررسی کنید:

  • راه‌اندازی برنامه با مجوز تولید و هر افزونه ثبت‌شده؛
  • CSS/اسکریپت‌های Viewer و تمام درخواست‌های صفحه‑تصویر در مسیرهای انتخاب‌شده؛
  • باز کردن سند، ناوبری، زوم، تصویرهای بندانگشتی، چاپ، و بستن صریح؛
  • جستجو در سند حاوی متن و وضعیت غیرقابل جستجو بودن فایل فقط‑تصویری؛
  • بارگذاری، ذخیره، صادرات حاشیه‌نویسی و کنترل قابلیت‌ها؛
  • کشف هدف مبدل، خروجی، دانلود و وضعیت واترمارک؛
  • صفحات، فریم‌ها و انیمیشن DICOM؛ متادیتای فنی در این بسته در دسترس نیست؛
  • سندهای محافظت‌شده با رمز عبور، قلم‌های سفارشی، متن غیرلاتین، و زمان‌سنجی‌های پیکربندی‌شده؛
  • رد توکن‌های بین‌جلسه‌ای و رفتار جلسه منقضی‌شده؛
  • برای یک فارم: انتشار بر روی یک گره، درخواست صفحهٔ دیرهنگام از گرهٔ دیگر، و تأیید اینکه Viewer پاسخ‌های 202 را مجدداً تلاش می‌کند به‌جای نمایش کاشی‌های خراب؛
  • موبایل، حالت تاریک، و مسیر reverse‑proxy تولید.

برنامهٔ بازگشت

آثار استقرار قبلی، بسته‌های منطبق، فایل‌های مجوز و منابع مرورگر کپی‌شده را با هم نگه دارید. یک بازگشت ایمن تمام نسل برنامه را جابجا می‌کند؛ این کار سرور نسل قبلی را با اسکریپت‌های فعلی یا سرور فعلی را با نقاط فراخوانی new Viewer(...) ترکیب نمی‌کند.

قبل از انتقال، مستند کنید:

  • اسلات یا اثر استقرار مورد استفاده برای بازگشت؛
  • تأثیر ذخیره‌سازی مشترک، در صورت وجود — آثار منتشرشده توسط نسخهٔ ۲۶.۸.۰ توسط میانی‌افزار وب‑فارم قبلی مصرف نمی‌شوند؛
  • چگونگی نامعتبر شدن جلسات سند فعال؛
  • بررسی سلامت و سند smoke مورد استفاده برای تصمیم‌گیری بازگشت؛
  • کسی که می‌تواند مجموعهٔ بسته‌ها و پیکربندی قبلی را بازگرداند.

مستندات قدیمی

راهنمای ترجمه‌شدهٔ قبلی همچنان در دسترس است در راهنمای تنظیمات قدیمی .NET Standard. دروازهٔ یکپارچه‌سازی قبلی سیگنال‌های شناسایی یکسان را توضیح می‌دهد و به این راهنمای مهاجرت بازمی‌گردد.

آدرس URL تاریخی را در نشانک‌ها و تیکت‌های پشتیبانی نگه دارید تا زمانی که نصب‌های netstandard2.0 هنوز وجود دارند. این مستندات یک نسل متفاوت را توصیف می‌کند و به API فعلی هدایت نمی‌شود.

آیا این صفحه مفید بود؟