اشتراک‌ها
1.Visual Studio 2017 15.7 منتشر شد

These are the customer-reported issues addressed in 15.7.1:

  • This release includes a fix that reduces memory usage and GC pressure during solution load.

Microsoft Security Advisory for .NET Core Denial Of Service Vulnerability

CVE-2018-0765

Microsoft is releasing this security advisory to provide information about a vulnerability in .NET Core and .NET native version 2.0. This advisory also provides guidance on what developers can do to update their applications to remove this vulnerability.

Microsoft is aware of a denial of service vulnerability that exists when .NET Framework and .NET Core improperly process XML documents. An attacker who successfully exploited this vulnerability could cause a denial of service against a .NET Framework, .NET Core, or .NET native application.

The update addresses the vulnerability by correcting how .NET Framework, .NET Core, and .NET native applications handle XML document processing.

If your application is an ASP.NET Core application, developers are also advised to update to ASP.NET Core 2.0.8. 

1.Visual Studio 2017 15.7 منتشر شد
مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت یازدهم - کار با فرم‌ها - قسمت دوم
در قسمت قبل، فر‌مهای template driven را بررسی کردیم. همانطور که مشاهده کردید، این نوع فرم‌ها، قابلیت‌های اعتبارسنجی پیشرفته‌ای را به همراه ندارند. برای فرم‌هایی که نیاز به اعتبارسنجی‌های سفارشی دارند، فرم‌های model driven پیشنهاد می‌شوند که در این قسمت بررسی خواهند شد.


طراحی فرم ثبت نام کاربران در سایت با روش model driven

در این قسمت قصد داریم فرم ثبت نام کاربران را به همراه اعتبارسنجی‌های پیشرفته‌ای پیاده سازی کنیم. به همین منظور، ابتدا پوشه‌ی جدید App\users را به مثال سری جاری اضافه کنید و سپس سه فایل user.ts، signup-form.component.ts و signup-form.component.html را به آن اضافه نمائید.
فایل user.ts بیانگر مدل کاربران سایت است؛ با این محتوا:
export interface IUser {
    id: number;
    name: string;
    email: string;
    password: string;
}

قالب فرم یا signup-form.component.html، در حالت ابتدایی آن چنین شکل استانداردی را خواهد داشت و فاقد اعتبارسنجی خاصی است:
<form>
    <div class="form-group">
        <label form="name">Username</label>
        <input id="name" type="text" class="form-control" />
    </div>
    <div class="form-group">
        <label form="email">Email</label>
        <input id="email" type="text" class="form-control" />
    </div>
    <div class="form-group">
        <label form="password">Password</label>
        <input id="password" type="password" class="form-control" />
    </div>
    <button class="btn btn-primary" type="submit">Submit</button>
</form>
اکنون می‌خواهیم این فرم را به یک فرم AngularJS 2.0 ارتقاء دهیم. بنابراین نیاز است اشیاء Control و ControlGroup را ایجاد کنیم و اینبار نمی‌خواهیم AngularJS 2.0 مانند قسمت قبلی، به صورت خودکار (و ضمنی)، این اشیاء را برای ما ایجاد کند. می‌خواهیم آن‌ها را با کدنویسی (به صورت صریح) ایجاد کنیم تا بتوانیم بر روی آن‌ها کنترل بیشتری داشته باشیم.
بنابراین ابتدا کلاس کامپوننت این فرم را در فایل signup-form.component.ts به نحو ذیل تکمیل کنید:
import { Component } from '@angular/core';
import { Control, ControlGroup, Validators } from '@angular/common';
 
@Component({
    selector: 'signup-form',
    templateUrl: 'app/users/signup-form.component.html'
})
export class SignupFormComponent {
    form = new ControlGroup({
        name: new Control('', Validators.required),
        email: new Control('', Validators.required),
        password: new Control('', Validators.required)
    });
 
    onSubmit(): void {
        console.log(this.form.value);
    }
}
و همچنین پیام‌های اعتبارسنجی اولیه را نیز به نحو زیر به فایل signup-form.component.html اضافه می‌کنیم:
<form [ngFormModel]="form" (ngSubmit)="onSubmit()">
    <div class="form-group">
        <label form="name">Username</label>
        <input id="name" type="text" class="form-control"
               ngControl="name"/>
        <label class="text-danger" *ngIf="!form.controls['name'].valid">
            Username is required.
        </label>
    </div>
    <div class="form-group">
        <label form="email">Email</label>
        <input id="email" type="text" class="form-control"
               ngControl="email" #email="ngForm"/>
        <label class="text-danger" *ngIf="email.touched && !email.valid">
            Email is required.
        </label>
    </div>
    <div class="form-group">
        <label form="password">Password</label>
        <input id="password" type="password" class="form-control"
               ngControl="password" #password="ngForm"/>
        <label class="text-danger" *ngIf="password.touched && !password.valid">
            Password is required.
        </label>
    </div>
    <button class="btn btn-primary" type="submit">Submit</button>
</form>
توضیحات:
تفاوت مهم این فرم و اعتبارسنجی‌هایش با قسمت قبل، در ایجاد اشیاء Control و ControlGroup به صورت صریح است:
form = new ControlGroup({
    name: new Control('', Validators.required),
    email: new Control('', Validators.required),
    password: new Control('', Validators.required)
});
کلا‌س‌های Control، ControlGroup و Validators در ماژول angular/common@ تعریف شده‌اند. بنابراین import متناظری نیز به ابتدای فایل اضافه شده‌است:
 import { Control, ControlGroup, Validators } from '@angular/common';

یک نکته
اگر محل قرارگیری کلاسی را فراموش کردید، آن‌را در مستندات AngularJS 2.0 ذیل قسمت API Review جستجو کنید. نتیجه‌ی جستجو، به همراه نام ماژول کلاس‌ها نیز می‌باشد.


خاصیت عمومی form که با new ControlGroup تعریف شده‌است، حاوی تعاریف صریح کنترل‌های موجود در فرم خواهد بود. در اینجا سازنده‌ی ControlGroup، یک شیء را می‌پذیرد که کلیدهای آن، همان نام کنترل‌های تعریف شده‌ی در قالب فرم و مقدار هر کدام، یک Control جدید است که پارامتر اول آن یک مقدار پیش فرض و پارامتر دوم، اعتبارسنجی مرتبطی را تعریف می‌کند (این اعتبارسنجی معرفی شده، یک متد استاتیک در کلاس توکار Validators است).
بنابراین چون سه المان ورودی، در فرم جاری تعریف شده‌اند، سه کلید جدید هم نام نیز در پارامتر ورودی ControlGroup ذکر گردیده‌اند.

