نظرات مطالب
مدیریت Instance در WCF
سلام
یه سوالی برام پیش اومده ممنون میشم راهنمایی فرمایید:
من یک سرویس WCF  ایجاد کردم  و اونو به شکل زیر تنظیم کردم :
 [ServiceContract(SessionMode=SessionMode.Reqquired)]
 و همچنین برای کلاس پیاده سازی کننده اینترفیس :
 [ServiceBehavior( InstanceContextMode = InstanceContextMode.PerSession)]
من داخل این کلاس یه متغیر از یک کلاس به صورت سراسری تعریف کردم که میخوام ازش توی متدهای متفاوت استفاده کنم اما ظاهرا با هر بار فراخوانی باز هم این متغیر داده‌های خودشو ازدست میده البته static نیست و به دلیل ساختار اون نمیتونم استاتیکش کنم
ممنون میشم راهنمایی نمایید
مطالب
بهبود SEO برنامه‌های Angular
یکی از موارد مهم بهینه سازی صفحات سایت برای موتورهای جستجو، افزودن عنوانی مناسب، به همراه توضیحات و واژه‌های کلیدی، twitter card ،Facebook Graph و امثال آن‌ها است. برای این منظور Angular به همراه سرویس‌هایی است که امکان افزودن این متاتگ‌ها را به صورت پویا مهیا می‌کنند.


آشنایی با امکانات بسته‌ی angular/platform-browser@

در ماژول angular/platform-browser@، دو سرویس Meta و Title، امکان تغییر پویای متاتگ‌های صفحه‌ی جاری را مهیا می‌کنند. برای نمونه فرض کنید قصد دارید یک چنین متاتگ‌هایی را به صفحه‌ی جاری اضافه کنید:
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
    <title>newTitle ...</title>
    <base href="/">
    <meta name="description" content="Angular meta service">
<meta name="author" content="DNT">
<meta name="keywords" content="Angular, Meta Service">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@my_site">
<meta name="twitter:title" content="Front-end Web Development">
<meta name="twitter:description" content="Learn frontend web development...">
<meta name="twitter:image" content="https://site/images/image.png">
<meta name="author" content="Other Author">
</head>
قدم اول انجام اینکار، تزریق سرویس‌های توکار Meta و Title به سازنده‌ی کامپوننت جاری است:
import { Component, OnInit } from "@angular/core";
import { Meta, Title } from "@angular/platform-browser";

@Component({
  selector: "app-seo-tests",
  templateUrl: "./seo-tests.component.html",
  styleUrls: ["./seo-tests.component.css"]
})
export class SeoTestsComponent implements OnInit {

  constructor(private metaService: Meta, private titleService: Title) {
  }
در ادامه متدهای مختلف این سرویس‌ها را بررسی خواهیم کرد:

افزودن یک یا چند متاتگ

متد addTag سرویس Meta، کار افزودن پویای یک متا تگ جدید را به همراه ویژگی‌های name و content آن، انجام می‌دهد. در ذیل چندین مثال از آن‌را مشاهده می‌کنید. در اینجا یا می‌توان از متد addTag استفاده کرد که تنها یک متاتگ را به صفحه اضافه می‌کند و یا از متد addTags کمک گرفت که می‌تواند آرایه‌ای از متاتگ‌ها را به صورت پویا به صفحه‌ی جاری اضافه کند:
    // addTag & addTags
    this.metaService.addTag({ name: "description", content: "How to optimize your Angular App for search engine and other crawlers." });
    this.metaService.addTag({ name: "author", content: "DNT" });
    this.metaService.addTag({ name: "keywords", content: "Angular, Meta Service" });
    // Or
    this.metaService.addTags([
      { name: "description", content: "How to optimize your Angular App for search engine and other crawlers." },
      { name: "author", content: "DNT" },
      { name: "keywords", content: "Angular, Meta Service" }
    ], false); // --> forceCreation = false

    this.metaService.addTag({ name: "twitter:card", content: "summary_large_image" });
    this.metaService.addTag({ name: "twitter:site", content: "@my_site" });
    this.metaService.addTag({ name: "twitter:title", content: "Front-end Web Development" });
    this.metaService.addTag({ name: "twitter:description", content: "Learn frontend web development..." });
    this.metaService.addTag({ name: "twitter:image", content: "https://site/images/image.png" });
    // Or
    this.metaService.addTags([
      { name: "twitter:card", content: "summary_large_image" },
      { name: "twitter:site", content: "@my_site" },
    ], false); // --> forceCreation = false
هر دو متد addTag و addTags دارای پارامتر boolean دومی به نام forceCreation نیز هستند. برای مثال اگر این پارامتر را به true تنظیم کنید، این متاتگ حتی اگر وجود هم داشته باشد، یکبار دیگر به صفحه اضافه خواهد شد.


دریافت محتوای متاتگ‌های موجود

با استفاده از متد getTag می‌توان یک متاتگ مشخص را به صورت HTMLMetaElement دریافت کرد:
    // getTag & getTags
    const viewport = this.metaService.getTag("name=viewport");
    if (viewport) {
      console.log(viewport.content); // width=device-width, initial-scale=1
    }
    const author = this.metaService.getTag("name=author");
    if (author) {
      console.log(author.content); // DNT
    }

    this.metaService.addTag({ name: "author", content: "DNT" });
    this.metaService.addTag({ name: "author", content: "Other Author" }, true);
    const authors = this.metaService.getTags("name=author");
    console.log(authors[0]); // <meta name="author" content="DNT">
    console.log(authors[1]); // <meta name="author" content="Other Author">
کار متد getTags بازگشت تمام متاتگ‌هایی با attribute-selector یکسان است. برای مثال در اینجا دوبار متاتگ author به صفحه اضافه شده‌است و خروجی getTags به همراه دو عنصر است.


به روز رسانی متاتگ‌های موجود

می‌توان از متد updateTag برای تغییر محتوای متاتگی موجود، استفاده کرد:
    // updateTag
    this.metaService.addTag({ name: "twitter:card", content: "summary_large_image" });
    this.metaService.updateTag({ name: "twitter:card", content: "summary" }, `name='twitter:card'`);

