اشتراکها
سلام
مشکلی که قبلتر در حین کار با فایلهای shp برخوردم، تغییر مقیاس نقشه و نگاشت مختصات خاص و توزیع شده ای بود که باید روی نقشه نمایش داده میشد . (مثلا نمایش مراکز استانها روی نقشه و تغییر scale نقشه و به تبع اون، تغییر مکان مختصات)
برای این کار چه راهکاری هست؟
* دسترسی به فایل های dbf و prj و sbn و sbx و shx وجود دارد.
ممنون
عملیات دریافت اطلاعات راه دور، در برنامههای Angular به صورت Ajax انجام میشود. در این حالت، پردازش تصاویر دریافتی از سرور، به علت داشتن محتوای باینری، نیاز به رعایت یک سری نکات خاص دارد که آنها را در این مطلب مرور خواهیم کرد.
کدهای سمت سرور دریافت تصویر
در اینجا کدهای سمت سرور برنامه، نکتهی خاصی را به همراه نداشته و صرفا یک تصویر ساده (محتوای باینری) را بازگشت میدهد:
که در نهایت با آدرس api/ShowImages/GetImage در سمت کلاینت قابل دسترسی خواهد بود.
سرویس دریافت محتوای باینری در برنامههای Angular
برای اینکه HttpClient برنامههای Angular بتواند محتوای باینری را بجای محتوای JSON پیشفرض آن دریافت کند، نیاز است نوع خروجی سمت سرور آنرا به blob تنظیم کرد:
به این ترتیب پس از اشتراک به متد getImage این سرویس، اطلاعات باینری این تصویر را دریافت خواهیم کرد.
ساخت URL برای دسترسی به اطلاعات باینری
تمام مرورگرهای جدید از ایجاد URL برای اشیاء Blob دریافتی از سمت سرور، توسط متد توکار URL.createObjectURL پشتیبانی میکنند. این متد، شیء URL را از شیء window جاری دریافت میکند و سپس اطلاعات باینری را دریافت کرده و آدرسی را جهت دسترسی موقت به آن تولید میکند.
بنابراین ابتدا سرویس جدیدی را در مسیر src\app\core\window.service.ts جهت دسترسی به این شیء پنجره ایجاد میکنیم:
هدف این است که در برنامه مستقیما با شیء window کار نکنیم و این سرویس تامین کنندهی آن باشد.
چون این سرویس، یک سرویس سراسری است، آنرا در قسمت providers مربوط به CoreModule ثبت خواهیم کرد تا در تمام برنامه قابل دسترسی شود:
اکنون هر قسمتی از برنامه که میخواهد برای دسترسی به این تصویر و نمایش آن، آدرسی از آنرا داشته باشد، باید به صورت ذیل عمل کند:
اصلاح Content Security Policy سمت سرور جهت نمایش تصاویر blob
زمانیکه از متد createObjectURL استفاده میشود، یک نمونه آدرس تولیدی آن چنین شکلی را پیدا میکند:
در این حالت در Content Security Policy سمت سرور، نیاز است امکان دسترسی به تصاویر از نوع blob را نیز آزاد معرفی کنید:
در غیراینصورت مرورگر نمایش یک چنین تصاویری را سد خواهد کرد.
دریافت تصویر از سرور و نمایش آن در برنامهی Angular
پس از این مقدمات، کامپوننتی که یک تصویر را از سمت سرور دریافت کرده و نمایش میدهد، چنین کدی را خواهد داشت:
با این قالب:
در اینجا در ngOnInit، به سرویس پنجره دسترسی یافته و وهلهای از آنرا جهت کار با متد createObjectURL شیء URL آن دریافت میکنیم.
سپس مشترک متد getImage دریافت تصویر شده و اطلاعات نهایی آنرا به صورت imageDataBlob دریافت میکنیم.
این اطلاعات باینری را به متد createObjectURL ارسال کرده و آدرس موقتی این تصویر را در مرورگر بدست میآوریم.
در ادامه سه روش کار با این URL نهایی را بررسی کردهایم:
- دسترسی مستقیم به imageBlobUrl
و سپس انتساب آن به خاصیت src یک تصویر در قالب این کامپوننت:
چون در این حالت Angular این URL را امن سازی میکند، یک چنین خروجی unsafe:blob بجای blob تولید خواهد شد:
که این مورد نیز توسط مرورگر با خطای ذیل سد میشود:
- برای رفع این مشکل، میتوان از سرویس DomSanitizer آن که به سازندهی کلاس تزریق شدهاست استفاده کرد:
اینبار یک چنین انتسابی به صورت مستقیم کار میکند:
چون خروجی آن دیگر unsafe:blob نیست:
- روش دیگر نمایش این تصویر، انتساب این آدرس به شیء بومی این تصویر است. برای اینکار در قالب این کامپوننت، یک template reference variable را به img نسبت میدهیم:
سپس در کامپوننت جاری، توسط تعریف یک ViewChild، میتوان به این متغیر دسترسی یافت:
اکنون که دسترسی مستقیمی را به این شیء پیدا کردهایم، نحوهی مقدار دهی خاصیت src آن به صورت ذیل خواهد بود:
در نهایت Angular یک چنین خروجی را برای نمایش اینگونه تصاویر، بر اساس کدهای فوق رندر میکند:
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید.
کدهای سمت سرور دریافت تصویر
در اینجا کدهای سمت سرور برنامه، نکتهی خاصی را به همراه نداشته و صرفا یک تصویر ساده (محتوای باینری) را بازگشت میدهد:
using Microsoft.AspNetCore.Mvc; namespace AngularTemplateDrivenFormsLab.Controllers { [Route("api/[controller]")] public class ShowImagesController : Controller { [HttpGet("[action]")] public IActionResult GetImage() { return File("~/assets/resume.png", "image/png"); } } }
سرویس دریافت محتوای باینری در برنامههای Angular
برای اینکه HttpClient برنامههای Angular بتواند محتوای باینری را بجای محتوای JSON پیشفرض آن دریافت کند، نیاز است نوع خروجی سمت سرور آنرا به blob تنظیم کرد:
import { Injectable } from "@angular/core"; import { Observable } from "rxjs/Observable"; import { HttpClient } from "@angular/common/http"; @Injectable() export class DownloadBinaryDataService { constructor(private httpClient: HttpClient) { } public getImage(): Observable<Blob> { return this.httpClient.get("/api/ShowImages/GetImage", { responseType: "blob" }); } }
ساخت URL برای دسترسی به اطلاعات باینری
تمام مرورگرهای جدید از ایجاد URL برای اشیاء Blob دریافتی از سمت سرور، توسط متد توکار URL.createObjectURL پشتیبانی میکنند. این متد، شیء URL را از شیء window جاری دریافت میکند و سپس اطلاعات باینری را دریافت کرده و آدرسی را جهت دسترسی موقت به آن تولید میکند.
بنابراین ابتدا سرویس جدیدی را در مسیر src\app\core\window.service.ts جهت دسترسی به این شیء پنجره ایجاد میکنیم:
import { Injectable } from "@angular/core"; function getWindow(): any { return window; } @Injectable() export class WindowRefService { get nativeWindow(): any { return getWindow(); } }
چون این سرویس، یک سرویس سراسری است، آنرا در قسمت providers مربوط به CoreModule ثبت خواهیم کرد تا در تمام برنامه قابل دسترسی شود:
import { WindowRefService } from "./window.service"; @NgModule({ providers: [ WindowRefService ] }) export class CoreModule {}
const urlCreator = this.nativeWindow.URL; imageBlobUrl = urlCreator.createObjectURL(imageDataBlob);
اصلاح Content Security Policy سمت سرور جهت نمایش تصاویر blob
زمانیکه از متد createObjectURL استفاده میشود، یک نمونه آدرس تولیدی آن چنین شکلی را پیدا میکند:
<img src="blob:http://localhost:5000/9d4bae44-00bc-4ed8-9f27-cac2de5ecd5d">
img-src 'self' data: blob:
دریافت تصویر از سرور و نمایش آن در برنامهی Angular
پس از این مقدمات، کامپوننتی که یک تصویر را از سمت سرور دریافت کرده و نمایش میدهد، چنین کدی را خواهد داشت:
import { WindowRefService } from "./../../core/window.service"; import { DownloadBinaryDataService } from "app/angular-http-client-blob/download-binary-data.service"; import { Component, OnInit, ViewChild, ElementRef } from "@angular/core"; import { DomSanitizer, SafeUrl } from "@angular/platform-browser"; @Component({ templateUrl: "./download-blobs.component.html", styleUrls: ["./download-blobs.component.css"] }) export class DownloadBlobsComponent implements OnInit { @ViewChild("sampleImage1") sampleImage1: ElementRef; private nativeWindow: Window; imageBlobUrl: string; sanitizedImageBlobUrl: SafeUrl; constructor(private downloadService: DownloadBinaryDataService, private windowRefService: WindowRefService, private sanitizer: DomSanitizer) { } ngOnInit() { this.nativeWindow = this.windowRefService.nativeWindow; this.downloadService.getImage().subscribe(imageDataBlob => { const urlCreator = this.nativeWindow.URL; this.imageBlobUrl = urlCreator.createObjectURL(imageDataBlob); // doesn't work -> <img [src]="imageBlobUrl"> this.sampleImage1.nativeElement.src = this.imageBlobUrl; // works -> <img #sampleImage1> this.sanitizedImageBlobUrl = this.sanitizer.bypassSecurityTrustUrl(this.imageBlobUrl); // works -> <img [src]="sanitizedImageBlobUrl"> }); } }
<h1>Angular HttpClient Blob</h1> <h4>#sampleImage1</h4> <img #sampleImage1> <h4>imageBlobUrl</h4> <img [src]="imageBlobUrl"> <h4>sanitizedImageBlobUrl</h4> <img [src]="sanitizedImageBlobUrl">
سپس مشترک متد getImage دریافت تصویر شده و اطلاعات نهایی آنرا به صورت imageDataBlob دریافت میکنیم.
این اطلاعات باینری را به متد createObjectURL ارسال کرده و آدرس موقتی این تصویر را در مرورگر بدست میآوریم.
در ادامه سه روش کار با این URL نهایی را بررسی کردهایم:
- دسترسی مستقیم به imageBlobUrl
this.imageBlobUrl = urlCreator.createObjectURL(imageDataBlob); // doesn't work -> <img [src]="imageBlobUrl">
<h4>imageBlobUrl</h4> <img [src]="imageBlobUrl">
<img _ngcontent-c1="" src="unsafe:blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc">
Refused to load the image 'unsafe:blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc' because it violates the following Content Security Policy directive: "img-src 'self' data: blob:".
- برای رفع این مشکل، میتوان از سرویس DomSanitizer آن که به سازندهی کلاس تزریق شدهاست استفاده کرد:
this.sanitizedImageBlobUrl = this.sanitizer.bypassSecurityTrustUrl(this.imageBlobUrl); // works -> <img [src]="sanitizedImageBlobUrl">
<img [src]="sanitizedImageBlobUrl">
<img _ngcontent-c1="" src="blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc">
- روش دیگر نمایش این تصویر، انتساب این آدرس به شیء بومی این تصویر است. برای اینکار در قالب این کامپوننت، یک template reference variable را به img نسبت میدهیم:
<img #sampleImage1>
@ViewChild("sampleImage1") sampleImage1: ElementRef;
this.sampleImage1.nativeElement.src = this.imageBlobUrl; // works -> <img #sampleImage1>
در نهایت Angular یک چنین خروجی را برای نمایش اینگونه تصاویر، بر اساس کدهای فوق رندر میکند:
<ng-component _nghost-c1=""><h1 _ngcontent-c1="">Angular HttpClient Blob</h1> <h4 _ngcontent-c1="">#sampleImage1</h4> <img _ngcontent-c1="" src="blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc"> <h4 _ngcontent-c1="">imageBlobUrl</h4> <img _ngcontent-c1="" src="unsafe:blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc"> <h4 _ngcontent-c1="">sanitizedImageBlobUrl</h4> <img _ngcontent-c1="" src="blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc"> </ng-component>
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید.
داتنت 8 به همراه بهبودهای قابل ملاحظهای در کارآیی برنامههای داتنتی است و در این بین تعدادی قابلیت جدید را نیز به زبان سیشارپ اضافه کردهاست. در این مطلب ویژگی جدید «Alias any type» آنرا بررسی میکنیم. پیشنیاز کار با این قابلیت تنها نصب SDK داتنت 8 است.
امکان تعریف alias، قابلیت جدیدی نیست!
در نگارشهای پیشین زبان #C نیز میتوان برای نوعهای نامدار داتنت، alias/«نام مستعار» تعریف کرد؛ برای مثال:
Aliasها در قسمت using تعاریف یک کلاس معرفی میشوند و یکی از اهدف آنها، کوتاه کردن تعاریف فضاهای نام طولانی است و یا رفع تداخلها؛ همچنین تنها به Named types، محدود هستند و Named types فقط شامل این موارد میشوند: classes ،delegates ،interfaces ،records و structs
بنابراین دو حالت تعریف Namespace alias برای کوتاه سازی فضاهای نام طولانی و یا تعریف Type alias برای معرفی یک نام مستعار جدید برای نوعی مشخص، میسر است:
تنها کارکرد نامهای مستعار، کوتاه و زیبا سازی نامهای طولانی نیستند. برای مثال گاهی از اوقات ممکن است که بین نام نوعهای موجود در usingهای جاری، تداخل حاصل شود و برنامه کامپایل نشود. برای مثال فرض کنید که دو using زیر را تعریف کردهاید:
کامپایل این برنامه میسر نیست. چون هر دو نوع System.Random و UnityEngine.Random پیشتر تعریف شدهاند و در اینجا دقیقا مشخص نیست که تامین کنندهی شیء Random، کدام فضای نام است. در این حالت میتوان برای مثال در حین نمونه سازی، فضای نام را صراحتا ذکر کرد:
و یا میتوان برای آن نام مستعاری نیز تعریف کرد:
در این حالت دیگر تداخلی وجود نداشته و کامپایلر دقیقا میداند که تامین کنندهی Random، کدام کتابخانه و کدام فضای نام است.
امکان تعریف alias برای هر نوعی در C# 12.0
محدودیت امکان تعریف alias برای نوعهای نامدار داتنت در C# 12.0 برطرف شده و اکنون میتوان برای انواع و اقسام نوعها مانند آرایهها، tuples و غیره نیز alias تعریف کرد:
در اینجا امکان تعریف alias را برای آرایهها، nullable value types، نوعهای توکار، tupleها و حتی نوعهای unsafe، مشاهده میکنید.
یک نکته: امکان تعریف alias برای nullable reverence types وجود ندارد.
بررسی یک مثال C# 12.0
در اینجا محتویات یک فایل Program.cs یک برنامهی کنسول داتنت 8 را مشاهده میکنید:
در این مثال برای یک نوع tuple سفارشی، یک alias به نام Person تعریف شده و سپس از آن برای نمونه سازی یک شیء جدید و یا ارسال آن به عنوان یک پارامتر متد، استفاده شدهاست. خروجی برنامهی فوق به صورت زیر است:
چه زمانی بهتر است از قابلیت تعریف نامهای مستعار نوعها و یا فضاهای نام استفاده شود؟
اگر یک نام طولانی را بتوان به این صورت خلاصه کرد، مفید هستند؛ برای مثال ساده سازی تعریف یک لیست طولانی به صورت زیر:
و مثالی دیگر در این زمینه، کوتاه سازی تعاریف متداول جنریک طولانی است:
و یا اگر بتوانند رفع تداخلی را حاصل کنند، بکارگیری آنها ضروری است (مانند مثال شیء Random ابتدای بحث) و یا اگر بتوانند از تکرار تعریف یک tuple جلوگیری کنند، ذکر آنها یک refactoring مثبت بهشمار میرود؛ مانند مثال زیر که در آن از تعریف نوع tuple ای، دوبار استفاده شدهاست:
اما ... آیا واقعا تعاریفی مانند ذیل، مفید یا ضروری هستند؟
اینجا فقط قطعه کدی اضافی را که بیشتر سبب سردرگمی و بالا بردن درجهی پیچیدگی برنامه میشود، تولید کردهایم. خوانایی و سادگی درک برنامه در این حالت کاهش پیدا میکند.
میدان دید نامهای مستعار
به صورت پیشفرض، تمام نامهای مستعار تنها در داخل همان فایلی که تعریف شدهاند، قابل استفاده میباشند. از زمان C# 10.0 ، میتوان پیش از واژهی کلیدی using از واژهی کلیدی global نیز استفاده کرد تا تعریف آنها فقط در پروژهی جاری به صورت سراسری قابل دسترسی شود.
به همین جهت اگر نوعی قرار است در سایر پروژهها استفاده شود، بهتر است از global using استفاده نشده و از همان روشهای متداول تعریف records و یا classes استفاده شود.
امکان تعریف alias، قابلیت جدیدی نیست!
در نگارشهای پیشین زبان #C نیز میتوان برای نوعهای نامدار داتنت، alias/«نام مستعار» تعریف کرد؛ برای مثال:
using MyConsole = System.Console; MyConsole.WriteLine("Test console");
بنابراین دو حالت تعریف Namespace alias برای کوتاه سازی فضاهای نام طولانی و یا تعریف Type alias برای معرفی یک نام مستعار جدید برای نوعی مشخص، میسر است:
// Namespace alias using SuperJSON = System.Text.Json; var document = SuperJSON.JsonSerializer.Serialize("{}"); // Type alias using SuperJSON = System.Text.Json.JsonSerializer; var document = SuperJSON.Serialize("{}");
تنها کارکرد نامهای مستعار، کوتاه و زیبا سازی نامهای طولانی نیستند. برای مثال گاهی از اوقات ممکن است که بین نام نوعهای موجود در usingهای جاری، تداخل حاصل شود و برنامه کامپایل نشود. برای مثال فرض کنید که دو using زیر را تعریف کردهاید:
using UnityEngine; using System; Random rnd = new Random();
var rnd = new System.Random();
using Random = System.Random;
امکان تعریف alias برای هر نوعی در C# 12.0
محدودیت امکان تعریف alias برای نوعهای نامدار داتنت در C# 12.0 برطرف شده و اکنون میتوان برای انواع و اقسام نوعها مانند آرایهها، tuples و غیره نیز alias تعریف کرد:
using Ints = int[]; using DatabaseInt = int?; using OptionalFloat = float?; using Grade= decimal; using Point3D = (int, int, int); using Person = (string name, int age, string country); using unsafe P = char*; using Matrix = int[][]; Matrix aMatrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
یک نکته: امکان تعریف alias برای nullable reverence types وجود ندارد.
بررسی یک مثال C# 12.0
در اینجا محتویات یک فایل Program.cs یک برنامهی کنسول داتنت 8 را مشاهده میکنید:
using MyConsole = System.Console; using Person = (string name, int age, string country); Person person = new("User 1", 33, "Iran"); Console.WriteLine(person); PrintPerson(person); MyConsole.WriteLine("Test console"); static void PrintPerson(Person person) { MyConsole.WriteLine($"{person.name}, {person.age}, {person.country}"); }
(User 1, 33, Iran) User 1, 33, Iran Test console
چه زمانی بهتر است از قابلیت تعریف نامهای مستعار نوعها و یا فضاهای نام استفاده شود؟
اگر یک نام طولانی را بتوان به این صورت خلاصه کرد، مفید هستند؛ برای مثال ساده سازی تعریف یک لیست طولانی به صورت زیر:
using Companies = System.Collections.Generic.List<Company>; Companies GetCompanies() { // logic here } class Company { public string Name; public int Id; }
using EventHandlers = System.Collections.Generic.IEnumerable<System.Func<System.Threading.Tasks.Task>>;
و یا اگر بتوانند رفع تداخلی را حاصل کنند، بکارگیری آنها ضروری است (مانند مثال شیء Random ابتدای بحث) و یا اگر بتوانند از تکرار تعریف یک tuple جلوگیری کنند، ذکر آنها یک refactoring مثبت بهشمار میرود؛ مانند مثال زیر که در آن از تعریف نوع tuple ای، دوبار استفاده شدهاست:
using Country = (string Abbreviation, string Name); Country GetCountry(string abbreviation) { // Logic here } List<Country> GetCountries() { // Logic here }
using Ints = int[]; using DatabaseInt = int?; using OptionalFloat = float?;
میدان دید نامهای مستعار
به صورت پیشفرض، تمام نامهای مستعار تنها در داخل همان فایلی که تعریف شدهاند، قابل استفاده میباشند. از زمان C# 10.0 ، میتوان پیش از واژهی کلیدی using از واژهی کلیدی global نیز استفاده کرد تا تعریف آنها فقط در پروژهی جاری به صورت سراسری قابل دسترسی شود.
به همین جهت اگر نوعی قرار است در سایر پروژهها استفاده شود، بهتر است از global using استفاده نشده و از همان روشهای متداول تعریف records و یا classes استفاده شود.
اگر به دو مطلب استفاده از Quartz.Net (^ و ^) و خصوصا نظرات آن دقت کرده باشید به این نتیجه خواهید رسید که ... این کتابخانهی در اصل جاوایی گنگ طراحی شدهاست. در سایت جاری برای انجام کارهای زمانبندی شده (مانند ارسال ایمیلهای روزانه خلاصه مطالب، تهیه خروجی PDF و XML سایت، تبدیل پیش نویسها به مطالب، بازسازی ایندکسهای جستجو و امثال آن) از یک Thread timer استفاده میشود که حجم نهایی کتابخانهی محصور کننده و مدیریت کنندهی وظایف آن جمعا 8 کیلوبایت است؛ متشکل از ... سه کلاس. در ادامه کدهای کامل و نحوهی استفاده از آن را بررسی خواهیم کرد.
دریافت کتابخانه DNT Scheduler و مثال آن
DNTScheduler
در این بسته، کدهای کتابخانهی DNT Scheduler و یک مثال وب فرم را، ملاحظه خواهید کرد. از این جهت که برای ثبت وظایف این کتابخانه، از فایل global.asax.cs استفاده میشود، اهمیتی ندارد که پروژهی شما وب فرم است یا MVC. با هر دو حالت کار میکند.
نحوهی تعریف یک وظیفهی جدید
کار با تعریف یک کلاس و پیاده سازی ScheduledTaskTemplate شروع میشود:
برای نمونه :
- در اینجا Order، ترتیب اجرای وظیفهی جاری را در مقایسه با سایر وظیفههایی که قرار است در یک زمان مشخص اجرا شوند، مشخص میکند.
- متد RunAt ثانیهای یکبار فراخوانی میشود (بنابراین بررسی now.Second را فراموش نکنید). زمان ارسالی به آن UTC است و اگر برای نمونه میخواهید بر اساس ساعت ایران کار کنید باید 3.5 ساعت به آن اضافه نمائید. این مساله برای سرورهایی که خارج از ایران قرار دارند مهم است. چون زمان محلی آنها برای تصمیم گیری در مورد زمان اجرای کارها مفید نیست.
در متد RunAt فرصت خواهید داشت تا منطق زمان اجرای وظیفهی جاری را مشخص کنید. برای نمونه در مثال فوق، این وظیفه هر دو دقیقه یکبار اجرا میشود. یا اگر خواستید اجرای آن فقط در سال 23 و 33 دقیقه هر روز باشد، تعریف آن به نحو ذیل خواهد بود:
- خاصیت IsShuttingDown موجود در کلاس پایه ScheduledTaskTemplate، توسط کتابخانهی DNT Scheduler مقدار دهی میشود. این کتابخانه قادر است زمان خاموش شدن پروسهی فعلی IIS را تشخیص داده و خاصیت IsShuttingDown را true کند. بنابراین در حین اجرای وظیفهای مشخص، به مقدار IsShuttingDown دقت داشته باشید. اگر true شد، یعنی فقط 30 ثانیه وقت دارید تا کار را تمام کنید.
خاصیت Pause هر وظیفه را برنامه میتواند تغییر دهد. به این ترتیب در مورد توقف یا ادامهی یک وظیفه میتوان تصمیم گیری کرد. خاصیت ScheduledTasksCoordinator.Current.ScheduledTasks، لیست وظایف تعریف شده را در اختیار شما قرار میدهد.
- در متد Run، منطق وظیفهی تعریف شده را باید مشخص کرد. برای مثال ارسال ایمیل یا تهیهی بک آپ.
- Name نیز نام وظیفهی جاری است که میتواند در گزارشات مفید باشد.
همین مقدار برای تعریف یک وظیفه کافی است.
نحوهی ثبت و راه اندازی وظایف تعریف شده
پس از اینکه چند وظیفه را تعریف کردیم، برای مدیریت بهتر آنها میتوان یک کلاس ثبت و معرفی کلی را مثلا به نام ScheduledTasksRegistry ایجاد کرد:
- شیء ScheduledTasksCoordinator.Current، نمایانگر تنها وهلهی مدیریت وظایف برنامه است.
- توسط متد ScheduledTasksCoordinator.Current.AddScheduledTasks، تنها کافی است کلاسهای وظایف مشتق شده از ScheduledTaskTemplate، معرفی شوند.
- به کمک متد ScheduledTasksCoordinator.Current.Start، کار Thread timer برنامه شروع میشود.
- اگر در حین اجرای متد Run، استثنایی رخ دهد، آنرا توسط یک Action delegate به نام ScheduledTasksCoordinator.Current.OnUnexpectedException میتوانید دریافت کنید. کتابخانهی DNT Scheduler برای اجرای وظایف، از یک ترد با سطح تقدم Below normal استفاده میکند تا در حین اجرای وظایف، برنامهی جاری با اخلال و کندی مواجه نشده و بتواند به درخواستهای رسیده پاسخ دهد. در این بین اگر استثنایی رخ دهد، میتواند کل پروسهی IIS را خاموش کند. به همین جهت این کتابخانه کار try/catch استثناهای متد Run را نیز انجام میدهد تا از این لحاظ مشکلی نباشد.
- متد ScheduledTasksCoordinator.Current.Dispose کار مدیر وظایف برنامه را خاتمه میدهد.
- از متد WakeUp تعریف شده میتوان برای بیدار کردن مجدد برنامه استفاده کرد.
استفاده از کلاس ScheduledTasksRegistry تعریف شده
پس از اینکه کلاس ScheduledTasksRegistry را تعریف کردیم، نیاز است آنرا به فایل استاندارد global.asax.cs برنامه به نحو ذیل معرفی کنیم:
- متد ScheduledTasksRegistry.Init در حین آغاز برنامه فراخوانی میشود.
- متد ScheduledTasksRegistry.End در پایان کار برنامه جهت پاکسازی منابع باید فراخوانی گردد.
همچنین در اینجا با فراخوانی ScheduledTasksRegistry.WakeUp، میتوانید برنامه را مجددا زنده کنید! IIS مجاز است یک سایت ASP.NET را پس از مثلا 20 دقیقه عدم فعالیت (فعالیت به معنای درخواستهای رسیده به سایت است و نه کارهای پس زمینه)، از حافظه خارج کند (این عدد در application pool برنامه قابل تنظیم است). در اینجا در فایل web.config برنامه میتوانید آدرس یکی از صفحات سایت را برای فراخوانی مجدد تعریف کنید:
همینکه درخواست مجددی به این صفحه برسد، مجددا برنامه توسط IIS بارگذاری شده و اجرا میگردد. به این ترتیب وظایف تعریف شده، در طول یک روز بدون مشکل کار خواهند کرد.
گزارشگیری از وظایف تعریف شده
برای دسترسی به کلیه وظایف تعریف شده، از خاصیت ScheduledTasksCoordinator.Current.ScheduledTasks استفاده نمائید:
لیست حاصل را به سادگی میتوان در یک Grid نمایش داد.
دریافت کتابخانه DNT Scheduler و مثال آن
DNTScheduler
در این بسته، کدهای کتابخانهی DNT Scheduler و یک مثال وب فرم را، ملاحظه خواهید کرد. از این جهت که برای ثبت وظایف این کتابخانه، از فایل global.asax.cs استفاده میشود، اهمیتی ندارد که پروژهی شما وب فرم است یا MVC. با هر دو حالت کار میکند.
نحوهی تعریف یک وظیفهی جدید
کار با تعریف یک کلاس و پیاده سازی ScheduledTaskTemplate شروع میشود:
public class SendEmailsTask : ScheduledTaskTemplate
using System; namespace DNTScheduler.TestWebApplication.WebTasks { public class SendEmailsTask : ScheduledTaskTemplate { /// <summary> /// اگر چند جاب در یک زمان مشخص داشتید، این خاصیت ترتیب اجرای آنها را مشخص خواهد کرد /// </summary> public override int Order { get { return 1; } } public override bool RunAt(DateTime utcNow) { if (this.IsShuttingDown || this.Pause) return false; var now = utcNow.AddHours(3.5); return now.Minute % 2 == 0 && now.Second == 1; } public override void Run() { if (this.IsShuttingDown || this.Pause) return; System.Diagnostics.Trace.WriteLine("Running Send Emails"); } public override string Name { get { return "ارسال ایمیل"; } } } }
- متد RunAt ثانیهای یکبار فراخوانی میشود (بنابراین بررسی now.Second را فراموش نکنید). زمان ارسالی به آن UTC است و اگر برای نمونه میخواهید بر اساس ساعت ایران کار کنید باید 3.5 ساعت به آن اضافه نمائید. این مساله برای سرورهایی که خارج از ایران قرار دارند مهم است. چون زمان محلی آنها برای تصمیم گیری در مورد زمان اجرای کارها مفید نیست.
در متد RunAt فرصت خواهید داشت تا منطق زمان اجرای وظیفهی جاری را مشخص کنید. برای نمونه در مثال فوق، این وظیفه هر دو دقیقه یکبار اجرا میشود. یا اگر خواستید اجرای آن فقط در سال 23 و 33 دقیقه هر روز باشد، تعریف آن به نحو ذیل خواهد بود:
public override bool RunAt(DateTime utcNow) { if (this.IsShuttingDown || this.Pause) return false; var now = utcNow.AddHours(3.5); return now.Hour == 23 && now.Minute == 33 && now.Second == 1; }
خاصیت Pause هر وظیفه را برنامه میتواند تغییر دهد. به این ترتیب در مورد توقف یا ادامهی یک وظیفه میتوان تصمیم گیری کرد. خاصیت ScheduledTasksCoordinator.Current.ScheduledTasks، لیست وظایف تعریف شده را در اختیار شما قرار میدهد.
- در متد Run، منطق وظیفهی تعریف شده را باید مشخص کرد. برای مثال ارسال ایمیل یا تهیهی بک آپ.
- Name نیز نام وظیفهی جاری است که میتواند در گزارشات مفید باشد.
همین مقدار برای تعریف یک وظیفه کافی است.
نحوهی ثبت و راه اندازی وظایف تعریف شده
پس از اینکه چند وظیفه را تعریف کردیم، برای مدیریت بهتر آنها میتوان یک کلاس ثبت و معرفی کلی را مثلا به نام ScheduledTasksRegistry ایجاد کرد:
using System; using System.Net; namespace DNTScheduler.TestWebApplication.WebTasks { public static class ScheduledTasksRegistry { public static void Init() { ScheduledTasksCoordinator.Current.AddScheduledTasks( new SendEmailsTask(), new DoBackupTask()); ScheduledTasksCoordinator.Current.OnUnexpectedException = (exception, scheduledTask) => { //todo: log the exception. System.Diagnostics.Trace.WriteLine(scheduledTask.Name + ":" + exception.Message); }; ScheduledTasksCoordinator.Current.Start(); } public static void End() { ScheduledTasksCoordinator.Current.Dispose(); } public static void WakeUp(string pageUrl) { try { using (var client = new WebClient()) { client.Credentials = CredentialCache.DefaultNetworkCredentials; client.Headers.Add("User-Agent", "ScheduledTasks 1.0"); client.DownloadData(pageUrl); } } catch (Exception ex) { //todo: log ex System.Diagnostics.Trace.WriteLine(ex.Message); } } } }
- توسط متد ScheduledTasksCoordinator.Current.AddScheduledTasks، تنها کافی است کلاسهای وظایف مشتق شده از ScheduledTaskTemplate، معرفی شوند.
- به کمک متد ScheduledTasksCoordinator.Current.Start، کار Thread timer برنامه شروع میشود.
- اگر در حین اجرای متد Run، استثنایی رخ دهد، آنرا توسط یک Action delegate به نام ScheduledTasksCoordinator.Current.OnUnexpectedException میتوانید دریافت کنید. کتابخانهی DNT Scheduler برای اجرای وظایف، از یک ترد با سطح تقدم Below normal استفاده میکند تا در حین اجرای وظایف، برنامهی جاری با اخلال و کندی مواجه نشده و بتواند به درخواستهای رسیده پاسخ دهد. در این بین اگر استثنایی رخ دهد، میتواند کل پروسهی IIS را خاموش کند. به همین جهت این کتابخانه کار try/catch استثناهای متد Run را نیز انجام میدهد تا از این لحاظ مشکلی نباشد.
- متد ScheduledTasksCoordinator.Current.Dispose کار مدیر وظایف برنامه را خاتمه میدهد.
- از متد WakeUp تعریف شده میتوان برای بیدار کردن مجدد برنامه استفاده کرد.
استفاده از کلاس ScheduledTasksRegistry تعریف شده
پس از اینکه کلاس ScheduledTasksRegistry را تعریف کردیم، نیاز است آنرا به فایل استاندارد global.asax.cs برنامه به نحو ذیل معرفی کنیم:
using System; using System.Configuration; using DNTScheduler.TestWebApplication.WebTasks; namespace DNTScheduler.TestWebApplication { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { ScheduledTasksRegistry.Init(); } protected void Application_End() { ScheduledTasksRegistry.End(); //نکته مهم این روش نیاز به سرویس پینگ سایت برای زنده نگه داشتن آن است ScheduledTasksRegistry.WakeUp(ConfigurationManager.AppSettings["SiteRootUrl"]); } } }
- متد ScheduledTasksRegistry.End در پایان کار برنامه جهت پاکسازی منابع باید فراخوانی گردد.
همچنین در اینجا با فراخوانی ScheduledTasksRegistry.WakeUp، میتوانید برنامه را مجددا زنده کنید! IIS مجاز است یک سایت ASP.NET را پس از مثلا 20 دقیقه عدم فعالیت (فعالیت به معنای درخواستهای رسیده به سایت است و نه کارهای پس زمینه)، از حافظه خارج کند (این عدد در application pool برنامه قابل تنظیم است). در اینجا در فایل web.config برنامه میتوانید آدرس یکی از صفحات سایت را برای فراخوانی مجدد تعریف کنید:
<?xml version="1.0"?> <configuration> <appSettings> <add key="SiteRootUrl" value="http://localhost:10189/Default.aspx" /> </appSettings> </configuration>
گزارشگیری از وظایف تعریف شده
برای دسترسی به کلیه وظایف تعریف شده، از خاصیت ScheduledTasksCoordinator.Current.ScheduledTasks استفاده نمائید:
var jobsList = ScheduledTasksCoordinator.Current.ScheduledTasks.Select(x => new { TaskName = x.Name, LastRunTime = x.LastRun, LastRunWasSuccessful = x.IsLastRunSuccessful, IsPaused = x.Pause, }).ToList();
نظرات اشتراکها
سرویس دهی رایگان Google Maps از فردا خاتمه مییابد
سرویس نقشه گوگل به سه دسته کلی maps, routes, places تقسیم شده که تحت عنوان Google Map Platform کار خواهند کرد و شامل قوانین جدید گوگل برای نقشه میشه
نظرات اشتراکها
سری برنامه نویسی ویندوز 8 با سیشارپ
برای برنامه نویسی ویندوز 8 باید حتماً Developer License داشت؟
اشتراکها
کتاب و ویدیو برای اموزش برنامه نویسی
اشتراکها