اکنون که خاصیت عمومی form، در کلاس کامپوننت فوق تعریف شد، آن‌را در قالب فرم، به ngFormModel بایند می‌کنیم:
<form [ngFormModel]="form" (ngSubmit)="onSubmit()">
به این ترتیب به AngularJS 2.0 اعلام می‌کنیم که ControlGroup و Controlهای آن‌را به صورت صریح ایجاد کرده‌ایم و بجای وهله‌‌های پیش فرض خود، از خاصیت عمومی form کلاس کامپوننت، این مقادیر را تامین کن.
مراحل بعد آن، با مراحلی که در قسمت قبل بررسی کردیم، تفاوتی ندارند:
الف) در اینجا به هر المان موجود، یک ngControl نسبت داده شده‌است تا هر المان را تبدیل به یک کنترل AngularJS2 2.0 کند.
ب) به هر المان، یک متغیر محلی شروع شده با # نسبت داده شده‌است تا با اتصال آن به ngForm بتوان به ngControl تعریف شده دسترسی پیدا کرد.
البته اکنون می‌توان از خاصیت form متصل به ngFormModel نیز بجای تعریف این متغیر محلی، به نحو ذیل استفاده کرد:
 <label class="text-danger" *ngIf="!form.controls['name'].valid">
ج) از این متغیر محلی جهت نمایش یا عدم نمایش پیام‌های خطای اعتبارسنجی، در ngIfهای تعریف شده، استفاده شده‌است.
د) و در آخر متد onSumbit موجود در کلاس کامپوننت را به رخداد ngSubmit متصل کرده‌ایم. همانطور که ملاحظه می‌کنید اینبار دیگر پارامتری را به آن ارسال نکرده‌ایم. از این جهت که خاصیت form موجود در سطح کلاس، اطلاعات کاملی را از اشیاء موجود در آن دارد و در متد onSubmit کلاس، به آن دسترسی داریم.
    onSubmit(): void {
        console.log(this.form.value);
    }
this.form.value حاوی یک شیء است که تمام مقادیر پر شده‌ی فرم را به همراه دارد.

بنابراین تا اینجا تنها تفاوت فرم جدید تعریف شده با قسمت قبل، تعریف صریح ControlGroup و کنتر‌ل‌های آن در کلاس کامپوننت و اتصال آن به ngFormModel است. به این نوع فرم‌ها، فرم‌های model driven هم می‌گویند.


نمایش فرم افزودن کاربران توسط سیستم Routing

با نحوه‌ی تعریف مسیریابی‌ها در قسمت نهم آشنا شدیم. برای نمایش فرم افزودن کاربران، می‌توان تغییرات ذیل را به فایل app.component.ts اعمال کرد:
//same as before...
import { SignupFormComponent } from './users/signup-form.component';
 
@Component({
    //same as before…
    template: `
                //same as before…                    
                <li><a [routerLink]="['AddUser']">Add User</a></li>
               //same as before…
    `,
    //same as before…
})
@RouteConfig([
    //same as before…    
    { path: '/adduser', name: 'AddUser', component: SignupFormComponent }
])
//same as before...
ابتدا به RouteConfig، مسیریابی کامپوننت فرم افزودن کاربران، اضافه شده‌است. سپس ماژول این کلاس در ابتدای فایل import شده و در آخر routerLink آن به قالب سایت و منوی بالای سایت اضافه شده‌است.


معرفی کلاس FormBuilder

روش دیگری نیز برای ساخت ControlGroup و کنترل‌های آن با استفاده از کلاس و سرویس فرم ساز توکار AngularJS 2.0 وجود دارد:
import { Control, ControlGroup, Validators, FormBuilder } from '@angular/common';

form: ControlGroup;
 
constructor(formBuilder: FormBuilder) {
    this.form = formBuilder.group({
        name: ['', Validators.required],
        email: ['', Validators.required],
        password: ['', Validators.required]
    });
}
کلاس و سرویس FormBuilder نیز در ماژول angular/common@ قرار دارد. برای استفاده‌ی از آن، آن‌را در سازنده‌ی کلاس تزریق کرده و سپس از متد group آن استفاده می‌کنیم. نحوه‌ی تعریف کلی اعضای آن با اعضای ControlGroup یکی است؛ با این تفاوت که اینبار بجای ذکر new Control، یک آرایه تعریف می‌شود که دقیقا اعضای آن، همان پارامترهای شیء کنترل هستند. این روش در کل خلاصه‌تر است و در آن تعریف چندین گروه مختلف، ساده‌تر می‌باشد. همچنین با روش تزریق وابستگی‌های بکار رفته‌ی در این فریم ورک نیز سازگاری بیشتری دارد.


پیاده سازی اعتبارسنجی سفارشی

فرض کنید می‌خواهیم ورود نام کاربر‌های دارای فاصله را غیر معتبر اعلام کنیم. برای این منظور فایل جدید usernameValidators.ts را به پوشه‌ی app\users اضافه کنید؛ با این محتوا:
import { Control } from '@angular/common';
 
export class UsernameValidators {
    static cannotContainSpace(control: Control) {
        if (control.value.indexOf(' ') >= 0) {
            return { cannotContainSpace: true };
        }
        return null;
    }
}
کلاس UsernameValidators می‌تواند شامل تمام اعتبارسنجی‌های سفارشی خاصیت نام کاربری باشد. به همین جهت نام آن جمع است و به s ختم شد‌ه‌است.
هر متد پیاده سازی کننده‌ی یک اعتبار سنجی سفارشی در این کلاس، استاتیک تعریف می‌شود؛ با نام دلخواهی که مدنظر است.
پارامتر ورودی این متدهای استاتیک، یک وهله از شیء کنترل است که توسط آن می‌توان برای مثال به خاصیت value آن دسترسی یافت و بر این اساس منطق اعتبارسنجی خود را پیاده سازی نمود. به همین جهت import آن نیز به ابتدای فایل جاری اضافه شده‌است.
خروجی این متد دو حالت دارد:
الف) اگر null باشد، یعنی اعتبارسنجی موفقیت آمیز بوده‌است.
ب) اگر اعتبارسنجی با شکست مواجه شود، خروجی این متد یک شیء خواهد بود که کلید آن، نام اعتبارسنجی مدنظر است و مقدار این کلید، هر چیزی می‌تواند باشد؛ یک true و یا یک شیء دیگر که اطلاعات بیشتری را در مورد این شکست ارائه دهد.

برای مثال اگر اعتبارسنج توکار required با شکست مواجه شود، یک چنین شی‌ءایی را بازگشت می‌دهد:
 { required:true }
و یا اگر اعتبارسنج minlength باشکست مواجه شود، اطلاعات بیشتری را در قسمت مقدار این کلید بازگشتی، ارائه می‌دهد:
{
  minlength : {
     requiredLength : 3,
     actualLength : 1
  }
}
در کل اینکه چه چیزی را بازگشت دهید، بستگی به طراحی مدنظر شما دارد.

پس از پیاده سازی یک اعتبارسنجی سفارشی، برای استفاده‌ی از آن، ابتدا ماژول آن‌را به ابتدای ماژول signup-form.component.ts اضافه می‌کنیم:
 import { UsernameValidators } from './usernameValidators';