    this.metaService.updateTag({ name: "description", content: "Angular meta service" });
در اینجا اگر پارامتر اختیاری دوم ذکر نشود، جستجوی یافتن عناصر، بر اساس name ذکر شده صورت می‌گیرد و سپس content آن‌ها به روز می‌شود.


حذف تگ‌های موجود

در اینجا می‌توان از دو متد removeTag که یک attribute-selector را دریافت می‌کند و یا removeTagElement که یک HTMLMetaElement را توسط متد getTag دریافت می‌کند، برای حذف کامل این تگ‌ها استفاده کرد:
    // removeTag & removeTagElement
    this.metaService.removeTag("charset");
    // Or
    const chartsetTag = this.metaService.getTag("charset");
    if (chartsetTag) {
      this.metaService.removeTagElement(chartsetTag);
    }


تنظیم عنوان صفحه‌ی جاری

سرویس توکار دیگری به نام Title امکان تغییر عنوان صفحه‌ی جاری را به صورت پویا میسر می‌کند:
    // Setting the browser page Title in an Angular app
    const currentTitle = this.titleService.getTitle();
    console.log(currentTitle);
    this.titleService.setTitle("newTitle ...");
متد getTitle، عنوان فعلی صفحه را باز می‌گرداند و متد setTitle، این عنوان را به روز رسانی می‌کند.


طراحی سرویسی برای افزودن پویای متاتگ‌ها به صفحات مختلف سایت

می‌توان شبیه به مطلب «نمایش Breadcrumbs در برنامه‌های Angular» به قسمت data مسیریابی، اطلاعات عنوان صفحه و همچنین  metaTags آن‌را اضافه کرد:
const routes: Routes = [
  {
    path: "seo", component: SeoTestsComponent,
    data: {
      title: "Page Title",
      metaTags: {
        description: "Page Description or some content here",
        keywords: "some, keywords, here, separated, by, a comma"
      }
    }
  }
];
سپس سرویسی را طراحی نمود که با پایان یافتن مسیریابی فعلی، این تنظیمات را به صورت خودکار انجام دهد و نیازی نباشد تا مدام به تمام کامپوننت‌ها، سرویس‌های Meta و Title را به صورت دستی اضافه کرد و این اطلاعات را تغییر داد.
به همین جهت سرویس SEO را در مسیر src\app\core\seo-service.ts به صورت ذیل ایجاد می‌کنیم:
import { Injectable } from "@angular/core";
import { Title, Meta } from "@angular/platform-browser";
import { Router, NavigationEnd, ActivatedRouteSnapshot } from "@angular/router";

@Injectable()
export class SeoService {

  constructor(private titleService: Title, private metaService: Meta, private router: Router) { }

  enableSeo() {
    this.router.events
      .filter(event => event instanceof NavigationEnd)
      .distinctUntilChanged()
      .subscribe(() => {
        this.addMetaData(this.router.routerState.snapshot.root);
      });
  }

  private addMetaData(root: ActivatedRouteSnapshot): void {
    if (root.children && root.children.length) {
      this.addMetaData(root.children[0]);
    } else if (root.data) {
      this.setTitle(root.data);
      this.setMetaTags(root.data);
    }
  }

  private setMetaTags(routeData: { [name: string]: any; }) {
    const routeDataMetaTagsKey = "metaTags";
    const metaTags = routeData[routeDataMetaTagsKey];
    if (!metaTags) { return; }
    for (const tag in metaTags) {
      if (metaTags.hasOwnProperty(tag)) {
        const newTag = { name: tag, content: metaTags[tag] };
        console.log("new tag", newTag);
        this.metaService.addTag(newTag);
      }
    }
  }

  private setTitle(routeData: { [name: string]: any; }) {
    const routeDataTitleKey = "title";
    const title = routeData[routeDataTitleKey];
    if (title) {
      console.log("new title", title);
      this.titleService.setTitle(title);
    }
  }
}
توضیحات:
در اینجا در ابتدای کار مشترک رخ‌داد NavigationEnd سیستم مسیریابی خواهیم شد:
    this.router.events
      .filter(event => event instanceof NavigationEnd)
      .distinctUntilChanged()
      .subscribe(() => {
        this.addMetaData(this.router.routerState.snapshot.root);
      });
هر زمانیکه رخ‌داد مرور صفحه‌ی جاری به پایان رسید، بر اساس مسیر ریشه‌ی آن، متد addMetaData فراخوانی می‌شود. این متد، یک متد بازگشتی است. از این جهت که مسیر جاری می‌تواند حاصل مرور یک مسیر والد و سپس چندین مسیر تو در توی فرزند و والد آن باشد.
سپس در این متد خاصیت data مسیرنهایی را خوانده و کلیدهای title و metaTags آن‌را استخراج می‌کنیم و سپس توسط متدهای this.titleService.setTitle و this.metaService.addTag، این عنوان و متاتگ‌های جدید را به صورت پویا به صفحه اضافه خواهیم کرد.

پس از تعریف این سرویس، برای معرفی آن به برنامه، ابتدا آن‌را به قسمت providers مربوط به CoreModule اضافه می‌کنیم:
import { SeoService } from "./seo-service";

@NgModule({
  providers: [
    SeoService
  ]
})
export class CoreModule {}
و در آخر به فایل app.component.ts مراجعه کرده و این سرویس را فعالسازی می‌کنیم:
import { SeoService } from "./core/seo-service";

export class AppComponent {
  constructor(private seoService: SeoService) {
    this.seoService.enableSeo();
  }
}
از این پس تمام مسیرهای برنامه به صورت خودکار تحت نظر قرار گرفته و درصورتیکه خاصیت data آن‌ها دارای کلیدهای title و metaTags باشند، به صورت خودکار پردازش خواهند شد.



کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
مطالب
کار با دیتاتایپ JSON در MySQL - قسمت اول
تا قبل از اضافه شدن دیتاتایپ JSON به صورت توکار در MySQL، داده‌های JSON را تنها میتوانستیم با فرمت رشته‌ای، درون دیتابیس ذخیره کنیم: 
CREATE TABLE tableName (
jsonData CHAR(250) -- or VARCHAR, TEXT, BLOB
);

INSERT INTO tableName VALUES (
'{ "name": "User1", "age": 41}'
);

SELECT * FROM tableName;

{ "name": "User1", "age": 41}
اما مشکل اینجاست که هیچ نوع اعتبارسنجی بر روی این دیتا صورت نخواهد گرفت؛ هیچ روشی برای مطمئن شدن از اینکه تگ‌ها به درستی استفاده شده‌اند، وجود ندارد و همچنین امکان جستجو را سخت خواهد کرد؛ زیرا مجبور خواهیم بود از Regular Expressions برای جستجوی درون متن‌های ذخیره شده استفاده کنیم:
SELECT * FROM tableName
WHERE jsonData REGEXP 'User1';

از نسخه MySQL 5.7.8 به بعد، می‌توانیم از نوع داده JSON برای ذخیره‌سازی محتوای JSON، استفاده کنیم. از این دیتاتایپ برای ذخیره‌سازی یک JSON document معتبر میتوان استفاده کرد:
CREATE TABLE tableName (
jsonData JSON
);

INSERT INTO tableName VALUES (
'{ "name": "User1", "age": 41, "name": "User2"}'
);

SELECT * FROM tableName;

{"age": 41, "name": "User2"}

همانطور که مشاهده میکنید MySQL به صورت اتوماتیک یکسری نرمال‌سازی را روی دیتا اعمال کرده است:
  • ابتدا بررسی خواهد شد که سند JSON معتبر باشد؛ در غیر اینصورت ذخیره‌سازی با مشکل مواجه خواهد شد.
  • از فیلدهایی که کلید تکراری دارند، صرفنظر خواهند شد. در مثال بالا دوبار فیلد name را مقداردهی کرده‌ایم. در اینجالت key/value دوم لحاظ شده‌است. البته میبایستی اصل first key wins لحاظ میشد، اما این مورد به عنوان یک باگ گزارش شده‌است و در نسخه‌های 8 به بعد رفع شده‌است (https://forums.mysql.com/read.php?3,660500,660500 - https://bugs.mysql.com/bug.php?id=86866).
  • فاصله‌های اضافی بین کلیدها حذف شده‌اند.
  • برای جستجوی بهتر، کلیدهای آبجکت JSON به صورت مرتب شده ذخیره شده‌اند.

جستجو درون JSON Document
یک سند JSON، از یکسری کلیدها به همراه مقادیرشان تشکیل شده‌است. همچنین مقادیر میتوانند شامل اشیاء یا آرایه‌هایی به صورت تودرتو باشند. بنابراین به یک path جهت استخراج مقادیر نیاز خواهیم داشت. برای نوشتن یک path باید scope آن را تعیین کنیم که در توابع MySQL این scope به صورت پیش‌فرض، سند جاری میباشد که توسط علامت $ مشخص میشود. 
فرض کنید ساختار زیر را درون دیتابیس ذخیره کرده‌ایم:
{
    "id": "1",
    "sku": "asdf123",
    "name": "Lorem ipsum jacket",
    "price": 12.45,
    "discount": 10,
    "offerEnd": "October 5, 2020 12:11:00",
    "new": false,
    "rating": 4,
    "saleCount": 54,
    "category": ["fashion", "men"],
    "tag": ["fashion", "men", "jacket", "full sleeve"],
    "variation": [
      {
        "color": "white",
        "image": "/assets/img/product/fashion/1.jpg",
        "size": [
          {
            "name": "x",
            "stock": 3
          },
          {
            "name": "m",
            "stock": 2
          },
          {
            "name": "xl",
            "stock": 5
          }
        ]
      },
      {
        "color": "black",
        "image": "/assets/img/product/fashion/8.jpg",
        "size": [
          {
            "name": "x",
            "stock": 4
          },
          {
            "name": "m",
            "stock": 7
          },
          {
            "name": "xl",
            "stock": 9
          },
          {
            "name": "xxl",
            "stock": 1
          }
        ]
      },
      {
        "color": "brown",
        "image": "/assets/img/product/fashion/3.jpg",
        "size": [
          {
            "name": "x",
            "stock": 1
          },
          {
            "name": "m",
            "stock": 2
          },
          {
            "name": "xl",
            "stock": 4
          },
          {
            "name": "xxl",
            "stock": 0
          }
        ]
      }
    ],
    "image": [
      "/assets/img/product/fashion/1.jpg",
      "/assets/img/product/fashion/3.jpg",
      "/assets/img/product/fashion/6.jpg",
      "/assets/img/product/fashion/8.jpg",
      "/assets/img/product/fashion/9.jpg"
    ],
    "description": {
      "shortDescription": "Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur.",
      "fullDescription": "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?"
    }
  }

برای دریافت دسته‌بندی‌های هر ردیف میتوانیم از تابع JSON_EXTRACT استفاده کنیم:
SELECT 
    JSON_PRETTY(
  JSON_EXTRACT(data, "$.category")
    )
FROM
    experiments.productMetadata;

/* 
  [
    "fashion",
    "men"
  ]
  [
    "fashion",
    "women"
  ]
  [
    "fashion",
    "men"
  ]
*/
همانطور که مشاهده میکنید، تابع JSON_EXTRACT یک آرگومان دومی را نیز دریافت میکند که توسط آن میتوانیم path موردنظر را وارد کنیم و همانطور که عنوان شد، از $ برای دسترسی به سند جاری استفاده میکنیم. سپس در ادامه نام پراپرتی‌ای را که میخواهیم استخراج کنیم، تعیین کرده‌ایم. در اینجا چون ساختار ذخیره شده، به صورت شیء میباشد، به صورت مستقیم از $ و بعد از آن نقطه و سپس نام پراپرتی استفاده کرده‌ایم. میتوانیم عمق پیمایش را نیز بیشتر کنیم. به عنوان مثال برای دسترسی به المنت دوم از آرایه tag درون دیتا خواهیم داشت:
JSON_EXTRACT(data, "$.tag[1]")
JSON_EXTRACT(data, "$.description.shortDescription")

همچنین اگر کلید مقداری را که میخواهیم جستجو کنیم، بدانیم اما از کلید والد آن اطلاع نداشته باشیم، میتوانیم از * استفاده کنیم: 
SELECT 
JSON_EXTRACT(data, "$.*.shortDescription")
FROM experiments.productMetadata;

JSON_KEYS
از این تابع جهت دریافت کلیدهای top level یک شیء JSON استفاده میشود:
SELECT 
JSON_KEYS(data)
FROM experiments.productMetadata;

-- ["id", "new", "sku", "tag", "name", "image", "price", "rating", "category", "discount", "offerEnd", "saleCount", "variation", "description"]
-- ["id", "new", "sku", "tag", "name", "image", "price", "rating", "category", "discount", "saleCount", "variation", "description"]


همچنین میتوانیم path را نیز به عنوان آرگومان دوم آن تعیین کنیم: 
SELECT 
JSON_KEYS(data, "$.description")
FROM experiments.productMetadata;

-- ["fullDescription", "shortDescription"]
-- ["fullDescription", "shortDescription"]

JSON_CONTAINS 
از این تابع برای جستجو استفاده خواهیم کرد و همانطور که از نام آن پیداست، در صورت وجود مقدار مورد جستجو، خروجی ۱ خواهد بود:
SELECT 
    JSON_CONTAINS(data, "10", "$.discount")
FROM
    experiments.productMetadata;

-- 1
-- 0

JSON_CONTAINS_PATH
توسط این تابع میتوانیم بررسی کنیم که یک path یا یک یکسری path خاص درون JSON document وجود دارند یا خیر: 
SELECT 
JSON_CONTAINS_PATH(data, "one", "$.description", "$.address", "$.website")
FROM experiments.productMetadata;
آرگومان اول این تابع، داکیومنتی است که میخواهیم جستجو کنیم. برای آرگومان دوم، یکی از دو مقدار one یا all را میتوانیم تنظیم کنیم. در ادامه لیستی از pathهایی را که میخواهیم جستجو کنیم، وارد کرده‌ایم. در حالت one، اگر تنها یکی از pathها درون داکیومنت JSON وجود داشته باشند، خروجی ۱ خواهد بود. اگر one را به all تنظیم کنیم، یعنی باید تمامی pathها، درون داکیومنت وجود داشته باشند تا خروجی ۱ شود؛ در غیراینصورت خروجی ۰ خواهد بود. 

JSON_SEARCH  
توسط این تابع میتوانیم position مقدار مورد جستجو را درون داکیومنت JSON پیدا کنیم: 
SELECT 
    JSON_SEARCH(data, 'one', 'fashion')
FROM
    experiments.productMetadata;
    
-- "$.tag[0]"
-- "$.tag[0]"

مطالب
ویژگی Batching در EF Core
در EF 6.x به ازای هر عبارت insert/update/delete یکبار رفت و برگشت به بانک اطلاعاتی صورت می‌گیرد. به همین جهت کارآیی تعداد بالای ثبت، به روز رسانی و حذف رکوردها توسط آن پایین است. برای رفع این مشکل ویژگی Batching به EF Core اضافه شده‌است که توسط آن اینبار دسته‌ای از عبارات را به صورت یکجا و در طی یک رفت و برگشت، به سمت بانک اطلاعاتی ارسال می‌کند. به این ترتیب کارآیی و سرعت insert/update/delete آن به شدت افزایش خواهد یافت.


نحوه‌ی فعالسازی Batching در EF Core

Batching به صورت پیش فرض در EF Core بدون نیاز به هیچگونه تنظیم اضافه‌تری فعال است. اما اگر خواستید برای مثال، حالت پیش فرض EF 6.x را توسط آن شبیه سازی کنید، می‌توانید مقدار MaxBatchSize را به عدد 1 تنظیم نمائید (تا غیرفعال شود):
optionsBuilder.UseSqlServer(
   @"Server=(localdb)\mssqllocaldb;Database=Demo.Batching;Trusted_Connection=True;",
   options => options.MaxBatchSize(1)
);

مقدار پیش فرض MaxBatchSize را در کلاس SqlServerModificationCommandBatch می‌توانید مشاهده کنید:
public class SqlServerModificationCommandBatch : AffectedCountModificationCommandBatch
    {
        private const int DefaultNetworkPacketSizeBytes = 4096;
        private const int MaxScriptLength = 65536 * DefaultNetworkPacketSizeBytes / 2;
        private const int MaxParameterCount = 2100;
        private const int MaxRowCount = 1000;
در اینجا MaxRowCount همان MaxBatchSize پیش فرض است که به عدد 1000 تنظیم شده‌است. بنابراین اگر تنظیم options => options.MaxBatchSize(1) را ذکر نکنید، به معنای ارسال 1000 تایی دستورات insert/update/delete در طی یک درخواست به سمت سرور است.


آیا محدودیتی هم در مورد عملیات Batching وجود دارد؟

SQL Server به ازای هر batch تنها 2100 پارامتر را پشتیبانی می‌کند. در این حالت EF Core به صورت خودکار یک چنین کوئری‌های حجیمی را به چند Batch جهت تنظیم این محدودیت تقسیم خواهد کرد و در نهایت برنامه به مشکلی بر نمی‌خورد.


یک آزمایش: Batching پیش فرض به چه صورتی کار می‌کند و چه اثری را دارد؟

کدهای کامل این آزمایش را از اینجا می‌توانید دریافت کنید: Batching.zip
در اینجا کلاس Blog را به همراه Context متناظر با آن مشاهده می‌کنید:
    public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
    }

    public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(
                @"Server=(localdb)\mssqllocaldb;Database=Demo.Batching;Trusted_Connection=True;"/*,
                options => options.MaxBatchSize(2)*/
                );
            optionsBuilder.EnableSensitiveDataLogging();
        }
    }