پس از آن، شبیه به افزودن متد استاتیک توکار Validators.required، این متد جدید را به لیست اعتبارسنجی‌های خاصیت name اضافه می‌کنیم. از آنجائیکه پیشتر المان دوم این آرایه مقدار دهی شده‌است، برای ترکیب چندین اعتبارسنجی با هم، از متد Validators.compose که آرایه‌ای از متدهای اعتبارسنجی را قبول می‌کند، کمک خواهیم گرفت:
 name: ['', Validators.compose([Validators.required, UsernameValidators.cannotContainSpace])],

و مرحله‌ی آخر، نمایش یک پیام اعتبارسنجی مناسب و متناظر با متد cannotContainSpace است. برای این منظور فایل signup-form.component.html را گشوده و تغییرات ذیل را اعمال کنید:
<div class="form-group">
    <label form="name">Username</label>
    <input id="name" type="text" class="form-control"
           ngControl="name"
           #name="ngForm" />
    <div *ngIf="name.touched && name.errors">
        <label class="text-danger" *ngIf="name.errors.required">
            Username is required.
        </label>
        <label class="text-danger" *ngIf="name.errors.cannotContainSpace">
            Username can't contain space.
        </label>
    </div>
</div>
همانطور که در قسمت قبل نیز عنوان شد، چون اکنون به یک المان، بیش از یک اعتبارسنجی اعمال شده‌است، استفاده از خاصیت valid، بیش از اندازه عمومی بوده و باید از خاصیت errors استفاده کرد. به همین جهت این دو اعتبارسنجی را در یک div محصور کننده قرار می‌دهیم و در صورت وجود خطایی، خاصیت name.errors، دیگر نال نبوده و دو برچسب قرار گرفته‌ی در آن بر اساس شرط‌های ngIf آن، پردازش خواهند شد.
نام خاصیت بازگشت داده شده‌ی در اعتبارسنجی سفارشی، به عنوان یک خاصیت جدید شیء errors قابل استفاده است؛ مانند name.errors.cannotContainSpace.

به عنوان تمرین ماژول جدید emailValidators.ts را افزوده و سپس اعتبارسنجی سفارشی بررسی معتبر بودن ایمیل وارد شده را تعریف کنید:
import {Control} from '@angular/common';
 
export class EmailValidators {
    static email(control: Control) {
        var regEx = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
        var valid = regEx.test(control.value);
        return valid ? null : { email: true };
    }
}
در ادامه آن‌را به لیست formBuilder.group افزوده و همچنین پیام اعتبارسنجی ویژه‌ای را نیز به قالب فرم اضافه کنید (کدهای کامل آن، در فایل zip انتهای بحث موجود است).


یک نکته

اگر نیاز است از regular expressions مانند مثال فوق استفاده شود، می‌توان از متد توکار Validators.pattern نیز استفاده کرد و نیازی به تعریف یک متد جداگانه برای آن وجود ندارد؛ مگر اینکه نیاز به بازگشت شیء خطای کاملتری با خواص بیشتری وجود داشته باشد.


اعتبارسنجی async یا اعتبارسنجی از راه دور (remote validation)

یک سری از اعتبارسنجی‌ها را در سمت کلاینت می‌توان تکمیل کرد؛ مانند بررسی معتبر بودن فرمت ایمیل وارده. اما تعدادی دیگر، نیاز به اطلاعاتی از سمت سرور دارند. برای مثال آیا نام کاربری در حال ثبت، تکراری است یا خیر؟ این نوع اعتبارسنجی‌ها در رده‌ی async validation قرار می‌گیرند.
سازنده‌ی شیء Control در AngularJS 2.0 که در مثال‌های بالا نیز مورد استفاده قرار گرفت، پارامتر اختیاری سومی را نیز دارد که یک AsyncValidatorFn را قبول می‌کند:
 control(value: Object, validator?: ValidatorFn, asyncValidator?: AsyncValidatorFn) : Control
پیاده سازی async validators، بسیار شبیه به سایر اعتبارسنج‌ها هستند. اما از آنجائیکه نیاز به کار با سرور را دارند، استاتیک تعریف کردن آن‌ها، سبب قطع شدن دسترسی آن‌ها از context کلاس جاری شده و امکان تزریق وابستگی‌ها را از دست خواهیم داد. برای مثال دیگر نمی‌توان به سادگی، سرویس دریافت اطلاعات کاربران را در اینجا تزریق کرد. یک راه حل رفع این مشکل، تعریف همان متد اعتبارسنج در کلاس کامپوننت فرم است:
nameShouldBeUnique(control: Control) {
    let name: string = control.value;
    return new Promise((resolve) => {
        this._userService.isUserNameUnique(<IUser>{ "name": name }).subscribe(
            (result: IResult) => {
                resolve(                    
                    result.result ? null : { 'nameShouldBeUnique': true }
                );
            },
            error => {
                resolve(null);
            }
        );
    });
}
و سپس فراخوانی آن به صورت ذیل، به عنوان سومین عنصر آرایه‌ی تعریف شده:
this.form = _formBuilder.group({
    name: ['', Validators.compose([
        Validators.required,
        UsernameValidators.cannotContainSpace
    ]),
        (control: Control) => this.nameShouldBeUnique(control)],
در اینجا با استفاده از arrow functions، امکان دسترسی به این متد تعریف شده‌ی در سطح کلاس، که استاتیک هم نیست، وجود خواهد داشت. به این ترتیب دیگر context کلاس را از دست نداده‌ایم و اینبار می‌توان به this._userService، که آن‌را در ادامه تکمیل خواهیم کرد، بدون مشکلی دسترسی یافت.
امضای متد nameShouldBeUnique تفاوتی با سایر متدهای اعتبارسنج نداشته و پارامتر ورودی آن، همان کنترل است که توسط آن می‌توان به مقدار وارد شده‌ی توسط کاربر دسترسی یافت. اما تفاوت اصلی آن در اینجا است که این متد باید یک شیء Promise را بازگشت دهد. یک Promise، بیانگر نتیجه‌ی یک عملیات async است. در اینجا دو حالت resolve و error را باید پیاده سازی کرد. در حالت error، یعنی عملیات async صورت گرفته با شکست مواجه شده‌است و در حالت resolve، یعنی عملیات تکمیل شده و اکنون می‌خواهیم نتیجه‌ی نهایی را بازگشت دهیم. نتیجه نهایی بازگشت داده شده‌ی در اینجا، همانند سایر validators است و اگر نال باشد، یعنی اعتبارسنجی موفقیت آمیز بوده و اگر یک شیء را بازگشت دهیم، یعنی اعتبارسنجی با شکست مواجه شده‌است.

این Promise، از یک سرویس تعریف شده‌ی در فایل جدید user.service.ts استفاده می‌کند:
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Headers, RequestOptions } from '@angular/http';
import { IUser } from  './user';
import { IResult } from './result';
 
@Injectable()
export class UserService {
    private _checkUserUrl = '/home/checkUser';
 
    constructor(private _http: Http) { }
 
    private handleError(error: Response) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
 
    isUserNameUnique(user: IUser): Observable<IResult> {
        let headers = new Headers({ 'Content-Type': 'application/json' }); // for ASP.NET MVC
        let options = new RequestOptions({ headers: headers });
 
        return this._http.post(this._checkUserUrl, JSON.stringify(user), options)
            .map((response: Response) => <IResult>response.json())
            .do(data => console.log("User: " + JSON.stringify(data)))
            .catch(this.handleError);
    }
}
با نحوه‌ی تعریف سرویس‌ها و همچنین کار با سرور و دریافت اطلاعات، در قسمت‌های قبلی بیشتر آشنا شدیم. در اینجا یک درخواست get، به آدرس home/checkuser سرور، ارسال می‌شود. سپس نتیجه‌ی آن در قالب اینترفیس IResult بازگشت داده خواهد شد. این اینترفیس را در فایل result.ts به صورت ذیل تعریف کرده‌ایم:
export interface IResult {
    result: boolean;
}

کدهای سمت سرور برنامه که کار بررسی یکتا بودن نام کاربری را انجام می‌دهند، به صورت ذیل در فایل Controllers\HomeController.cs تعریف شده‌اند:
namespace MVC5Angular2.Controllers
{
    public class HomeController : Controller
    {
        [HttpPost]
        public ActionResult CheckUser(User user)
        {
            var isUnique = new { result = true };
            if (user.Name?.Equals("Vahid", StringComparison.OrdinalIgnoreCase) ?? false)
            {
                isUnique = new { result = false };
            }
 
            return new ContentResult
            {
                Content = JsonConvert.SerializeObject(isUnique, new JsonSerializerSettings
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver()
                }),
                ContentType = "application/json",
                ContentEncoding = Encoding.UTF8
            };
        }
    }
}
در اینجا اگر نام کاربری وارد شده مساوی Vahid بود، یک شیء anonymous، مطابق امضای اینترفیس IResult سمت کاربر (همان فایل result.ts عنوان شده) بازگشت داده می‌شود.

بنابراین تا اینجا مسیر سمت سرور home/checkuser تکمیل شده‌است. این مسیر توسط سرویس کاربر صدا زده شده و اگر نام کاربری وارد شده موجود باشد، نتیجه‌ای را مطابق امضای قرارداد IResult سفارشی ما بازگشت می‌دهد.
پس از آن مجددا به فایل signup-form.component.ts مراجعه کرده و سرویس جدید UserService را به سازنده‌ی آن تزریق کرده‌ایم. همچنین قسمت providers این کامپوننت را هم جهت تکمیل اطلاعات تزریق کننده‌ی توکار AngularJS 2.0 مقدار دهی کرده‌ایم. البته همانطور که در مبحث تزریق وابستگی‌ها نیز عنوان شد، اگر این سرویس قرار نیست در کلاس دیگری استفاده شود، نیازی نیست تا آن‌را در بالاترین سطح ممکن و در فایل app.component.ts ثبت و معرفی کرد:
@Component({
    selector: 'signup-form',
    templateUrl: 'app/users/signup-form.component.html',
    providers: [ UserService ]
})
export class SignupFormComponent {
 
    constructor(private _formBuilder: FormBuilder, private _userService: UserService) {
پس از ترزیق وابستگی private _userService: UserService، اکنون این سرویس به سادگی و به حالت متداولی در متد nameShouldBeUnique(control: Control) قابل دسترسی خواهد بود و از آن می‌توان جهت اعتبارسنجی‌های غیرهمزمان استفاده کرد.

اکنون که کدهای فعال سازی اعتبارسنجی از راه دور ما تکمیل شده‌است، به فایل signup-form.component.html مراجعه کرده و پیام مناسبی را نمایش خواهیم داد:
<div *ngIf="name.touched && name.errors">
    <label class="text-danger" *ngIf="name.errors.required">
        Username is required.
    </label>
    <label class="text-danger" *ngIf="name.errors.cannotContainSpace">
        Username can't contain space.
    </label>
    <label class="text-danger" *ngIf="name.errors.nameShouldBeUnique">
        This username is already taken.
    </label>
</div>
در ادامه اگر برنامه را اجرا کنید، با ورود نام کاربری Vahid، یک چنین پیام خطایی، مشاهده خواهد شد:



نمایش پیام loading در حین انجام اعتبارسنجی از راه دور

شاید بد نباشد که در حین انجام عملیات اعتبارسنجی از راه دور و ارسال درخواستی به سرور و بازگشت نتیجه‌ی آن، یک پیام loading را نیز نمایش داد. برای انجام این‌کار نیاز است تغییرات ذیل را به فایل signup-form.component.html اضافه کنیم:
<input id="name" type="text" class="form-control"
       ngControl="name"
       #name="ngForm" />
<div *ngIf="name.control.pending">
    Checking server, Please wait ...
</div>
در اینجا یک div جدید را ذیل المان ورود نام کاربری اضافه کرده‌ایم. همچنین نحوه‌ی نمایش آن‌را با دسترسی به متغیر name# و کنترل منتسب، به آن مدیریت می‌کنیم. اگر عملیات async ایی بر روی این کنترل در حال اجرا باشد، Promise تعریف شده، وضعیت pending را بازگشت می‌دهد. به همین جهت می‌توان از این خاصیت، جهت نمایش دادن یا مخفی کردن عبارت و یا تصویری استفاده کرد.

 
اعتبارسنجی ترکیبی در حین submit یک فرم

فرض کنید می‌خواهید منطقی را که حاصل اعتبارسنجی تمام فیلدهای فرم است (و نه هر کدام به تنهایی)، در حین submit آن اعمال کنید. برای مثال آیا ترکیب نام کاربری و کلمه‌ی عبور شخصی در حین login معتبر است یا خیر؟ در این حالت پس از بررسی‌های لازم در متد onSubmit، می‌توان با استفاده از متد find شیء form، به یکی از کنترل‌های فرم دسترسی یافت و سپس با استفاده از متد setErrors، خطای اعتبارسنجی سفارشی را به آن اضافه کرد:
onSubmit(): void {
    console.log(this.form.value);
 
    this.form.find('name').setErrors({
        invalidData : true
    }); 
}
سپس در سمت قالب این کامپوننت، نحوه‌ی نمایش این اعتبارسنجی سفارشی، همانند قبل است:
<div *ngIf="name.touched && name.errors">
    <label class="text-danger" *ngIf="name.errors.invalidData">
        Check the inputs....
    </label>
</div>


اتصال المان‌های فرم به مدلی جهت ارسال به سرور

اکنون که دسترسی به خاصیت this.form را داریم و این خاصیت توسط [ngFormModel] به تمام اشیاء تعریف شده‌ی در فرم و تغییرات آن‌ها دسترسی دارد، می‌توان از آن برای دسترسی به شیء‌ایی که حاوی مدل فرم است، استفاده کرد. برای نمونه در مثال فوق، خاصیت value آن، چنین خروجی را دارد:
  { name="VahidN", email="email@site.com", password="123"}
بنابراین برای ارسال اطلاعات این فرم به سرور، تنها کافی است این شیء را ارسال کنیم. به همین جهت در فایل user.service.ts، به کلاس سرویس کاربران، متد addUser را اضافه می‌کنیم:
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Headers, RequestOptions } from '@angular/http';
import { IUser } from  './user';
import { IResult } from './result';
 
@Injectable()
export class UserService {
    private _addUserUrl = '/home/addUser';
 
    constructor(private _http: Http) { }
 
    private handleError(error: Response) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
 
    addUser(user: IUser): Observable<IUser> {
        let headers = new Headers({ 'Content-Type': 'application/json' }); // for ASP.NET MVC
        let options = new RequestOptions({ headers: headers });
 
        return this._http.post(this._addUserUrl, JSON.stringify(user), options)
            .map((response: Response) => <IUser>response.json())
            .do(data => console.log("User: " + JSON.stringify(data)))
            .catch(this.handleError);
    }
}
کدهای سمت سرور آن در فایل Controllers\HomeController.cs نیز چنین شکلی را می‌توانند داشته باشند:
[HttpPost]
public ActionResult AddUser(User user)
{
    user.Id = 1; //todo: save user and get id from db
 
    return new ContentResult
    {
        Content = JsonConvert.SerializeObject(user, new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        }),
        ContentType = "application/json",
        ContentEncoding = Encoding.UTF8
    };
}
و پس از آن کدهای متد onSubmit فایل signup-form.component.ts برای ارسال این شیء به صورت ذیل خواهند بود:
onSubmit(): void {
    console.log(this.form.value);
 
    /*this.form.find('name').setErrors({
            invalidData : true
        });*/
 
    this._userService.addUser(<IUser>this.form.value)
        .subscribe((user: IUser) => {
            console.log(`ID: ${user.id}`);
        });
}


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: (این کدها مطابق نگارش RC 1 هستند)
MVC5Angular2.part11.zip


خلاصه‌ی بحث

برای اینکه بتوان کنترل بیشتری را بر روی المان‌های فرم داشت، ابتدا سرویس FormBuilder را در سازنده‌ی کلاس کامپوننت فرم تزریق می‌کنیم. سپس با استفاده از متد group آن، المان‌های فرم را به صورت کلیدهای شیء پارامتر آن تعریف می‌کنیم. در اینجا می‌توان اعتبارسنجی‌های توکار AngularJS 2.0 را که در کلاس پایه‌ی Validators مانند Validators.required وجود دارند، تعریف کرد. با استفاده از متد compose آن‌ها را ترکیب نمود و یا پارامتر سومی را جهت اعتبارسنجی‌های async اضافه نمود. در این حالت شیء form تعریف شده به صورت [ngFormModel] به قالب فرم متصل می‌شود و از تغییرات آن آگاه خواهد شد.
نظرات مطالب
ساخت ربات تلگرامی با #C
سلام  . دو تا مشکل دارم :
1. اینکه نمیدونم چرا برخی از پیامهای دریافتی null هستش و بخاطر همین نال بودن rs.message برنامه میفته تو catch  . چطوری نال رو رد کنم ؟
2. وقتی از متد آپدیت استفاده میکنم وقتی داخل یک حلقه قرارش میدم همینطوری هر بار که حلقه شروع میشه میاد هرچی پیام قبلا توسط کاربر به بات فرستاده شده رو میگیره در آرایه ش میریزه و باز پاسخ میده و وقتی هم که پیام جدید واسش میاد نمیتونه به لیستش اضافه کنه .  
protected void Page_Load(object sender, EventArgs e)
        {
            long offset = 0;
            var Bot = new Telegram.Bot.TelegramBotClient("Token");
            int whilecount = 0;
            while (true)
            {
               
                WebRequest req = WebRequest.Create("https://api.telegram.org/bot" + "Token" + "/getUpdates");
                req.UseDefaultCredentials = true;
                WebResponse resp = req.GetResponse();
                Stream stream = resp.GetResponseStream();
                StreamReader sr = new StreamReader(stream);
                string s = sr.ReadToEnd();
                sr.Close();
                var jobject = Newtonsoft.Json.Linq.JObject.Parse(s);
                mydata gg = JsonConvert.DeserializeObject<mydata>(jobject.ToString());
                List<result> results = new List<result>();
                foreach (result rs in gg.result)
                {
                    try
                    {
                         
                        //Debug.Assert(message != null, "message != null");
                        //if ((BotTelegramWeb.TaktopBot.message.Equals(rs, null))!= true)
                        if (rs.update_id != 547758883 && rs.update_id !=547758886)
                        {
                            results.Add(rs);
                            SendMessage(rs.update_id.ToString(), "hello      " + rs.message.chat.first_name); 
                        }
                        else
                        {
                            continue;
                        }
                    }
                    catch (Exception ex)
                    {
                        throw;
                    }
                }
            }    
        }
       
    }

اشتراک‌ها
v1.21.18 افزونه‌ی #C مخصوص Visual Studio Code منتشر شد
v1.21.18 افزونه‌ی #C مخصوص Visual Studio Code منتشر شد
مطالب
اجرای SSIS Package از طریق برنامه کاربردی

مقدمه

در اکثر موارد در یک Landscape عملیاتی، چنانچه به تجمیع و انتقال داده‌ها از بانک‌های اطلاعاتی مختلف نیاز باشد، از SSIS Package اختصار (SQL Server Integration Service) استفاده می‌شود و معمولاً با تعریف یک Job در سطح SQL Server به اجرای Package در زمانهای مشخص می‌پردازند. چنانچه در موقعیتی لازم باشد که از طریق برنامه کاربردی توسعه یافته، به اجرای Package مبادرت ورزیده شود و البته نخواهیم Job تعریف شده را از طریق کد برنامه، اجرا کنیم و در واقع این امکان را داشته باشیم که همانند یک رویه ذخیره شده تعریف شده در سطح بانک اطلاعاتی به اجرای عمل فوق بپردازیم، یک راه حل می‌تواند تعریف یک CLR Stored Procedures باشد. در این مقاله به بررسی این موضوع پرداخته می‌شود، در ابتدا لازم است به بیان تئوری موضوع پرداخته شود (قسمت‌های 1 الی 5) در ادامه به ذکر پیاده سازی روش پیشنهادی پرداخته می‌شود.

1- اجرای Integration Service Package 

جهت اجرای یک Package از ابزارهای زیر می‌توان استفاده کرد:
• command-line ابزار خط فرمان dtexec.exe
• ابزار اجرائی پکیج dtexecui.exe
• استفاده از SQL Server Agent job
توجه: همچنین یک Package را در زمان طراحی در  Business Intelligence Development Studio) BIDS)  می‌توان اجرا نمود.

2- استفاده از dtexec جهت اجرای Package

با استفاده از ابزار dtexec می‌توان Package‌های ذخیره شده در فایل سیستم، یک SQL Instance و یا Package‌های ذخیره شده در Integration Service را اجرا نمود.

توجه:
در سیستم عامل‌های 64 بیتی، ابزار dtexec موجود در Integration Service با نسخه 64 بیتی نصب می‌شود. چنانچه بایست Package‌های معینی را در حالت 32 بیتی اجرا کنید، لازم است ابزار dtexec نسخه 32 بیتی نصب شود. ابزار dtexec دستیابی به تمامی ویژگی‌های پیکربندی و اجرای Package از قبیل اتصالات، مشخصات(Properties)، متغیرها، logging و شاخص‌های پردازشی را فراهم می‌کند.
توجه: زمانی که از نسخه‌ی ابزار dtexec که با SQL Server 2008 ارائه شده استفاده می‌کنید برای اجرای یک SSIS Package نسخه 2005، Integration Service به صورت موقت Package را به نسخه 2008 ارتقا می‌دهد، اما نمی‌توان از ابزار dtexec برای ذخیره این تغییرات استفاده کرد.

2-1- ملاحظات نصب dtexec روی سیستم‌های 64 بیتی

به صورت پیش فرض، یک سیستم عامل 64 بیتی که هر دو نسخه 64 بیتی و 32 بیتی ابزار خط فرمان Integration Service را دارد، نسخه 32 بیتی نصب شده را در خط فرمان اجرا خواهد کرد. نسخه 32 بیتی بدین دلیل اجرا می‌شود که در متغیر محیطی (Path (Path environment variable مسیر directory نسخه 32 بیتی قرار گرفته است.به طور معمول:
(<drive>:\Program Files(x86)\Microsoft SQL Server\100\DTS\Binn)
توجه: اگر از SQL Server Agent برای اجرای Package استفاده می‌کنید، SQL Server Agent به طور خودکار از ابزار نسخه 64 بیتی استفاده می‌کند. SQL Server Agent از Registry و نه از متغیر محیطی Path استفاده می‌کند. برای اطمینان از اینکه نسخه 64 بیتی این ابزار را در خط فرمان اجرا می‌کنید، directory را به directory ای تغییر دهید که شامل نسخه 64 بیتی این ابزار است(<drive>:\Program Files\Microsoft SQL Server\100\DTS\Binn) و ابزار را از این مسیر اجرا کنید و یا برای همیشه مسیر قرار گرفته در متغیر محیطی path را با مسیری که نسخه 64 بیتی قرار دارد، جایگزین کنید.

2-2- تفسیر کدهای خروجی

هنگامی که یک Package اجرا می‌شود، dtexec یک کد خروجی (Return Code) بر می‌گرداند:

 مقدار توصیف
 0  Package با موفقیت اجرا شده است.
 1  Package با خطا مواجه شده است.
 3 Package در حال اجرا توسط کاربر لغو شده است.
 4  Package پیدا نشده است.
 5  Package بارگذاری نشده است.
 6  ابزار با یک خطای نحوی یا خطای معنایی در خط فرمان برخورد کرده است.

2-3- قوانین نحوی dtexec

تمامی گزینه‌ها (Options) باید با یک علامت Slash (/)  و یا Minus (-)  شروع شوند.
یک آرگومان باید در یک quotation mark محصور شود چنانچه شامل یک فاصله خالی باشد.
گزینه‌ها و آرگومان‌ها بجز رمزعبور حساس به حروف کوچک و بزرگ نیستند.

2-3-1- Syntax

 dtexec /option [value] [/option [value]]…


2-3-2- Parameters

نکته: در Integration Service، ابزار خط فرمان dtsrun که برایData Transformation Service) DTS)‌های نسخه SQL Server 2000 استفاده می‌شد، با ابزار خط فرمان dtexec جایگزین شده است.
• تعدادی از گزینه‌های خط فرمان dtsrun به طور مستقیم در dtexec معادل دارند برای مثال نام Server و نام Package.
• تعدادی از گزینه‌های dtsrun به طور مستقیم در dtexec معادل ندارند.
• تعدادی گزینه‌های خط فرمان جدید dtsexec وجود دارد که در ویژگی‌های جدید Integration Service پشتیبانی می‌شود.

2-3-3- مثال

1) به منظور اجرای یک SSIS Package که در SQL Server ذخیره شده است، با استفاده از Windows Authentication :
 dtexec /sq <Package Name> /ser <Server Name>

2) به منظور اجرای یک SSIS Package که در پوشه File System در SSIS Package Store ذخیره شده است :
 dtexec /dts “\File System\<Package File Name>”

3) به منظور اجرای یک SSIS Package که در سیستم فایل ذخیره شده است و مشخص کردن گزینه logging:
 dtexec /f “c:\<Package File Name>” /l “DTS.LogProviderTextFile; <Log File Name>”

4) به منظور اجرای یک SSIS Package که در SQL Server ذخیره شده با استفاده از SQL Server Authentication برای نمونه(user:ssis;pwd:ssis@ssis)و رمز (Package(123:
 dtexec  /server “<Server Name>”  /sql “<Package Name>”  / user “ssis” /Password “ssis@ssis” /De “123”


3- تنظیمات سطح حفاظتی یک Package

به منظور حفاظت از داده‌ها در Package‌های Integration Service می‌توانید یک سطح حفاظتی (protection level) را تنظیم کنید که به حفاظت از داده‌های صرفاً حساس یا تمامی داده‌های یک Package کمک نماید. به  علاوه می‌توانید این داده‌ها را با یک Password یا یک User Key رمزگذاری نمائید یا به رمزگذاری داده‌ها در بانک اطلاعاتی اعتماد کنید. همچنین سطح حفاظتی که برای یک Package استفاده می‌کنید، الزاماً ایستا (static) نیست و در طول چرخه حیات یک Package می‌تواند تغییر کند. اغلب سطح حفاظتی در طول توسعه یا به محض (deploy) استقرار Package تنظیم می‌شود.
توجه: علاوه بر سطوح حفاظتی که توصیف شد، Package‌ها در بانک اطلاعاتی msdb ذخیره می‌شوند که همچنین می‌توانند توسط نقش‌های ثابت در سطح بانک اطلاعاتی (fixed database-level roles) حفاظت شوند. Integration Service شامل 3 نقش ثابت بانک اطلاعاتی برای نسبت دادن مجوزها به Package است که عبارتند از db_ssisadmin  ،db_ssisltduser و db_ssisoperator

3-1- درک سطوح حفاظتی

در یک Package اطلاعات زیر به عنوان حساس تعریف می‌شوند:
• بخش password در یک connection string. گرچه، اگر گزینه ای را که همه چیز را رمزگذاری کند، انتخاب کنید تمامی connection string حساس در نظر گرفته می‌شود.
• گره‌های task-generated XML که برچسب (tagged) هایی حساس هستند.
• هر متغییری که به عنوان حساس نشان گذاری شود.

3-1-1- Do not save sensitive

هنگامی که Package ذخیره می‌شود از ذخیره مقادیر ویژگی‌های حساس در Package جلوگیری می‌کند. این سطح حفاظتی رمزگذاری نمی‌کند اما در عوض از ذخیره شدن ویژگی هایی که حساس نشان گذاری شده اند به همراه Package جلوگیری می‌کند.

3-1-2- Encrypt all with password

به منظور رمزگذاری تمامی Package از یک Password استفاده می‌شود. Package توسط Password ای رمزگذاری می‌شود  که کاربر هنگامی که Package را ایجاد یا Export می‌کند، ارائه می‌دهد. به منظور باز کردن Package در SSIS Designer یا اجرای Package توسط ابزار خط فرمان dtexec کاربر بایست رمز Package را ارائه نماید. بدون رمز کاربر قادر به دستیابی و اجرای Package نیست.

3-1-3- Encrypt all with user key

به منظور رمزگذاری تمامی Package از یک کلید که مبتنی بر Profile کاربر جاری می‌باشد، استفاده می‌شود. تنها کاربری که Package را ایجاد یا Export می‌کند، می‌تواند Package را در SSIS Designer باز کند و یا Package را توسط ابزار خط فرمان dtexec اجرا کند.

3-1-4- Encrypt sensitive with password

به منظور رمزگذاری تنها مقادیر ویژگی‌های حساس در Package از یک Password استفاده می‌شود. برای رمزگذاری از DPAPI استفاده می‌شود. داده‌های حساس به عنوان بخشی از Package ذخیره می‌شوند اما آن داده‌ها با استفاده از Password رمزگذاری می‌شوند. به منظور باز نمودن Package در SSIS Designer کاربر باید رمز Package را ارائه دهد. اگر رمز ارائه نشود، Package بدون داده‌های حساس باز می‌شود و کاربر باید مقادیر جدیدی برای داده‌های حساس فراهم کند. اگر کاربر سعی نماید Package را بدون ارائه رمز اجرا کند، اجرای Package با خطا مواجه می‌شود.

3-1-5- Encrypt sensitive with user key

به منظور رمزگذاری تنها مقادیر ویژگی‌های حساس در Package از یک کلید که مبتنی بر Profile کاربر جاری می‌باشد، استفاده می‌شود. تنها کاربری که از همان Profile استفاده می‌کند، Package را می‌تواند بارگذاری (load) کند. اگر کاربر متفاوتی Package را باز نماید، اطلاعات حساس با مقادیر پوچی جایگزین می‌شود و کاربر باید مقادیر جدیدی برای داده‌های حساس فراهم کند. اگر کاربر سعی نماید Package را بدون ارائه رمز اجرا کند، اجرای Package با خطا مواجه می‌شود. برای رمزگذاری از DPAPI استفاده می‌شود.

3-1-6- (Rely on server storage for encryption (ServerStorage

با استفاده از نقش‌های بانک اطلاعاتی، SQL Server تمامی Package را حفاظت می‌کند. این گزینه تنها زمانی پشتیبانی می‌شود که Package در بانک اطلاعاتی msdb ذخیره شده است.

4- استفاده از نقش‌های Integration Service

برای کنترل کردن دستیابی به Package، SSIS شامل 3 نقش ثابت در سطح بانک اطلاعاتی است. نقش‌ها می‌توانند تنها روی Package هایی که در بانک اطلاعاتی msdb ذخیره شده اند، بکار روند. با استفاده از SSMS می‌توانید نقش‌ها را به Package‌ها نسبت دهید، این انتساب نقش‌ها در بانک اطلاعاتی msdb ذخیره می‌شود.

 Write action  Read action Role
 Import packages
Delete own packages
Delete all packages
Change own package roles
Change all package roles

* به نکته رجوع شود

 Enumerate own packages
Enumerate all packages
View own packages
View all packages
Execute own packages
Execute all packages
Export own packages
Export all packages
Execute all packages in SQL Server Agent
 db_ssisadmin
or
sysadmin

Import packages
Delete own packages
Change own package roles

Enumerate own packages
Enumerate all packages
View own packages
Execute own packages
Export own packages
db_ssisltduser
None
Enumerate all packages
View all packages
Execute all packages
Export all packages
Execute all packages in SQL Server Agent
db_ssisoperator
Stop all currently running packages
View execution details of all running packages
Windows administrators
* نکته: اعضای نقش‌های db_ssisadmin و dc_admin ممکن است قادر باشند مجوزهای خودشان را تا سطح sysadmin ارتقا دهند براساس این ترفیع مجوز امکان اصلاح و اجرای Package‌ها از طریق SQL Server Agent میسر می‌شود. برای محافظت در برابر این ارتقا، با استفاده از یک (account) حساب Proxy  با دسترسی محدود، Job هایی که این Package‌ها را اجرا می‌کنند، پیکربندی شوند یا  تنها اعضای نقش sysadmin به نقش‌های db_ssisadmin و dc_admin افزوده شوند.

همچنین جدول sysssispackages در بانک اطلاعاتی msdb شامل Package هایی است که در SQL Server ذخیره می‌شوند. این جدول شامل ستون هایی که اطلاعاتی درباره نقش هایی که به Package‌ها نسبت داده شده است، می‌باشد.
به صورت پیش فرض، مجوزهای نقش‌های ثابت بانک اطلاعاتی db_ssisadmin و db_ssisoperator و شناسه منحصر به فرد کاربری (unique security identifier) که Package را ایجاد کرده برای خواندن Package بکار می‌رود، و مجوزهای نقش db_ssisadmin و شناسه منحصر به فرد کاربری که Package را ایجاد کرده برای نوشتن Package به کار می‌رود. یک User باید عضو نقش db_ssisadmin و db_ssisltduser یا db_ssisoperator برای داشتن دسترسی خواندن Package باشد. یک User باید عضو نقش db_ssisadmin برای داشتن دسترسی نوشتن Package باشد.

5- اتصال به صورت Remote به Integration Service

زمانی که یک کاربر بدون داشتن دسترسی کافی تلاش کند به یک Integration Service به صورت Remote متصل شود، با پیغام خطای "Access is denied" مواجه می‌شود. برای اجتناب از این پیغام خطا می‌توان تضمین کرد که کاربر مجوز مورد نیاز DCOM را دارد.
به منظور پیکربندی کردن دسترسی کاربر به صورت Remote به سرویس Integration  مراحل زیر را دنبال کنید:
- Component Service را باز نمایید ( در Run عبارت dcomcnfg را تایپ کنید).
- گره Component Service را باز کنید، گره Computer و سپس My Computer را باز نمایید و روی DCOM Config کلیک نمایید.
- گره DCOM Config را باز کنید و از لیست برنامه هایی که می‌توانند پیکربندی شوند MsDtsServer را انتخاب کنید.
- روی Properties برنامه MsDtsServer رفته و قسمت Security را انتخاب کنید.
- در قسمت Lunch and Activation Permissions، مورد Customize را انتخاب و سپس روی Edit کلیک نمایید تا پنجره Lunch Permission باز شود.
- در پنجره Lunch Permission، کاربران را اضافه و یا حذف کنید و مجوزهای مناسب را به کاربران یا گروه‌های مناسب نسبت دهید. مجوزهای موجود عبارتند از Local Lunch، Remote Lunch، Local Activation و Remote Activation .
- در قسمت Access Permission مراحل فوق را به منظور نسبت دادن مجوزهای مناسب به کاربران یا گروه‌های مناسب انجام دهید.
- سرویس Integration  را Restart کنید.
مجوز دسترسی  Lunch به منظور شروع و خاتمه سرویس، اعطا  یا رد  می‌شود و مجوز دسترسیActivation به منظور متصل شدن به سرویس، اعطا (grant) یا رد (deny) می‌شود.

6- پیاده سازی

در ابتدا به ایجاد یک CLR Stored Procedures پرداخته می‌شود نام اسمبلی ساخته شده به این نام RunningPackage.dll می‌باشد و حاوی کد زیر است:
Partial Public Class StoredProcedures
    '------------------------------------------------
    'exec dbo.Spc_NtDtexec 'Package','ssis','ssis@ssis','1234512345'
    '------------------------------------------------
    <Microsoft.SqlServer.Server.SqlProcedure()> _
    Public Shared Sub Spc_NtDtexec(ByVal PackageName As String, _
                                   ByVal UserName As String, _
                                   ByVal Password As String, _
                                   ByVal Decrypt As String)
        Dim p As New System.Diagnostics.Process()
        p.StartInfo.FileName = "C:\Program Files\Microsoft SQL Server\100\DTS\Binn\DTExec.exe"
        p.StartInfo.RedirectStandardOutput = True
        p.StartInfo.Arguments = "/sql " & PackageName & " /User " & UserName & " /Password " & Password & " /De " & Decrypt
        p.StartInfo.UseShellExecute = False
        p.Start()
        p.WaitForExit()
        Dim output As String
        output = p.StandardOutput.ReadToEnd()
        Microsoft.SqlServer.Server.SqlContext.Pipe.Send(output)
    End Sub
End Class
در حقیقت توسط این رویه به اجرای برنامه dtexec.exe و ارسال پارامترهای مورد نیاز جهت اجرا پرداخته می‌شود. با توجه به توضیحات تئوری بیان شده، سطح حفاظتی Package ایجاد شده Encrypt all with password توصیه می‌شود که رمز مذکور در قالب یکی از پارامتر ارسالی به رویه ساخته شده موسوم به Spc_NtDtexec ارسال می‌گردد.

در قدم بعدی نیاز به Register کردن dll ساخته شده در سطح بانک اطلاعاتی SQL Server است، این گام‌ها پس از اتصال به SQL Server Management Studio به شرح زیر است:
1- فعال کردن CLR در سرویس SQL Server
SP_CONFIGURE 'clr enabled',1
GO
RECONFIGURE

2- فعال کردن ویژگی TRUSTWORTHY در بانک اطلاعاتی مورد نظر
 ALTER DATABASE <Database Name> SET TRUSTWORTHY ON
GO
RECONFIGURE

3- ایجاد Assembly و Stored Procedure در بانک اطلاعاتی مورد نظر
Assembly ساخته شده با نام RunningPacakge.dll در ریشه :C کپی شود. بعد از ثبت نمودن این Assembly لزومی به وجود آن نمی‌باشد.
USE <Database Name>
GO
CREATE ASSEMBLY [RunningPackage]
AUTHORIZATION [dbo]
FROM 'C:\RunningPackage.dll'
WITH PERMISSION_SET = UNSAFE
Go
CREATE PROCEDURE [dbo].[Spc_NtDtexec]
@PackageName [nvarchar](50),
@UserName [nvarchar](50),
@Password [nvarchar](50),
@Decrypt [nvarchar](50)
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [RunningPackage].[RunningPackage.StoredProcedures].[Spc_NtDtexec]
GO
توجه: Application User برنامه بایست دسترسی اجرای رویه ذخیره شده Spc_NtDtexec را در بانک اطلاعاتی مورد نظر داشته باشد همچنین بایست عضو نقش db_ssisoperator در بانک اطلاعاتی msdb باشد.( منظور از Application User، لاگین است که در Connection string برنامه قرار داده اید.)

در برنامه کاربردی تان کافی است متدی به شکل زیر ایجاد و با توجه به نیازتان در برنامه به فراخوانی آن و اجرای Package بپردازید.
    Private Sub ExecutePackage()
        Dim oSqlConnection As SqlClient.SqlConnection
        Dim oSqlCommand As SqlClient.SqlCommand
        Dim strCnt As String = String.Empty
        strCnt = "Data Source=" & txtServer.Text & ";User ID=" & txtUsername.Text & ";Password=" & txtPassword.Text & ";Initial Catalog=" & cmbDatabaseName.SelectedValue.ToString() & ";"
        Try
            oSqlConnection = New SqlClient.SqlConnection(strCnt)
            oSqlCommand = New SqlClient.SqlCommand
            With oSqlCommand
                .Connection = oSqlConnection
                .CommandType = System.Data.CommandType.StoredProcedure
                .CommandText = "dbo.Spc_NtDtexec"
                .Parameters.Clear()
                .Parameters.Add("@PackageName", System.Data.SqlDbType.VarChar, 50)
                .Parameters.Add("@UserName", System.Data.SqlDbType.VarChar, 50)
                .Parameters.Add("@Password", System.Data.SqlDbType.VarChar, 50)
                .Parameters.Add("@Decrypt", System.Data.SqlDbType.VarChar, 50)
                .Parameters("@PackageName").Value = txtPackageName.Text.Trim()
                .Parameters("@UserName").Value = txtUsername.Text.Trim()
                .Parameters("@Password").Value = txtPassword.Text.Trim()
                .Parameters("@Decrypt").Value = txtDecrypt.Text.Trim()
            End With
            If (oSqlCommand.Connection.State <> System.Data.ConnectionState.Open) Then
                oSqlCommand.Connection.Open()
                oSqlCommand.ExecuteNonQuery()
                System.Windows.Forms.MessageBox.Show("Success")
            End If
            If (oSqlCommand.Connection.State = System.Data.ConnectionState.Open) Then
                oSqlCommand.Connection.Close()
            End If
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error")
        End Try
    End Sub 'ExecutePackage
اشتراک‌ها
پلاگین SonarLint برای بهینه سازی کدها

SonarLint is a free IDE extension that lets you fix coding issues before they exist! Like a spell checker, SonarLint highlights Bugs and Security Vulnerabilities as you write code, with clear remediation guidance so you can fix them before the code is even committed. SonarLint in VS Code supports analysis of C, C++, HTML, Java, JavaScript, PHP, Python and TypeScript, and you can install it directly from the VS Code Marketplace! 

پلاگین SonarLint برای بهینه سازی کدها