در ابتدا MaxBatchSize را تنظیم نخواهیم کرد. یعنی از همان عدد 1000 پیش فرض استفاده می‌شود. تنظیم EnableSensitiveDataLogging نیز سبب می‌شود تا لاگ نهایی تهیه شده جهت نمایش، پرمحتواتر شود.
در این حالت اگر به روز رسانی‌ها (2 مورد) و ثبت‌های ذیل (6 مورد) را انجام دهیم:
            using (var db = new BloggingContext())
            {
                db.GetService<ILoggerFactory>().AddProvider(new MyLoggerProvider());

                // Modify some existing blogs
                var existing = db.Blogs.ToArray();
                existing[0].Url = "http://sample.com/blogs/dogs";
                existing[1].Url = "http://sample.com/blogs/cats";

                // Insert some new blogs
                db.Blogs.Add(new Blog { Name = "The Horse Blog", Url = "http://sample.com/blogs/horses" });
                db.Blogs.Add(new Blog { Name = "The Snake Blog", Url = "http://sample.com/blogs/snakes" });
                db.Blogs.Add(new Blog { Name = "The Fish Blog", Url = "http://sample.com/blogs/fish" });
                db.Blogs.Add(new Blog { Name = "The Koala Blog", Url = "http://sample.com/blogs/koalas" });
                db.Blogs.Add(new Blog { Name = "The Parrot Blog", Url = "http://sample.com/blogs/parrots" });
                db.Blogs.Add(new Blog { Name = "The Kangaroo Blog", Url = "http://sample.com/blogs/kangaroos" });

                db.SaveChanges();
            }
یک چنین خروجی SQL ایی تولید می‌شود:
Executed DbCommand (41ms) [Parameters=[@p1='57', @p0='http://sample.com/blogs/dogs' (Size = 4000), @p3='58', @p2='http://sample.com/blogs/cats' (Size = 4000), @p4='The Horse Blog' (Size = 4000), @p5='http://sample.com/blogs/horses' (Size = 4000), @p6='The Snake Blog' (Size = 4000), @p7='http://sample.com/blogs/snakes' (Size = 4000), @p8='The Fish Blog' (Size = 4000), @p9='http://sample.com/blogs/fish' (Size = 4000), @p10='The Koala Blog' (Size = 4000), @p11='http://sample.com/blogs/koalas' (Size = 4000), @p12='The Parrot Blog' (Size = 4000), @p13='http://sample.com/blogs/parrots' (Size = 4000), @p14='The Kangaroo Blog' (Size = 4000), @p15='http://sample.com/blogs/kangaroos' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Blogs] SET [Url] = @p0
WHERE [BlogId] = @p1;
SELECT @@ROWCOUNT;

UPDATE [Blogs] SET [Url] = @p2
WHERE [BlogId] = @p3;
SELECT @@ROWCOUNT;

DECLARE @inserted2 TABLE ([BlogId] int, [_Position] [int]);
MERGE [Blogs] USING (
VALUES (@p4, @p5, 0),
(@p6, @p7, 1),
(@p8, @p9, 2),
(@p10, @p11, 3),
(@p12, @p13, 4),
(@p14, @p15, 5)) AS i ([Name], [Url], _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT ([Name], [Url])
VALUES (i.[Name], i.[Url])
OUTPUT INSERTED.[BlogId], i._Position
INTO @inserted2;

SELECT [t].[BlogId] FROM [Blogs] t
INNER JOIN @inserted2 i ON ([t].[BlogId] = [i].[BlogId])
ORDER BY [i].[_Position];
در این دستورات موارد ذیل قابل توجه هستند:
- فقط یکبار Executed DbCommand مشاهده می‌شود.
- کل دستورات update و insert در طی یک درخواست و یک تراکنش به سمت بانک اطلاعاتی ارسال شده‌اند.
- ثبت دسته‌ای توسط merge using انجام شده‌است.
- در آخر نیز طبق معمول کار EF، شماره Idهای رکوردهای ثبت شده به سمت کلاینت بازگشت داده می‌شود.

در ادامه MaxBatchSize را به عدد 2 تنظیم می‌کنیم:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
   optionsBuilder.UseSqlServer(
     @"Server=(localdb)\mssqllocaldb;Database=Demo.Batching;Trusted_Connection=True;",
     options => options.MaxBatchSize(2)
    );
    optionsBuilder.EnableSensitiveDataLogging();
}
در این حالت اگر برنامه را اجرا کنیم، یک چنین خروجی قابل مشاهده خواهد بود:
Executed DbCommand (17ms) [Parameters=[@p1='65', @p0='http://sample.com/blogs/dogs' (Size = 4000), @p3='66', @p2='http://sample.com/blogs/cats' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Blogs] SET [Url] = @p0
WHERE [BlogId] = @p1;
SELECT @@ROWCOUNT;

UPDATE [Blogs] SET [Url] = @p2
WHERE [BlogId] = @p3;
SELECT @@ROWCOUNT;

Executed DbCommand (18ms) [Parameters=[@p0='The Horse Blog' (Size = 4000), @p1='http://sample.com/blogs/horses' (Size = 4000), @p2='The Snake Blog' (Size = 4000), @p3='http://sample.com/blogs/snakes' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DECLARE @inserted0 TABLE ([BlogId] int, [_Position] [int]);
MERGE [Blogs] USING (
VALUES (@p0, @p1, 0),
(@p2, @p3, 1)) AS i ([Name], [Url], _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT ([Name], [Url])
VALUES (i.[Name], i.[Url])
OUTPUT INSERTED.[BlogId], i._Position
INTO @inserted0;

SELECT [t].[BlogId] FROM [Blogs] t
INNER JOIN @inserted0 i ON ([t].[BlogId] = [i].[BlogId])
ORDER BY [i].[_Position];

Executed DbCommand (34ms) [Parameters=[@p0='The Fish Blog' (Size = 4000), @p1='http://sample.com/blogs/fish' (Size = 4000), @p2='The Koala Blog' (Size = 4000), @p3='http://sample.com/blogs/koalas' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DECLARE @inserted0 TABLE ([BlogId] int, [_Position] [int]);
MERGE [Blogs] USING (
VALUES (@p0, @p1, 0),
(@p2, @p3, 1)) AS i ([Name], [Url], _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT ([Name], [Url])
VALUES (i.[Name], i.[Url])
OUTPUT INSERTED.[BlogId], i._Position
INTO @inserted0;

SELECT [t].[BlogId] FROM [Blogs] t
INNER JOIN @inserted0 i ON ([t].[BlogId] = [i].[BlogId])
ORDER BY [i].[_Position];

Executed DbCommand (15ms) [Parameters=[@p0='The Parrot Blog' (Size = 4000), @p1='http://sample.com/blogs/parrots' (Size = 4000), @p2='The Kangaroo Blog' (Size = 4000), @p3='http://sample.com/blogs/kangaroos' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DECLARE @inserted0 TABLE ([BlogId] int, [_Position] [int]);
MERGE [Blogs] USING (
VALUES (@p0, @p1, 0),
(@p2, @p3, 1)) AS i ([Name], [Url], _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT ([Name], [Url])
VALUES (i.[Name], i.[Url])
OUTPUT INSERTED.[BlogId], i._Position
INTO @inserted0;

SELECT [t].[BlogId] FROM [Blogs] t
INNER JOIN @inserted0 i ON ([t].[BlogId] = [i].[BlogId])
ORDER BY [i].[_Position];
در این دستورات موارد ذیل قابل توجه هستند:
- اینبار تعداد 4 دستور Executed DbCommand مشاهده می‌شود ( برای انجام 2 به روز رسانی و 6 ثبت).
- هر batch بر اساس تنظیم MaxBatchSize به 2 دستور T-SQL محدود شده‌است که البته در انتها در حالت‌های insert، یک select هم برای بازگشت Idها به سمت کلاینت وجود دارد.
بنابراین اینبار بجای یکبار رفت و برگشت حالت قبل (استفاده از مقدار پیش فرض 1000 برای MaxBatchSize)، 4 بار رفت و برگشت به سمت بانک اطلاعاتی صورت گرفته‌است.

زمان کل انجام عملیات در حالت اول 41 میلی ثانیه و در حالت دوم 84 میلی ثانیه است که سرعت آن 51 درصد نسبت به حالت اول کاهش یافته‌است.
نظرات مطالب
EF Code First #11
با سلام
جناب نصیری من پس از خواندن مطالب واقعا مفید شما و چند تا مطلب دیگه تو سایتهای CodeProject, Stackoverflow ,Asp.NET , ... تقریبا گیج شدم بنابر این چند تا سوال دارم
1- وقتی از یک orm مثلا EF استفاده میکنم داشتن یک class library  به نام DAL و انتقال edmx یا کلاس‌های code first به اون اشکالی که نداره؟
2- لایه سرویس همان BLL هست؟ میتوان اونجا مستقیم به DbContext و توابع اون مثل add ، Delete و غیره دسترسی داشت؟
3- یه جا مثل اینکه فرموده بودید UoW خوبه استفاده کنیم و مخزن نه درسته ؟ اگه آره چرا و چطور ؟
4- من یه Classlibrary تشکیا میدم به اسم Entities و POCO‌های EF رو منتقل میکنم اونجا در آینده مشکل ساز که نیست؟
5- جای صحیح استفاده از الگوهای مخزن و UoW کجاست؟
6- میشه یه مثال که همه اینهارو که فرمودید رو رعایت کرده و مورد تایید شماست رو معرفی بفرمایید؟
بازخوردهای پروژه‌ها
تراز یک ستون بر اساس مقدار ستون دیگر
با سلام
در گزارشی می‌خواهم با توجه به مقدار ستون بستانکار، ستون شرح چپ چین یا راست چین شود.
مثلا اگر بستانکار مقدار داشت(مثبت بود)، ستون شرح چپ چین و در غیر اینصورت راست چین شود.
 columns.AddColumn(column =>
               {
                   column.PropertyName<VoucherRowPrintViewModel>(x => x.Title);
                   column.CellsHorizontalAlignment(PdfRpt.Core.Contracts.HorizontalAlignment.Left);
                   column.IsVisible(true);
                   column.Order(1);
                   column.Width(3);
                   column.HeaderCell("شرح");
               });
columns.AddColumn(column =>
               {
                   column.PropertyName<VoucherRowPrintViewModel>(x => x.Creditor);
                   column.CellsHorizontalAlignment(PdfRpt.Core.Contracts.HorizontalAlignment.Right);
                   column.IsVisible(true);
                   column.Order(4);
                   column.Width(2);
                   column.ColumnItemsTemplate(template =>
                   {
                       template.TextBlock();
                       template.DisplayFormatFormula(obj => string.Format("{0:n0}", obj));
                   });
                   column.AggregateFunction(aggregateFunction =>
                   {
                       aggregateFunction.NumericAggregateFunction(AggregateFunction.Sum);
                       aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                   });
                   column.HeaderCell("بستانکار");
               });

نظرات مطالب
نحوه تعریف Linked Server و دریافت اطلاعات از سروری دیگر
سلام  آقای جهانگیری
شما برای این کار یکسری تنظیمات را باید در Enterprise manager انجام بدهید
1-نصب Certificate در ویندوز خودتان(از طریق MMC)
2-فعال کردن Certificate در قسمت Enterprise Manager
3-پارامتر  Encrypt را هم در قسمت Connection String خود لحاظ کنید.
--------------------
اگر سئوالی بود بنده در خدمتم.
نظرات مطالب
NOSQL قسمت سوم
سلام
تشکر از مطالب خوبتون
اما چند تا سوال دارم.
1- از این سه تا پایگاه داده که در اخر نوشتید فکر کنم فقط MongoDB مجانی باشه. درسته؟
2- آیا دستورات در همه این پایگاه داده‌ها به همین صورت است؟
3- آیا همه سرورها و هاست‌ها از این پایگاه داده‌ها مانند MS SQL پشتیبانی می‌کنند و یا سرورهای خاص را باید پیدا کرد؟
تشکر
مطالب
جایگزین کردن jQuery با JavaScript خالص - قسمت چهارم - ایجاد تغییرات در DOM
Document Object Model و یا به اختصار DOM به ظهور زبان JavaScript  گره خورده‌است. این مدل به همراه یک API پیاده سازی شده‌ی با JavaScript است که امکان دسترسی به اسناد HTML را مسیر می‌کند. علاوه بر امکاناتی مانند انتخاب عناصر، کار با ویژگی‌ها و ذخیره‌ی اطلاعات که تاکنون بررسی کردیم، DOM API به همراه روش‌هایی برای ایجاد عناصر جدید، حذف عناصر موجود و جابجایی آن‌ها در صفحه می‌باشد. یکی از مهم‌ترین اهداف jQuery کار ساده‌تر با DOM است و تعداد متدهایی را که برای کار با DOM ارائه می‌کند، تاکنون کمتر از 20 درصد کل DOM API اصلی را پوشش می‌دهند.


حرکت دادن المان‌ها در صفحه

ابتدا قطعه کد HTML زیر را درنظر بگیرید:
  <body>
    <h2>Flavors</h2>
    <ul class="flavors">
      <li>chocolate</li>
      <li>strawberry</li>
      <li>vanilla</li>
    </ul>

    <h2>Types</h2>
    <ul class="types">
      <li>frozen yogurt</li>
      <li>custard</li>
      <li>Italian ice</li>
    </ul>

    <ul class="unassigned">
      <li>rocky road</li>
      <li>gelato</li>
    </ul>
  </body>
می‌خواهیم با تغییر DOM، به خروجی زیر برسیم که در آن لیست‌ها جابجا، تکمیل و یا خالی شده‌اند:
  <body>
    <h2>Types</h2>
    <ul class="types">
      <li>frozen yogurt</li>
      <li>Italian ice</li>
      <li>custard</li>
      <li>gelato</li>
    </ul>

    <h2>Flavors</h2>
    <ul class="flavors">
      <li>chocolate</li>
      <li>vanilla</li>
      <li>rocky road</li>
      <li>strawberry</li>
    </ul>

    <ul class="unassigned">
    </ul>
  </body>

حرکت دادن المان‌ها توسط jQuery
var $flavors = $('.flavors');
var $chocolate = $flavors.find('li').eq(0);
var $vanilla = $flavors.find('li').eq(2);
$chocolate.after($vanilla);
به این ترتیب vanilla به بعد از chocolate در لیست flavors منتقل می‌شود.
در ادامه می‌خواهیم لیست types را به همراه عنوان آن‌، به قبل از لیست flavors منتقل کنیم:
var $typesHeading = $('h2').eq(1);
$typesHeading.prependTo('body');
$typesHeading.after($('.types'));
متد prependTo سبب درج عنوان types دقیقا پس از تگ body می‌شود. سپس لیست types را پس از این عنصر جابجا شده اضافه می‌کنیم.
سپس در لیست unassigned ابتدا rocky road آن‌را یافته و به بالای strawberry در لیست flavors اضافه می‌کنیم. همچنین gelato آن‌را نیز یافته و به انتهای لیست types اضافه خواهیم کرد:
var $unassigned = $('.unassigned');
var $rockyRoad = $unassigned.find('li').eq(0);
var $gelato = $unassigned.find('li').eq(1);

$vanilla.after($rockyRoad);
$gelato.appendTo($('.types'));

حرکت دادن المان‌ها توسط جاوا اسکریپت خالص (سازگار با IE 8.0 به بعد)

در ابتدا می‌خواهیم المان vanilla را به قبل از المان strawberry حرکت دهیم. برای اینکار می‌توان از متد استاندارد insertBefore استفاده کرد:
var flavors = document.querySelector('.flavors');
var strawberry = flavors.children[1];
var vanilla = flavors.children[2];

flavors.insertBefore(vanilla, strawberry);
flavors در اینجا والد نودی است که قرار است جابجا شود. اولین پارامتری که به متد insertBefore ارسال می‌شود، المانی است که قرار است جابجا شود. دومین پارامتر آن «نود مرجع» است. چون می‌خواهیم vanilla را قبل از strawberry درج کنیم، المان strawberry نود مرجع خواهد بود.
سپس کار انتقال عنوان لیست types و خود آن به قبل از لیست flavors صورت می‌گیرد:
var headings = document.querySelectorAll('h2');
var flavorsHeading = headings[0];
var typesHeading = headings[1];
var typesList = document.querySelector('.types');

document.body.insertBefore(typesHeading, flavorsHeading);
document.body.insertBefore(typesList, flavorsHeading);
در اینجا ابتدا عنوان types، به ابتدای document.body منتقل می‌شود (چون والد این عنوان document.body است، متد insertBefore بر روی آن فراخوانی می‌شود). سپس می‌خواهیم خود typesList را نیز حرکت دهیم. به همین جهت نیاز به نود مرجع عنوان flavors است که به عنوان پارامتر دوم متد insertBefore ذکر شده‌است تا این لیست، پیش از آن درج شود.
در آخر می‌خواهیم آیتم‌های لیست unassigned را به لیست‌های مرتبط با آ‌ن‌ها انتقال دهیم:
flavors.insertBefore(document.querySelector('.unassigned > li'), strawberry); 
document.querySelector('.types').appendChild(document.querySelector('.unassigned > li'));
در اولین سطر، querySelector تعریف شده، اولین المان لیست یا همان rocky road را بازگشت می‌دهد. به این ترتیب المان rocky road لیست unassigned به لیست flavors منتقل می‌شود . به همین جهت flavors به عنوان والد متد insertBefore تعریف شده‌است. نود مرجع نیز strawberry است؛ زیرا می‌خواهیم rocky road را به پیش از آن منتقل کنیم.
در سطر دوم، چون هم اکنون المان rocky road از لیست unassigned حذف شده‌است، متد querySelector فراخوانی شده، اولین عنصر لیست یا همان gelato را بازگشت می‌دهد. این المان را توسط متد appendChild به انتهای لیست types اضافه خواهیم کرد. متد appendChild نیز همانند متد insertBefore نیاز به یک والد دارد که همان عنصری است که قرار است المان‌ها به آن افزوده شوند.


کپی کردن المان‌ها

  <ol class="numbers">
    <li>one</li>
    <li>two</li>
  </ol>
در جی‌کوئری برای تهیه‌ی یک کپی از این المان خواهیم داشت:
 // deep clone: return value is an exact copy
$('.numbers').clone();
اگر به این متد پارامتر true نیز ارسال شود، اطلاعات و همچنین رخ‌دادهای منتسب به آن نیز کپی می‌شوند. البته این کپی فقط شامل اطلاعات تدارک دیده شده‌ی توسط jQuery API است و نه خارج از آن.
و در جاوا اسکریپت خالص (سازگار با IE 8.0 به بعد) برای کپی کردن المان‌ها دو روش shallow و deep وجود دارد:
// shallow clone: return value is an empty <ol>
document.querySelector('.numbers').cloneNode();

// deep clone: return value is an exact copy of the tree
document.querySelector('.numbers').cloneNode(true);
Shallow clone به معنای کپی المان ol بدون فرزندان آن است. در حالت deep clone المان ol و تمام فرزندان آن با هم کپی می‌شوند.
باید دقت داشت که متد cloneNode آنچه را که مشاهده می‌کنید یا همان اصل markup را کپی می‌کند. بنابراین اگر از طریق جاوا اسکریپت تغییراتی را در آن شیء داده باشید در متد cloneNode لحاظ نمی‌شود.
بدیهی است المان‌های clone شده تا زمانیکه با متدهایی مانند insertBefore و یا appendChild به صفحه اضافه نشوند، در صفحه نمایان نخواهند شد.


ایجاد و حذف المان‌ها

فرض کنید می‌خواهیم به لیست flavors مثال ابتدای بحث، دو مورد جدید را اضافه کنیم.
روش افزودن المان‌های جدید توسط جی‌کوئری:
var $flavors = $('.flavors');

// add two new flavors
$('<li>pistachio</li>').appendTo($flavors);
$('<li>neapolitan</li>').appendTo($flavors);
و یا حذف یک آیتم موجود توسط جی‌کوئری:
// remove the "gelato" type
$('.types li:last').remove();
در اینجا last: اصطلاحا یک pseduo-class ابداعی توسط jQuery است که آنچنان کارآیی بالایی هم ندارد.

روش افزودن المان‌های جدید توسط جاوا اسکریپت خالص:
var flavors = document.querySelector('.flavors');

// add two new flavors
flavors.insertAdjacentHTML('beforeend', '<li>pistachio</li>')
flavors.insertAdjacentHTML('beforeend', '<li>neapolitan</li>')
و برای حذف آخرین آیتم یک لیست توسط جاوا اسکریپت خالص:
// remove the "gelato" type
document.querySelector('.types li:last-child').remove();
در اینجا last-child: یک CSS3 pseudo-class selector استاندارد است.
روش دیگر انجام اینکار به صورت زیر توسط متد removeChild است:
var gelato = document.querySelector('.types li:last-child');

// remove the "gelato" type
gelato.parentNode.removeChild(gelato);


کار با المان‌های متنی

در جی‌کوئری متد ()text آن امکان دریافت محتوای متنی و همچنین به روز رسانی آن‌را میسر می‌کند:
 $('.types li').eq(1).text('italian ice');
در اینجا متن دومین المان لیست types به italian ice با i کوچک به روز رسانی می‌شود.

در جاوا اسکریپت خالص، دو خاصیت textContent و همچنین innerText برای خواندن و یا به روز رسانی محتوای متنی عناصر مورد استفاده قرار می‌گیرند. برای مثال معادل قطعه کد جی‌کوئری فوق که از متد text استفاده می‌کند با جاوا اسکریپت خالص به صورت زیر است:
 document.querySelectorAll('.types li')[1].textContent = 'italian ice';
توسط querySelectorAll تمام liهای types یافت شده و سپس خاصیت textContent دومین عنصر آن با italian ice به روز رسانی شده‌است.
خاصیت innerText هرچند بر روی اینترفیس HTMLElement تعریف شده‌است، اما جزء هیچکدام از استانداردهای وب نیست؛ ولی توسط تمام مرورگرهای امروزی پشتیبانی می‌شود. در این حالت به روز رسانی متن توسط آن با خاصیت textContent دقیقا یکی است؛ اما خروجی آن برعکس حالت‌های قبل، متن رندر شده‌ی المان‌ها را بازگشت می‌دهد. برای مثال در اینجا شامل فاصله‌های پیش از این المان‌ها در markup نمی‌شود.
برای مثال این قسمتی از خروجی خاصیت textContent است:
   Flavors

      chocolate
      vanilla
      rocky road
      strawberry
اما در این همین حالت خروجی innerText به این صورت است:
Flavors

chocolate
vanilla
rocky road
strawberry
کار با محتوای HTML ایی رشته‌ای

گاهی از اوقات از سرور قطعه‌ای کد HTML ایی را دریافت می‌کنیم (که هنوز به صورت المان یا المان‌های DOM در نیامده‌است) و در سمت کلاینت می‌خواهیم آن‌را به قسمتی از صفحه اضافه کنیم. روش انجام اینکار در jQuery به صورت زیر است:
var container = '<h2>Containers</h2><ul><li>cone</li><li>cup</li></ul>';
$('<div>').html(container).appendTo('body');
ابتدا یک المان div جدید را ایجاد کرده‌ایم. سپس محتوای این div را با اطلاعات دریافتی از سرور مقدار دهی و در آخر آن‌را به انتهای body اضافه می‌کنیم.
روش دریافت محتوای رشته‌ای HTML قابل ارسال به سرور نیز به صورت زیر است:
  var contents = $('body').html();
روش انجام اینکار با جاوا اسکریپت خالص به صورت زیر است:
var div = document.createElement('div');
div.innerHTML = container;
document.body.appendChild(div);
در اینجا با استفاده از متد استاندارد createElement یک div جدید منقطع از DOM را ایجاد و سپس محتوای آن‌را توسط خاصیت innerHTML به HTML دریافتی از سرور تنظیم کرده‌ایم. در آخر این المان منقطع را توسط متد appendChild به انتهای document.body افزوده‌ایم.
روش خواندن این محتوای نهایی نیز به صورت زیر است:
var contents = document.body.innerHTML;
در حالت کار با جاوا اسکریپت خالص به خاصیت outerHTML یک المان نیز دسترسی داریم که خواندن و یا به روز رسانی آن، صرفا بر روی خود المان اصلی تاثیر می‌گذارد؛ اما innerHTML بر روی المان‌های فرزند این المان (محتوای آن) تاثیر گذار است.
مطالب
نکات امنیتی OWASP مخصوص برنامه نویس‌های دات نت
OWSAP ارگانی است غیرانتفاعی که هدف آن ترویج طراحی برنامه‌های امن وب است. در این راه هم مطالب آموزشی بسیار ارزشمندی را منتشر کرده است [+]. در لینک‌های زیر این مطالب از دیدگاه برنامه نویس‌های دات نت مورد بررسی قرار گرفته‌اند. هر چند مطابق آخرین گزارش WhiteHat که اخیرا منتشر شده [+]، تعداد Exploits مربوط به ASP.NET در مقایسه با PHP و جاوا بسیار کمتر بوده اما نیاز است تا با مشکلات عمومی موجود و راه‌حل‌های مرتبط بیشتر آشنا شد: