نظرات مطالب
ASP.NET MVC #18
سلام؛بنده هم همین مشکل رو دارم.
برای اینکه قسمت‌های مختلف که شما مطمئنا از ان اگاه نیستید و شاید بران تاثیر بگذاره رو نداشته باشید.
یک پروژه تازه رو باز کردم یک راست رفتم سراغ web.config این کد رو در قسمت system.web وارد کردم  
<authorization>
     <deny users="?" />
</authorization>
بعد بلافاصله یک کنترلر به نام Home ایجاد کردم و یک view برای index اد کردم...
قبل از اکشن index  هم  [AllowAnonymous] رو قرار دادم
ولی بعد از اجرا هنوز هم دسترسی ندارم ...
Access is denied.  

آیا دلیل و نکته است که رعایت نکرده ام؟
مطالب
افزودن تصدیق ایمیل به ASP.NET Identity در MVC 5
در پست قبلی نحوه سفارشی کردن پروفایل کاربران در ASP.NET Identity را مرور کردیم. اگر بیاد داشته باشید یک فیلد آدرس ایمیل به کلاس کاربر اضافه کردیم. در این پست از این فیلد استفاده میکنیم تا در پروسه ثبت نام ایمیل‌ها را تصدیق کنیم. بدین منظور پس از ثبت نام کاربران یک ایمیل فعالسازی برای آنها ارسال می‌کنیم که حاوی یک لینک است. کاربران با کلیک کردن روی این لینک پروسه ثبت نام خود را تایید می‌کنند و می‌توانند به سایت وارد شوند. پیش از تایید پروسه ثبت نام، کاربران قادر به ورود نیستند.


در ابتدا باید اطلاعات کلاس کاربر را تغییر دهید تا دو فیلد جدید را در بر گیرد. یک فیلد شناسه تایید (confirmation token) را ذخیره می‌کند، و دیگری فیلدی منطقی است که مشخص می‌کند پروسه ثبت نام تایید شده است یا خیر. پس کلاس ApplicationUser  حالا باید بدین شکل باشد.
public class ApplicationUser : IdentityUser
{
    public string Email { get; set; }
    public string ConfirmationToken { get; set; }
    public bool IsConfirmed { get; set; }
}
اگر پیش از این کلاس ApplicationUser را تغییر داده اید، باید مهاجرت‌ها را فعال کنید و دیتابیس را بروز رسانی کنید. حالا می‌توانیم از این اطلاعات جدید در پروسه ثبت نام  استفاده کنیم و برای کاربران ایمیل‌های تاییدیه را بفرستیم.
private string CreateConfirmationToken()
{
    return ShortGuid.NewGuid();
}
 
private void SendEmailConfirmation(string to, string username, string confirmationToken)
{
    dynamic email = new Email("RegEmail");
    email.To = to;
    email.UserName = username;
    email.ConfirmationToken = confirmationToken;
    email.Send();
}
 
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        string confirmationToken = CreateConfirmationToken();
        var user = new ApplicationUser()
        {
            UserName = model.UserName,
            Email = model.Email,
            ConfirmationToken = confirmationToken, 
                IsConfirmed = false };
        var result = await UserManager.CreateAsync(user, model.Password);
        if (result.Succeeded)
        {
            SendEmailConfirmation(model.Email, model.UserName, confirmationToken);
            return RedirectToAction("RegisterStepTwo", "Account");
        }
        else
        {
            AddErrors(result);
        }
    }
 
    // If we got this far, something failed, redisplay form
    return View(model);
}
برای تولید شناسه‌های تایید (tokens) از کلاسی بنام ShortGuid استفاده شده است. این کلاس یک مقدار GUID را encode می‌کند که در نتیجه آن مقدار خروجی کوتاه‌تر بوده و برای استفاده در URL‌ها ایمن است. کد این کلاس را از این وبلاگ گرفته ام. پس از ایجاد حساب کاربری باید شناسه تولید شده را به آن اضافه کنیم و مقدار فیلد IsConfirmed را به false تنظیم کنیم. برای تولید ایمیل‌ها من از Postal استفاده می‌کنم. Postal برای ساختن ایمیل‌های دینامیک شما از موتور Razor استفاده می‌کند. می‌توانید ایمیل‌های ساده (plain text) یا HTML بسازید، عکس و فایل در آن درج و ضمیمه کنید و امکانات بسیار خوب دیگر. اکشن متد RegisterStepTwo تنها کاربر را به یک View هدایت می‌کند که پیامی به او نشان داده می‌شود.
بعد از اینکه کاربر ایمیل را دریافت کرد و روی لینک تایید کلیک کرد به اکشن متد RegisterConfirmation باز می‌گردیم.
private bool ConfirmAccount(string confirmationToken)
{
    ApplicationDbContext context = new ApplicationDbContext();
    ApplicationUser user =  context.Users.SingleOrDefault(u => u.ConfirmationToken == confirmationToken);
    if (user != null)
    {
        user.IsConfirmed = true;
        DbSet<ApplicationUser> dbSet = context.Set<ApplicationUser>();
        dbSet.Attach(user);
        context.Entry(user).State = EntityState.Modified;
        context.SaveChanges();
 
        return true;
    }
    return false;
}
 
[AllowAnonymous]
public ActionResult RegisterConfirmation(string Id)
{
    if (ConfirmAccount(Id))
    {
        return RedirectToAction("ConfirmationSuccess");
    }
    return RedirectToAction("ConfirmationFailure");
}
متد ConfirmAccount سعی می‌کند کاربری را در دیتابیس پیدا کند که شناسه تاییدش با مقدار دریافت شده از URL برابر است. اگر این کاربر پیدا شود، مقدار خاصیت IsConfirmed را به true تغییر می‌دهیم و همین مقدار را به تابع باز می‌گردانیم. در غیر اینصورت false بر می‌گردانیم. اگر کاربر تایید شده است، می‌تواند به سایت وارد شود. برای اینکه مطمئن شویم کاربران پیش از تایید ایمیل شان نمی‌توانند وارد سایت شوند، باید اکشن متد Login را کمی تغییر دهیم.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindAsync(model.UserName, model.Password);
        if (user != null && user.IsConfirmed)
        {
            await SignInAsync(user, model.RememberMe);
            return RedirectToLocal(returnUrl);
        }
        else
        {
            ModelState.AddModelError("", "Invalid username or password.");
        }
    }
 
    // If we got this far, something failed, redisplay form
    return View(model);
}
تنها کاری که می‌کنیم این است که به دنبال کاربری می‌گردیم که فیلد IsConfirmed آن true باشد. اگر مقدار این فیلد false باشد کاربر را به سایت وارد نمی‌کنیم و پیغام خطایی نمایش می‌دهیم.
همین. این تمام چیزی بود که برای اضافه کردن تصدیق ایمیل به اپلیکیشن خود نیاز دارید. از آنجا که سیستم ASP.NET Identity با Entity Framework مدیریت می‌شود و با مدل Code First ساخته شده، سفارشی کردن اطلاعات کاربران و سیستم عضویت ساده‌تر از همیشه است.



توضیحاتی درباره کار با Postal

اگر به متد SendEmailConfirmation دقت کنید خواهید دید که آبجکتی از نوع Email می‌سازیم (که در اسمبلی‌های Postal وجود دارد) و از آن برای ارسال ایمیل استفاده می‌کنیم. عبارت "RegEmail" نام نمایی است که باید برای ساخت ایمیل استفاده شود. این متغیر از نوع dynamic است، مانند خاصیت ViewBag. بدین معنا که می‌توانید مقادیر مورد نظر خود را بصورت خواص دینامیک روی این آبجکت تعریف کنید. از آنجا که Postal از موتور Razor استفاده می‌کند، بعدا در View ایمیل خود می‌توانید به این مقادیر دسترسی داشته باشید.
در پوشه Views پوشه جدیدی بنام Emails بسازید. سپس یک فایل جدید با نام RegEmail.cshtml در آن ایجاد کنید. کد این فایل را با لیست زیر جایگزین کنید.
To: @ViewBag.To
From: YOURNAME@gmail.com
Subject: Confirm your registration

Hello @ViewBag.UserName,
Please confirm your registration by following the link bellow.

@Html.ActionLink(Url.Action("RegisterConfirmation", "Account", new { id = @ViewBag.ConfirmationToken }), "RegisterConfirmation", "Account", new { id = @ViewBag.ConfirmationToken }, null)
این فایل، قالب ایمیل‌های شما خواهد بود. ایمیل‌ها در حال حاظر بصورت plain text ارسال می‌شوند. برای اطلاعات بیشتر درباره ایمیل‌های HTML و امکانات پیشرفته‌تر به سایت پروژه Postal  مراجعه کنید.

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

  • ViewBag.To آدرس ایمیل گیرنده را نشان می‌دهد.
  • ViewBag.UserName نام کاربر جاری را نمایش می‌دهد.
  • ViewBag.ConfirmationToken شناسه تولید شده برای تایید کاربر است.
در این قالب لینکی به متد RegisterConfirmation در کنترلر Account وجود دارد که شناسه تایید را نیز با پارامتری بنام id انتقال می‌دهد.

یک فایل ViewStart.cshtml_ هم در این پوشه بسازید و کد آن را با لیست زیر جایگزین کنید.
@{ Layout = null; /* Overrides the Layout set for regular page views. */ }
مسیرراه‌ها
Entity framework code-first
شروع به کار با EF Code first

برای تکمیل بحث نیاز است تغییرات انجام شده از نگارش 4 به 6 را نیز مد نظر داشته باشید:


آشنایی با مباحث Migrations



آشنایی با تنظیمات نگاشت‌ها به دو روش استفاده از ویژگی‌ها و Fluent API



اعتبارسنجی و بررسی استثناءها



ردیابی تغییرات



استفاده از SQL خام و بانک‌های اطلاعاتی متفاوت

      نکات مهم کوئری نویسی در EF



      استفاده از EF در WPF


      لایه بندی پروژه‌های EF Code first



      پروژ‌ه‌های انجام شده با EF Code first

       
      مطالب
      آشنایی با ساختار IIS قسمت هفتم

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

         همانطور که در قسمت‌های قبلی گفتیم سرور IIS آماده خصوصی سازی و کار بر اساس علائق شماست؛ ولی توجه داشته باشید حذف تمامی ماژول‌ها ممکن است اثرات جانبی هم داشته باشد. در اینجا ما ماژول هایی را به شما معرفی می‌کنیم که بدانید کار هر ماژول چیست تا مثلا با حذف ماژولی، امنیت وب سایت خود را به خطر نیندازید :

      ماژول‌های سودمند یا utility

      نام ماژول:

      UriCacheModule

      توضیح:

      این ماژول نوعی کش برای URLها به شمار می‌رود. موقعی که url درخواست می‌شود، اطلاعات در اولین درخواست خوانده شده و کش می‌شود و اگر دوباره همان url درخواست شود، بدون خواندن تنظیمات و بر اساس تنظیمات قبلی، کار url مربوطه را انجام میدهد تا اطلاعات پیکربندی تغییر کند و بر اساس اطلاعات جدید، خود را به روز کند.

      تگ قابل پیکربندی:

      لازم ندارد

      وابستگی:

      ندارد

      اثرات حذف آن:

      کارایی سیستم کاهش می‌یابد و سیستم مجبور است برای هر درخواست فایل پیکربندی را بخواند.

      نام ماژول :

      FileCacheModule

      توضیح :

      فایل هندلِ فایل‌هایی که قبلا در سرور باز شده‌اند را کش می‌کند تا در صورت نیاز در دفعات بعدی سریعتر عمل کند.

      تگ قابل پیکربندی :

      لازم ندارد .

      وابستگی :

      ندارد.

      اثرات حذف آن :

      کارایی سیستم کاهش می‌یابد. سیستم در هر اجرای دستور مربوط به فایل‌ها باید فایل هندل را به دست آورد.

      نام ماژول :

      TokenCacheModule

      توضیح :

      توکن‌های امنیتی ویندوز که پسوردهایی بر اساس authentication schemes هستند را کش می‌کند (anonymous authentication, basic authentication, IIS client certificate authentication )

      تگ قابل پیکربندی :

      لازم ندارد

      وابستگی :

      ندارد

      اثرات حذف آن :

      کارایی سیستم به شدت پایین می‌آید. کاربر باید با هر درخواستی لاگین کند. یکی از اصلی‌ترین ضربه‌ها با حذف این ماژول این است که اگر مثلا یک پسورد از یک فایل html محافظت می‌کند و این صفحه به 50 تصویر ارجاع دارد، 51 بار باید درخواست لاگین اجرا گردد یا شاید هم بدتر

      MANAGED ENGINE: ASP.NET INTEGRATION

      نام ماژول :

      ManagedEngine

      توضیح :

      مدیریت ماژول‌های native و مدیریت شده

      تگ قابل پیکربندی :


      وابستگی :

      ندارد

      اثرات حذف آن :

      مشخصا غیرفعال شدن asp.net integrated و غیر فعال شدن تمامی ماژول‌ها و هندلر‌های تگ وب کانفیگ یا داخل فایل کانفیگ IIS که در مقالات قبلی به تفصیل بیان کرده‌ایم.

      IIS 7 NATIVE MODULES

      نام ماژول :

      HttpCacheModule

      توضیح :

      مدیریت کش خروجی در htttp.sys بر اساس پیکربندی مثل تعریف سایز کش و ...

      تگ قابل پیکربندی :

      System.webServer/caching

      وابستگی :

      ندارد.

      اثرات حذف آن :

      محتوا دیگر به صورت کرنل مد، کش نمی‌شود و کش پروفایل هم ندید گرفته می‌شود و احتمالا بر کارآیی و استفاده از منابع هم اثر می‌گذارد.

      نام ماژول :

      DynamicCompressionModule

      توضیح :

      پیاده سازی in-memory compression در محتوای پویا

      تگ قابل پیکربندی :

      system.webServer/httpCompression and system.webServer/urlCompression.

      وابستگی :

      وابستگی ندارد چرا که به طور پیش فرض غیرفعال است.

      نام ماژول :

      StaticCompressionModule

      توضیح :

      پیادسازی فشرده سازی در محتوای ایستا و برای فایل‌های سیستمی از نوع in memory

      تگ قابل پیکربندی :

      system.webServer/httpCompression and system.webServer/urlCompression

      وابستگی :

      ندارد.

      اثرات حذف آن :

      در صورت عدم فشرده سازی بر مصرف ترافیک تاثیر گذار است.

      نام ماژول :

      DefaultDocumentModule

      توضیح :

      پیاده سازی یک لیست سند پیش فرض. درخواست‌ها مدام پشت سر هم می‌آیند و این درخواست‌های پشت سرهم، به سند پیش فرض هدایت می‌شوند. همان پنجره ای که شما به ترتیب فایل‌های index.htm,index.asp,default.aspx و... را تعیین می‌کنید.

      تگ قابل پیکربندی :

      system.webServer/defaultDocument

      وابستگی :

      ندارد.

      اثرات حذف آن :

      درخواست را به ریشه هدایت می‌کند. مثلا برای localhost صفحه 404 باز میگرداند و اگر directoryBrowsing فعال باشد لیستی از دایرکتوری ریشه را باز میگرداند.

      نام ماژول :

      DirectoryListingModule

      توضیح :

      پیادی سازی لیستی از محتویات یک دایرکتوری

      تگ قابل پیکربندی :

      system.webServer/directoryBrowse

      وابستگی :

      ندارد.

      اثرات حذف آن :

      اگر این ماژول و ماژول قبلی غیرفعال باشند response نهایی خالی است.

      نام ماژول :

      ProtocolSupportModule

      توضیح :

      پیاده سازی اختصاصی از response header

      پیاده سازی تنظیمات trace و HTTP verbs.

      پیاده سازی تنظیمات مربوطه به keep-alive بر اساس پیکربندی

      تگ قابل پیکربندی :

      system.webServer/httpProtocol

      وابستگی :

      ندارد.

      اثرات حذف آن :

      بازگرداندن پیام خطای "405 Method not allowed".

      نام ماژول :

      HttpRedirectionModule

      توضیح :

      پیاده سازی عملیات انتقال یا redirect

      تگ قابل پیکربندی :

      system.webServer/httpRedirect

      وابستگی :

      ندارد.

      اثرات حذف آن :

      خطر امنیتی: اگر منابعی با redirect کردن محافظت می‌شوند، از این پس در دسترسند.

      نام ماژول :

      ServerSideIncludeModule

      توضیح :

      حمایت از فایل shtm یا shtml و ...

      تگ قابل پیکربندی :

      system.webServer/serverSideInclude

      وابستگی :

      ندارد.

      اثرات حذف آن :

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

      نام ماژول :

      StaticFileModule

      توضیح :

      فایل‌های ایستا را به همراه پسوند ارسال می‌کند. مثل jpg,html و نوع محتوا را بر اساس staticContent/mimeMap پیکربندی می‌کند.

      تگ قابل پیکربندی :

      system.webServer/staticContent

      وابستگی :

      ندارد.

      اثرات حذف آن :

      فایل‌های ایستا دیگر ارائه نشده و به جای آن خطای 404 بازگشت داده می‌شود.

      نام ماژول :

      AnonymousAuthenticationModule

      توضیح :

      پیاده سازی سیستم شناسایی افراد ناشناس. همانطور که میدانید در یک وب سایت حداقل محتوایی برای افرادی بدون داشتن اکانت هم وجود دارد. برای اینکار یک شیء httpuser ایجاد می‌کند.

      تگ قابل پیکربندی :

      system.webServer/security/authentication/anonymousAuthentication

      وابستگی :

      ندارد.

      اثرات حذف آن :

      حداقل باید یک سیستم امنیتی برای شناسایی یا authenticate وجود داشته باشد. httpuser یک ساختار داده ای در IIS می‌باشد و در صورت نبودن هیچ سیستم شناسایی وجود نداشته و در نبود شیء httpuser سیستم خطای 401.2 را تولید می‌کند.

      نام ماژول :

      CertificateMappingAuthenticationModule

      توضیح :

      مجوز SSL را به Active Directory نگاشت می‌کند.

      تگ قابل پیکربندی :

      system.webServer/security/authentication/clientCertificateMappingAuthentication

      وابستگی :

      برای اینکه این ماژول وظیفه خود را انجام دهد باید تنظیمات SSL انجام شود و همچنین سیستم IIS جزئی از دامنه Active directory باشد

      اثرات حذف آن :

      درخواست‌ها، نرمال رسیدگی میشوند انگار SSL وجود ندارد.

      نام ماژول :

      BasicAuthenticationModule

      توضیح :

      پیاده سازی پایه‌ای و روتین شناسایی کاربران بر اساس آن چیزی که در استانداد زیر آمده است

      RFC 2617.

      تگ قابل پیکربندی :

      system.webServer/security/authentication/basicAuthentication

      وابستگی :

      None.

      اثرات حذف آن :

      حداقل باید یک سیستم امنیتی برای شناساسایی یا authenticate وجود داشته باشد. httpuser یک ساختار داده‌ای در IIS می‌باشد و در صورت نبود، هیچ سیستم شناسایی یافت نشده و نبود شیء  httpuser در سیستم، خطای 401.2 را تولید می‌کند.

      نام ماژول :

      WindowsAuthenticationModule

      توضیح :

      ((windows Authentication (NTLM or Negotiate (Kerberos

      تگ قابل پیکربندی :

      system.webServer/security/authentication/windowsAuthentication

      وابستگی :

      ندارد.

      اثرات حذف آن :

      حداقل باید یک سیستم امنیتی برای شناسایی یا authenticate وجود داشته باشد. httpuser یک ساختار داده ای در IIS می‌باشد و در صورت نبود، هیچ سیستم شناسایی یافت نشده و نبود شیء httpuser در سیستم، خطای 401.2 را تولید می‌کند.

      نام ماژول :

      DigestAuthenticationModule

      توضیح :

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

      RFC 2617 .

      تگ قابل پیکربندی :

      system.webServer/security/authentication/digestAuthentication

      وابستگی :

      IIS باید بخشی از دامنه Active Directory باشد.

      اثرات حذف آن :

      حداقل باید یک سیستم امنیتی برای شناسایی یا authenticate وجود داشته باشد. httpuser یک ساختار داده ای در IIS می‌باشد و در صورت نبود، هیچ سیستم شناسایی یافت نشده و نبود شیء httpuser در سیستم، خطای 401.2 را تولید می‌کند.

      نام ماژول :

      IISCertificateMappingAuthenticationModule

      توضیح :

      پیاده سازی نگاشت مجوزهای IIS، نگهداری و ذخیره اطلاعات همه نگاشت‌ها و مجوزهای کاربری چون SSL client certificates  

      تگ قابل پیکربندی :

      system.webServer/iisClientCertificateMappingAuthentication

      وابستگی :

      اطلاعات SSL به همراه دریافت client certificates جهت پیکربندی این ماژول

      اثرات حذف آن :

      حداقل باید یک سیستم امنیتی برای شناسایی یا authenticate وجود داشته باشد. httpuser یک ساختار داده ای در IIS می‌باشد و در صورت نبود، هیچ سیستم شناسایی یافت نشده و نبود شیء httpuser در سیستم، خطای 401.2 را تولید می‌کند.

      نام ماژول :

      UrlAuthorizationModule

      توضیح :

      پیاده سازی authorization بر اساس قوانین پیکربندی شده

      تگ قابل پیکربندی :

      system.webServer/security/authorization

      وابستگی :

      ندارد.

      اثرات حذف آن :

      محتواهای محافظت شده توسط authorization دیگر محافظت نمی‌شوند.

      نام ماژول :

      IsapiModule

      توضیح :

      پیاده سازی ISAPI Extension 

      تگ قابل پیکربندی :

      system.webServer/isapiCgiRestriction

      وابستگی :

      ندارد.

      اثرات حذف آن :

      هندلر‌های معرفی شده در بخش IsapiModule و تگ handlers دیگر اجرا نمی‌شوند

      نام ماژول :

      IsapiFilterModule

      توضیح :

      پیاده سازی ISAPI filter 

      تگ قابل پیکربندی :

      system.webServer/isapiFilters

      وابستگی :

      ندارد.

      اثرات حذف آن :

      اگر برنامه ای از ISAPI filter استفاده می‌کند، در اجرا دچار مشکل خواهد شد.

      نام ماژول :

      IpRestrictionModule

      توضیح :

      یک سیستم تشخیص دسترسی  بر اساس آی پی‌های ورژن4

      تگ قابل پیکربندی :

      system.webServer/security/ipSecurity

      وابستگی :

      IPv4 stack باید نصب شود.

      اثرات حذف آن :

      کلاینت هایی که IP هایشان در IPsecurity لیست شده‌اند ندید گرفته میشوند

      نام ماژول :

      RequestFilteringModule

      توضیح :

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

      تگ قابل پیکربندی :

      system.webServer/security/requestFiltering

      وابستگی :

      ندارد.

      اثرات حذف آن :

      دیگر قوانین امنیتی اجرا نخواهند شد و سبب وجود مشکلات امنیتی میشود.

      نام ماژول :

      CustomLoggingModule

      توضیح :

      پیاده سازی اینترفیس ILogPlugin در سمت IIS، به مشتریان اجازه میدهد تا لاگ‌های خود را توسعه دهند. هر چند این روش توصیه نمی‌شود و توصیه کارشناس مایکروسافت استفاده از یک ماژول دست نویس از نوع RQ_LOG_REQUEST می باشد.

      Implements the ILogPlugin interface on top of IIS. ILogPlugin is a previous COM implementation that allowed customers to extend IIS logging. We do not not recommend extending IIS using this interface. Instead, customers should write a module and subscribe to the RQ_LOG_REQUEST notification.

      تگ قابل پیکربندی :

      system.webServer/httpLogging and system.applicationhost/sites/site/logFile/customLogPluginClsid

      وابستگی :

      ندارد.

      اثرات حذف آن :

      مسلما پلاگین‌های‌های این اینترفیس از کار می‌‌افتند که سیستم ODBC Logging هم جز آن است.

      نام ماژول :

      CustomErrorModule

      توضیح :

      پیاده سازی مدیریت خطاهای ویژه

      تگ قابل پیکبرندی :

      system.webServer/httpErrors

      وابستگی :

      None.

      اثرات حذف آن :

      در صورتی که خطایی از هسته باشد، نتیجه یک صفحه، با توضیح مختصری از خطا خواهد بود. در غیر این صورت اگر خطا از برنامه یا کامپوننتی باشد جزئیات خطا فاش خواهد شد

      نام ماژول :

      HttpLoggingModule

      توضیح :

      پیاده سازی سیستم logging استاندارد http.sys

      تگ قابل پیکربندی :

      system.applicationHost/log and system.webServer/httpLogging

      وابستگی :

      ندارد.

      اثرات حذف آن :

      از کار افتادن سیستم لاگ

      نام ماژول :

      FailedRequestsTracingModule

      توضیح :

      پیاده سازی سیستم ردیابی درخواست‌های ناموفق و اجرای قوانین، طبق پیکربندی

      تگ قابل پیکربندی :

      system.webServer/tracing and system.webServer/httpTracing

      وابستگی :

      ندارد.

      اثرات حذف آن :

      Tracing http requests will no longer work.

      نام ماژول :

      RequestMonitorModule

      توضیح :

      پیاده سازی IIS Run-time State and Control Interface یا به اختصار RSCA . به کاربران اجازه می‌دهد از اطلاعات، حین اجرا، کوئری بگیرند. مثل درخواست درحال اجرای جاری، آغاز به کار یا توقف وب سایت و دامنه‌های اپلیکیشن در حال اجرای جاری

      تگ قابل پیکربندی :

      ندارد.

      وابستگی :

      ندارد.

      اثرات حذف آن :

      ابزارهای مرتبط با این موضوع از کار می‌افتند

      نام ماژول :

      CgiModule

      توضیح :

      پیاده سازی CGI در سمت IIS

      تگ قابل پیکبرندی :

      system.webServer/cgi and system.webServer/isapiCgiRestriction

      وابستگی :

      ندارد.

      اثرات حذف آن :

      برنامه‌های CGI متوقف می‌شوند

      نام ماژول :

      TracingModule

      توضیح :

      پیاده سازی سیستم ردیابی ETW

      تگ قابل پیکربندی :

      system.webServer/httpTracing

      وابستگی :

      ندارد.

      اثرات حذف آن :

      باعث از کار افتادن سیستم مربوطه می‌شود

      نام ماژول :

      ConfigurationValidationModule

      توضیح :

      اعتبارسنجی تنظیمات برنامه ASP.Net که به حالت integrate انتقال یافته است

      تگ قابل پیکربندی :

      system.webServer/Validation

      وابستگی :

      ندارد.

      اثرات حذف آن :

      عدم اعتبارسنجی و در نتیجه عدم نمایش خطاها

      MANAGED MODULES:

      نام ماژول :

      OutputCache

      توضیح :

      پیاده سازی output caching

      تگ قابل پیکربندی :

      system.web/caching/outputCache

      وابستگی :

      نیاز به ManagedEngine .

      اثرات حذف آن :

      عدم اجرای output cache

      نام ماژول :

      Session

      توضیح :

      مدیریت سشن ها

      تگ قابل پیکربندی :

      system.web/sessionState

      وابستگی :

      نیاز به ManagedEngine . 

      اثرات حذف آن :

      سشن‌ها از دسترس خارج می‌شوند.

      نام ماژول :

      WindowsAuthentication

      توضیح :

      اینجا 

      تگ قابل پیکربندی :

      system.web/authentication

      وابستگی :

      نیاز به ManagedEngine .

      اثرات حذف آن :

      این حالت قابل اجرا نخواهد بود

      نام ماژول :

      FormsAuthentication

      توضیح :

      اینجا 

      تگ قابل پیکربندی :

      system.web/authentication

      وابستگی :

      نیاز به ManagedEngine .

      اثرات حذف آن :

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

      نام ماژول :

      DefaultAuthentication

      توضیح :

      اطمینان از وجود شی Authentication در context مربوطه 

      تگ قابل پیکربندی :

      system.web/authentication

      وابستگی :

      نیاز به ManagedEngine .  

      اثرات حذف آن :

      اگر مد Forms authentication انتخاب شده باشد بر روی بعضی از کاربران ناشناس کار نخواهد کرد و رویداد DefaultAuthentication.OnAuthenticate اجرا نخواهد شد.

      نام ماژول :

      RoleManager

      توضیح :

      اینجا 

      تگ قابل پیکربندی :


      وابستگی :

      نیاز به ManagedEngine .

      اثرات حذف آن :

      این قابلیت در دسترس نمی‌باشد

      نام ماژول :

      UrlAuthorization

      توضیح :

      اینجا 

      تگ قابل پیکربندی :

      system.web/authorization.

      وابستگی :

      نیاز به ManagedEngine .  

      اثرات حذف آن :

      باعث از کار افتادن asp.net authorization و فاش شدن بعضی اطلاعات و همچنین دیگر تهدیدات امنیتی

      نام ماژول :

      AnonymousIdentification

      توضیح :

      اینجا 

      تگ قابل پیکربندی :


      وابستگی :

      نیاز به ManagedEngine . 

      اثرات حذف آن :

      The anonymous identification feature used by the ASP.NET Profile will not work.

      نام ماژول :

      Profile

      توضیح :

      اینجا 

      تگ قابل پیکربندی :


      وابستگی :

      ManagedEngine module must be installed.

      اثرات حذف آن :

      ASP.Net Profile از کار خواهد افتاد

      نام ماژول :

      UrlMappingsModule

      توضیح :

       تبدیل یک Url واقعی به یک Url کاربرپسند 

      تگ قابل پیکبرندی :


      وابستگی :

      نیاز به ManagedEngine .

      اثرات حذف آن :

      نگاشت Url‌ها صورت نمی‌گیرد

      مطالب
      یکپارچه سازی TortoiseSVN و YouTrack
      پیش نیاز
      اگر در مورد TortoiseSVN و سورس کنترل اطلاعات پایه ندارید، کتاب  مدیریت فایلهای یک پروژه نرم افزاری با استفاده از Subversion  آقای نصیری را مطالعه کنید و همچنین سیستم پیگیری خطای  YouTrack را نگاهی بیاندازید (البته اگر اطلاعی ندارید).

      مقدمه
      هنگام کار روی یک پروژه، باگ ها، وظیفه‌ها و موضوعاتی به شما واگذار می‌شود که باید انها را انجام دهید. هنگام commit کردن تغییرات، برای مشخص شدن اینکه تغییرات مربوط به کدام Bug-Id بوده است بود است باید سیستم Bug/Issue Tracker رو با سورس کنترل یکپارچه کنیم. 

      یکپارچه سازی TortoiseSVN و YouTrack
      1- روی یک نسخه کاری پروژه راست کلیک، از منوی TortoiseSVN گزینه Properties را انتخاب کنید.

      گزینه Properties در TortoiseSVN

      2- از پنجر باز شده دکمه New، گزینه Other را انتخاب کنید. در پنجره باز شده از منوی کشویی مربوط به Property Name، مقادیر خصلت‌های زیر را تنظیم کنید:
      bugtraq:url : آدرس YouTrack Sever که به این صورت وارد می‌شود: %http://localhost:8080/issue/%BUGID
      bugtraq:message : درو اقع الگویی پیامی هست که برای نگهداری Bug-Id استفاده می‌شود و باید شامل کلمه %BUGID% باشد. مثلا: %Issue: %BUGID
      bugtraq:number : مقدار این خصلت را false وارد کنید؛ چون Bug-Idهای‌های YouTrack می‌توانند شامل عدد و حروف باشند.

      دیالوگ Propeties


      بعد از اینکه این سه خصلت را مقداردهی کرید، تغییرات را Commit کنید. همانطور که می‌بینید یک Textbox (بالا، سمت راست) اضافه شده که محل وارد کردن Bug-Id مربوط به تغییرات است. از این پس، می‌توانید Bug-Id یا Issue-Id‌های مربوط به هر تغییرات را در آن Textbox وارد کنید.


      همچنین تغییرات در پلاگین AnkhSVN در ویژال استودیو نیز اعمال می‌شود:


      اکنون، در متن commitها شماره Bud-Id نیز ذکر شده است.


      نکته 1: اگر YouTrack روی یک سرور نصب هست، بجای localhost نام کامپیوتر سرور یا آی پی آن را وارد کنید. پورت 8080 نیز بصورت پیش فرض است و اگر هنگام نصب آن را تغییر داده اید، اینجا نیز آنرا تغییر دهید.
      نکته 2: خصلت bugtraq:message یک الگوی پیام از شما می‌گیرد؛ یعنی الگو را تحت هر شکلی می‌توان وارد کرد. بعنوان مثال الگو را به این شکل وارد کنید: "برای مشاهده جزئیات بیشتر به Bug-Id شماره %BUGID% مراجعه کنید."
      نکته 3: اگر خصلت bugtraq:number مقدارش true باشد، برای وارد کردن Bug-Id فقط از عدد می‌توانید استفاده کنید. بصورت پیش فرض مقدار این خصلت true است.
      نکته 4: می‌توانید این تنظیمات را در یک فایل Export کنید و در بقیه پروژه ها، با یک مرحله و بسادگی آنرا Import کنید.
      خصلت‌های دیگری نیز می‌توان برروی مخزن کد اعمال کرد که از حوزه این مقاله خارج است. همچنین تنظمیات اختیاری جانبی دیگری نیز برای یکپارچه سازی وجود دارند. برای دیدن این تنظمیات روی نسخه کاری راست کلیک، از منوی TortoiseSVN گزینه Properties را انتخاب کنید و از پنجره باز شده روی دکمه New و گزینه ( Bugtraq (Issue tracker integration  را انتخاب کنید.

      برای اطلاعات بیشتر در مورد این تنظیمات، داکیومنت یکپارچه سازی با سیستم‌های Bug tracking / Issue Tracking  را مطالعه کنید. 
      مطالب
      مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت هشتم - ساده سازی معرفی سرویس‌ها توسط Scrutor
      قابلیت‌های قرار گرفته‌ی در اسمبلی Microsoft.Extensions.DependencyInjection که پایه‌ی تزریق وابستگی‌های برنامه‌های مبتنی بر NET Core. را ارائه می‌دهد، برای پیاده سازی اکثر پروژه‌ها کافی است. اما اگر از نگارش‌های پیشین ASP.NET MVC به ASP.NET Core مهاجرت کرده باشید، حتما با قابلیت‌های ویژه‌ی اسکن اسمبلی‌های موجود در IoC Containers ثالث، جهت ساده سازی معرفی سرویس‌های برنامه به سیستم تزریق وابستگی‌ها، آشنایی دارید. برای مثال StructureMap قابلیت اسکن اسمبلی‌های موجود در برنامه و معرفی اینترفیس‌ها و سرویس‌های موجود در آن‌را به Container خود دارد:
      var container = new Container(x =>
                  {
                      x.Scan(scanner =>
                      {
                          scanner.AssemblyContainingType<IOrderHandler>();
                          // connects `IAccounting` to `Accounting` and `ISales` to `Sales` automatically.
                          scanner.WithDefaultConventions();
                      });
                  });
      و یا AutoFac نیز به همین صورت:
      builder.RegisterAssemblyTypes(myAssembly)
          .Where(t => t.IsAssignableTo<IMyInterface>())
          .AsImplementedInterfaces();
      البته می‌توان مجددا به تمام این قابلیت‌ها رسید؛ به شرطی‌که سیستم تزریق وابستگی‌های پایه‌ی NET Core. را با یکی از IoC Containers ثالث به طور کامل تعویض کنیم. اگر قصد چنین تعویض پایه‌ای را ندارید و هنوز قصد دارید از همان Microsoft.Extensions.DependencyInjection استفاده کنید، اما تعدادی متد الحاقی جدید تعریف شده‌ی بر فراز آن، کار اسکن کردن اسمبلی‌ها را مانند قبل انجام دهند، می‌توان از کتابخانه‌ی کمکی Scrutor استفاده کرد. این کتابخانه، جایگزین سیستم تزریق وابستگی‌های توکار برنامه‌های NET Core. نیست؛ بلکه صرفا مکمل آن است.


      دریافت و نصب کتابخانه‌ی کمکی Scrutor

      کتابخانه‌ی کمکی Scrutor سورس باز بوده و بسته‌ی NuGet آن توسط یکی از دستورات زیر به پروژه افزوده می‌شود:
      > Install-Package Scrutor
      > dotnet add package Scrutor
      و یا به صورت مدخلی جدید در فایل csproj:
      <Project Sdk="Microsoft.NET.Sdk.Web">
        <ItemGroup>
          <PackageReference Include="Scrutor" Version="3.0.2" />
        </ItemGroup>
      </Project>


      ثبت و معرفی ساده‌تر سرویس‌ها بر اساس قواعد نامگذاری آن‌ها توسط Scrutor

      فرض کنید تعدادی سرویس را به صورت زیر تعریف کرده‌اید:
      namespace CoreIocServices
      {
          public interface IFoo
          {
              void Run();
          }
      
          public class Foo : IFoo
          {
              public void Run()
              {
                  throw new System.NotImplementedException();
              }
          }
      
          public interface IBar
          {
              void Add();
          }
      
          public class Bar : IBar
          {
              public void Add()
              {
                  throw new System.NotImplementedException();
              }
          }
      
      
          public interface IBaz
          {
              void Stop();
          }
      
          public class Baz : IBaz
          {
              public void Stop()
              {
                  throw new System.NotImplementedException();
              }
          }
      }
      روش متداول معرفی آن‌ها به IoC Container برنامه به صورت زیر است:
      services.AddScoped<IFoo, Foo>();
      services.AddScoped<IBar, Bar>();
      services.AddScoped<IBaz, Baz>();
      و هرچقدر تعداد سرویس‌های برنامه بیشتر شود، سطرهای فوق نیز بیشتر خواهند شد.
      در اینجا در حین تعریف سرویس‌های فوق این روش نامگذاری رعایت شده‌است: هر اینترفیس، نامش یک I بیشتر از نام کلاس مشتق شده‌ی از آن دارد؛ مانند اینترفیس IFoo و کلاس Foo. کتابخانه‌ی StructureMap که در ابتدای بحث معرفی شد، کار اسکن و اتصال یک چنین سرویس‌هایی را با تعریف scanner.WithDefaultConventions انجام می‌دهد. معادل آن با Scrutor به صورت زیر است:
      namespace CoreIocSample02
      {
          public class Startup
          {
              public void ConfigureServices(IServiceCollection services)
              {
                  services.Scan(scan =>
                      //scan.FromCallingAssembly()
                      scan.FromAssemblyOf<IFoo>()
                          .AddClasses()
                          .AsMatchingInterface()
                          .WithScopedLifetime());
      تعریف فوق به این معنا است:
      - scan.FromAssemblyOf کار اسکن اسمبلی را انجام می‌دهد که نوع IFoo در آن قرار دارد. اگر از scan.FromCallingAssembly استفاده کنیم، به این معنا است که کار اسکن را دقیقا از همین اسمبلی فراخوان کدهای جاری، شروع کن. اما چون IFoo تعریف شده، در یک پروژه و اسمبلی دیگر قرار دارد، به همین جهت نیاز به ذکر صریح اسمبلی آن نیز هست.
      - AddClasses یعنی تمام کلاس‌های public, non-abstract را به لیست services اضافه کن.
      - AsMatchingInterface یعنی بر اساس قرارداد نامگذاری IClassName و ClassName، اتصالات سرویس‌ها را انجام بده.
      بجای آن می‌توان از AsImplementedInterfaces نیز استفاده کرد. این حالت برای زمانی مناسب است که یک کلاس، چندین اینترفیس را پیاده سازی کند (مثلا کلاس TestService اینترفیس‌های ITestService و IService را پیاده سازی کرده باشد) و علاقمند باشید به ازای هر اینترفیس، یکبار سرویس آن نیز ثبت شود؛ کاری مانند تنظیمات زیر:
      services.AddScoped<ITestService, TestService>();
      services.AddScoped<IService, TestService>();
      یا حتی می‌توان از متد ()<As<T نیز استفاده کرد. در اینجا به Scrutor گفته می‌شود که تمام کلاس‌های یافت شده را بر اساس نوع سرویس T ثبت و معرفی کن. البته اگر کلاسی نتواند نوع اینترفیس T را پیاده سازی کند، در زمان اجرا با استثناء مواجه خواهید شد.
      - WithScopedLifetime نیز طول عمر این سرویس‌های اضافه شده را مشخص می‌کند. در اینجا می‌توان WithTransientLifetime و WithSingletonLifetime را نیز ذکر کرد.

      بنابراین همانطور که ملاحظه می‌کنید، هنوز هم همان سیستم Microsoft.Extensions.DependencyInjection برقرار است؛ اما با وجود متد الحاقی جدید Scan، کار تعاریف سرویس‌های برنامه به شدت ساده می‌شود.


      کار با وهله‌های کلاس‌های سرویس‌ها بجای اینترفیس‌های آن توسط Scrutor

      می‌خواهیم مثال سوم قسمت ششم «چگونه بجای اینترفیس‌ها، یک وهله از کلاسی مشخص را از سیستم تزریق وابستگی‌ها درخواست کنیم؟» را توسط Scrutor پیاده سازی کنیم:
      namespace CoreIocServices
      {
          public interface IService { }
          public class Service1 : IService { }
          public class Service2 : IService { }
          public class Service : IService { }
      }
      در حالت متداول آن می‌توان از روش زیر نیز استفاده کرد:
      services.AddTransient<Service1>();
      services.AddTransient<Service2>();
      services.AddTransient<Service>();
      که با افزایش تعداد کلاس‌های سرویس برنامه به همین نحو نیز افزایش خواهند یافت. معادل این تنظیمات با Scrutor به صورت زیر است:
      namespace CoreIocSample02
      {
          public class Startup
          {
              public void ConfigureServices(IServiceCollection services)
              {
                  services.Scan(scan =>
                    //scan.FromCallingAssembly()
                    scan.FromAssemblyOf<IService>()
                        .AddClasses()
                        .AsSelf()
                        .WithTransientLifetime());
      در اینجا اسمبلی حاوی IService اسکن خواهد شد و سپس تمام کلاس‌های public, non-abstract آن AsSelf (ثبت پیاده سازی خود کلاس به عنوان سرویس) با طول عمر Transient به لیست services اضافه می‌شوند و یا اگر صرفا تعدادی سرویس مشخص مد نظر بود می‌توان به صورت زیر عمل کرد:
      services.Scan(scan =>
                     scan.AddTypes(new[] { typeof(Service1), typeof(Service2) })
                         .AsSelf()
                         .WithTransientLifetime());
      متدهایی که در Scrutor، یک پیاده سازی را به عنوان سرویس معرفی می‌کنند، شامل این موارد هستند:
      AsSelf: معادل ()<services.AddTransient<TestService است. در این حالت کلاس‌هایی که اینترفیسی را پیاده سازی نمی‌کنند و یا در کل مایل هستید که از طریق تزریق وابستگی‌ها در دسترس باشند، می‌توان توسط متد AsSelf به سیستم معرفی کرد.
      AsSelfWithInterfaces: معادل تنظیمات زیر است:
      services.AddSingleton<TestService>();
      services.AddSingleton<ITestService>(x => x.GetRequiredService<TestService>());
      services.AddSingleton<IService>(x => x.GetRequiredService<TestService>());
      فرض کنید کلاس TestService اینترفیس‌های ITestService و IService را پیاده سازی کرده باشد. با استفاده از AsSelfWithInterfaces، یکبار پیاده سازی خود سرویس به سیستم معرفی می‌شود، سپس به ازای هر اینترفیس، از همان وهله‌ی TestService برای وهله سازی سرویس‌های ITestService و IService نیز استفاده می‌شود.


      روش‌های متفاوت اسکن اسمبلی‌ها در Scrutor

      Scrutor به همراه روش‌های متعددی برای تعریف اسمبلی یا اسمبلی‌هایی است که باید اسکن شوند و نمونه‌ای از آن‌را با FromAssemblyOf بررسی کردیم:
      services.Scan(scan =>
                    //scan.FromCallingAssembly()
                    scan.FromAssemblyOf<IService>()
      سایر موارد آن به شرح زیر هستند:
      الف) FromAssemblyOf<>, FromAssembliesOf : اسمبلی یا اسمبلی‌هایی که نوع یا نوع‌های تعیین شده را به همراه دارند، اسکن می‌کند.
      ب) FromCallingAssembly, FromExecutingAssembly, FromEntryAssembly کار اسکن اسمبلی‌های فراخوان، اسمبلی که هم اکنون در حال اجرا است و اسمبلی آغازین برنامه را انجام می‌دهند.
      ج) FromAssemblyDependencies: تمام اسمبلی‌هایی را که وابسته‌ی به اسمبلی معرفی شده‌ی به آن هستند، اسکن می‌کند.
      د) FromApplicationDependencies, FromDependencyContext: تمام اسمبلی‌هایی را که توسط برنامه، ارجاعی به آن‌ها وجود دارند، اسکن می‌کند.


      انتخاب دقیق‌تر کلاس‌ها و سرویس‌های مدنظر توسط Scrutor

      شاید عملکرد کلی متد AddClasses مدنظر شما نباشد و نیاز به انتخاب دقیق‌تری از سرویس‌های اسکن شده را داشته باشید؛ برای این مورد نیز Scrutor روش‌های زیر را ارائه می‌دهد. برای مثال خود کلاس AddClasses دارای overloadهای زیر نیز هست:
          public interface IImplementationTypeSelector : IAssemblySelector, IFluentInterface
          {
              IServiceTypeSelector AddClasses();
              IServiceTypeSelector AddClasses(bool publicOnly);
              IServiceTypeSelector AddClasses(Action<IImplementationTypeFilter> action);
              IServiceTypeSelector AddClasses(Action<IImplementationTypeFilter> action, bool publicOnly);
          }
      حالت پیش‌فرض آن انتخاب تمام کلاس‌های public, non-abstract است. اگر پارامتر publicOnly را با false مقدار دهی کنید، internal/private nested classes را نیز انتخاب می‌کند. پارامتر action ای که در اینجا درنظر گرفته شده، جهت فیلتر کردن سرویس‌های انتخابی است که تعدادی از مثال‌های آن‌را در زیر بررسی می‌کنیم:
      services.Scan(scan => scan
                    .FromAssemblyOf<IService>()
                      .AddClasses(classes => classes.AssignableTo<IService>())
      // .AddClasses(classes => classes.InNamespaces("MyApp")) 
      // .AddClasses(classes => classes.Where(type => type.Name.EndsWith("Repository")) 
                          .AsImplementedInterfaces()
                          .WithTransientLifetime());
      در اینجا در حالت اول، کلاس‌هایی که صرفا اینترفیس IService را پیاده سازی کرده باشند، انتخاب می‌شوند. حالت دوم آن، انتخاب‌ها را به یک فضای نام محدود می‌کند و حالت سوم اگر نام کلاسی به Repository ختم شود، آن‌را به عنوان سرویس انتخاب خواهد کرد.


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

      یکی از مزیت‌های طراحی یک برنامه با درنظر گرفتن الگوی تزریق وابستگی‌ها، امکان جایگزین کردن سرویس‌های پیش‌فرض آن با سرویس‌های دیگری است. فرض کنید کتابخانه‌ای ارائه شده و از الگوریتم هش کردن X استفاده کرده‌است؛ اما شما علاقمندید تا از الگوریتم Y بجای آن استفاده کنید. اگر این کتابخانه وهله‌ی الگوریتم هش کردن را از طریق تزریق وابستگی‌ها تامین کرده باشد، فقط کافی است در ابتدای معرفی تنظیمات تزریق وابستگی‌های آن، سرویس الگوریتم هش کردن موجود را با نمونه‌ی خاص خودتان جایگزین کنید.
      اکنون فرض کنید پیش از استفاده‌ی از Scrutor، تعدادی سرویس را به روش متداولی ثبت و معرفی کرده‌اید:
      services.AddTransient<ITransientService, TransientService>();
      services.AddScoped<IScopedService, ScopedService>();
      حال که قرار است متد Scan آن، سرویس‌های یک اسمبلی را به لیست موجود اضافه کند، به سرویس‌های زیر می‌رسد:
      public class TransientService : IFooService {}
      public class AnotherService : IScopedService {}
       رفتار آن با سرویس‌های معادلی که از پیش ثبت شده‌اند چگونه باید باشد؟ برای مدیریت این مساله، متد UsingRegistrationStrategy پیش بینی شده‌است:
      services.Scan(scan =>
                      scan.FromAssemblyOf<IFoo>()
                          .AddClasses()
                          .UsingRegistrationStrategy(RegistrationStrategy.Skip)
                          .AsMatchingInterface()
                          .WithScopedLifetime());
      و پارامتر دریافتی آن یک چنین امضایی را دارد:
      namespace Scrutor
      {
          public abstract class RegistrationStrategy
          {
              public static readonly RegistrationStrategy Skip;
              public static readonly RegistrationStrategy Append;
              protected RegistrationStrategy();
              public static RegistrationStrategy Replace();
              public static RegistrationStrategy Replace(ReplacementBehavior behavior);
              public abstract void Apply(IServiceCollection services, ServiceDescriptor descriptor);
          }
      }
      - حالت Append آن که حالت پیش‌فرض نیز هست، تمام سرویس‌های یافت شده را به لیست IServiceCollection اضافه می‌کند؛ صرفنظر از اینکه پیشتر ثبت شده‌است یا خیر.
      - حالت Skip آن، سرویسی را تکراری ثبت نمی‌کند. یعنی اگر سرویسی پیشتر در مجموعه‌ی IServiceCollection موجود بود، مجددا آن‌را ثبت نمی‌کند.

      سپس نوبت به متدهای Replace می‌رسد که یک چنین پارامتری را قبول می‌کنند:
      namespace Scrutor
      {
          [Flags]
          public enum ReplacementBehavior
          {
              Default = 0,
              ServiceType = 1,
              ImplementationType = 2,
              All = 3
          }
      }
      - در حالت استفاده‌ی از Replace(​ReplacementBehavior.​ServiceType)، اگر سرویسی پیشتر در لیست IServiceCollection ثبت شده باشد، آن‌را حذف کرده و سپس نمونه‌ی جدید را ثبت می‌کند (ثبت سرویس بر اساس اینترفیس و پیاده سازی آن).
      - در حالت استفاده‌ی از Replace(​ReplacementBehavior.​ImplementationType)، اگر پیاده سازی کلاسی پیشتر در لیست IServiceCollection ثبت شده باشد، آن‌را حذف کرده و سپس نمونه‌ی جدید را ثبت می‌کند (ثبت سرویس صرفا بر اساس نام کلاس آن).
      - حالت Replace(​ReplacementBehavior.All) هر دو حالت قبل را با هم شامل می‌شود.


      امکان ترکیب چندین استراتژی جستجو با هم توسط Scrutor

      در یک برنامه‌ی واقعی غیرممکن است که بخواهید تمام کلاس‌ها را با یک طول عمر، اسکن و ثبت کنید. برای این منظور می‌توان از قابلیت فیلتر کردن کلاس‌ها که در مورد آن بحث شد و همچنین امکان ترکیب زنجیر وار حالت‌های مختلف اسکن، استفاده کرد:
      services.Scan(scan => scan 
        .FromAssemblyOf<CombinedService>() 
          .AddClasses(classes => classes.AssignableTo<ICombinedService>()) // Filter classes 
            .AsSelfWithInterfaces() 
            .WithSingletonLifetime() 
       
          .AddClasses(x=> x.AssignableTo(typeof(IOpenGeneric<>))) // Can close generic types 
            .AsMatchingInterface() 
       
          .AddClasses(x=> x.InNamespaceOf<MyClass>()) 
            .UsingRegistrationStrategy(RegistrationStrategy.Replace()) // Defaults to ReplacementBehavior.ServiceType 
            .AsMatchingInterface() 
            .WithScopedLifetime() 
       
        .FromAssemblyOf<DatabaseContext>()   // Can load from multiple assemblies within one Scan() 
          .AddClasses()  
            .AsImplementedInterfaces() 
      );
      مطالب
      ویژگی های کمتر استفاده شده در NET. - بخش ششم

      #Execute VB code via C

      می توان از طریق #C، ماکروهای Visual Basic مورد استفاده‌ی در Office را تولید کرد.
      static void AddChartButton( Workbook workBook,
                                  Worksheet xlWorkSheetNew,
                                  Range currentRange,  int macroId,
                                  int startRow, int endRow,
                                  int startCol, int endCol,
                                  string buttonImagePath )
      {
          var cell = currentRange.Next;
          var width = cell.Width;
          var height = 24;
          var left = cell.Left;
          var top = System.Math.Max( cell.Top + cell.Height - height, 0 );
          var button = xlWorkSheetNew.Shapes.AddPicture( buttonImagePath,
                                                          MsoTriState.msoFalse,
                                                          MsoTriState.msoCTrue,
                                                          left, top, width, height );
          var module = workBook.VBProject.VBComponents.Add( vbext_ComponentType.vbext_ct_StdModule );
          module.CodeModule.AddFromString( GetMacro( macroId,
                                                      startRow, endRow,
                                                      startCol, endCol ) );
          button.OnAction = "Macro" + macroId;
      }
      
      static string GetMacro( int macroId,
                              int startRow,  int endRow,
                              int startCol, int endCol )
      {
          var sb = new StringBuilder();
          var range = "ActiveSheet.Range(Cells(" + startRow + "," + startCol + "), Cells(" + endRow + "," + endCol + ")).Select";
          sb.AppendLine( "Sub Macro" + macroId + "()" );
          sb.AppendLine( "On Error Resume Next" );
          sb.AppendLine( range );
          sb.AppendLine( "ActiveSheet.Shapes.AddChart.Select" );
          sb.AppendLine( "ActiveChart.ChartType = xlColumn" );
          sb.AppendLine( "ActiveChart.SetSourceData Source:=" + range );
          sb.AppendLine( "On Error GoTo 0" );
          sb.AppendLine( "End Sub" );
          return sb.ToString();
      }

      و برای استفاده از آن می‌توان مانند مثال زیر عمل کرد:

      var excelApp = new Microsoft.Office.Interop.Excel.Application();
      var fileName = @"C:\Users\Vahid\Desktop\VBA.xlsm";
      var workBook = excelApp.Workbooks.Open( fileName );
      var sheet = workBook.Sheets[1];
      
      AddChartButton( workBook,
                      sheet,
                      sheet.Range["B1"],
                      1231,
                      1,
                      10,
                      1,
                      2,
                      @"C:\Users\Vahid\Desktop\BarChart.png");
      
      excelApp.DisplayAlerts = false;
      workBook.Close( true,
                      fileName );

      خروجی مثال بالا


      نکته: در صورتیکه بعد از اجرای برنامه، خطای " programmatic access to visual basic project is not trusted"  رخ داد از این طریق می‌توانید مشکل را حل کنید.

      File -> Options -> Trust Center -> Trust Center Settings -> Macro Settings -> Trust Access to the VBA Project object model


      volatile

      کلمه کلیدی volatile نشان می‌دهد که یک فیلد ممکن است توسط چندین thread به صورت همزمان تغییر کند. فیلدهایی که به عنوان volatile تعریف می‌شوند، شامل بهینه سازی کامپایلر برای دسترسی از طریق تنها یک thread قرار نمی‌گیرند و بروزرسانی مقدار فعلی این فیلد را در تمامی زمان‌ها، تضمین می‌کند.
      class Program
      {
          volatile bool _shouldPartyContinue = true;
      
          static void Main()
          {
              var firstDimension = new Program();
              var secondDimension = new Thread( firstDimension.StartPartyInAnotherDimension );
              secondDimension.Start( firstDimension );
              Thread.Sleep( 5000 );
              firstDimension._shouldPartyContinue = false;
              Console.WriteLine( "Party Finish" );
          }
      
          void StartPartyInAnotherDimension( object input )
          {
              var currentDimensionInput = (Program)input;
              Console.WriteLine( "let the party begin" );
              while ( currentDimensionInput._shouldPartyContinue ) {}
              Console.WriteLine( "Party ends: (" );
          }
      }
      نکته: اگر متغیر shouldPartyContinue به وسیله volatile علامت گذاری نشده بود، برنامه در حالت Release (که گزینه Optimize code تیک داشته باشد) هیچگاه به پایان نمی‌رسید.

      ::global

      وقتی که یک عضو توسط موجودیت دیگری با همان نام مخفی شده باشد، با استفاده از کلمه کلیدی ::global (فضای نام سراسری) قابلیت دسترسی به آن امکان پذیر می‌شود.
      class Program
      {
          public static void Main(string[] args)
          {
              Console.WriteLine( Number );  // Error
              global::System.Console.WriteLine( "Console: " + Console ); //OK
          }
      
          public class System { }
      
          // Define a constant called ‘Console’ to cause more problems.
          const int Console = 7;
          const int Number = 67;
      }
      همانطور که در مثال بالا مشاهده می‌کنید، به دلیل وجود ثابت Console و کلاس System امکان دسترسی به متد WriteLine وجود ندارد، برای در دسترس قرار گرفتن آن باید از ::global استفاده کرد.

      DebuggerDisplayAttribute

      با استفاده از  DebuggerDisplayAttribute  می‌توانید نحوه نمایش یک فیلد یا یک کلاس را در پنجره متغیر دیباگر مشخص کنید.
      [DebuggerDisplay( "{DebuggerDisplay}" )]
      public class DebuggerDisplayTest
      {
          public string FirstName { get; set; }
      
          public string LastName { get; set; }
      
          public int Age { get; set; }
      
          [DebuggerBrowsable( DebuggerBrowsableState.Never )]
          string DebuggerDisplay => $"{FirstName} {LastName} {Age} years old";
      }
      و بعد از استفاده‌ی از آن، خروجی زیر بدست می‌آید:

      همچنین شما می‌توانید عبارات مختلفی را به صورت مستقیم در این attribute استفاده کنید.
      [DebuggerDisplay( "Age {Age > 0 ? Age : 25}" )]
      public class DebuggerDisplayTest
      {
       //...
      }
      اگر مقدار پروپرتی Age بیشتر از 0 باشد، مقدار Age و در غیراینصورت 25 نشان داده می‌شود.
      مطالب
      استفاده از EF در اپلیکیشن های N-Tier : قسمت ششم
      در قسمت قبل رویکرد‌های مختلف برای حذف موجودیت‌های منفصل را بررسی کردیم. در این قسمت مدیریت همزمانی یا Concurrency را بررسی خواهیم کرد.


      فرض کنید می‌خواهیم مطمئن شویم که موجودیتی که توسط یک کلاینت WCF تغییر کرده است، تنها در صورتی بروز رسانی شود که شناسه (token) همزمانی آن تغییر نکرده باشد. به بیان دیگر شناسه ای که هنگام دریافت موجودیت بدست می‌آید، هنگام بروز رسانی باید مقداری یکسان داشته باشد.

      مدل زیر را در نظر بگیرید.


      می‌خواهیم یک سفارش (order) را توسط یک سرویس WCF بروز رسانی کنیم در حالی که اطمینان حاصل می‌کنیم موجودیت سفارش از زمانی که دریافت شده تغییری نکرده است. برای مدیریت این وضعیت دو رویکرد تقریبا متفاوت را بررسی می‌کنیم. در هر دو رویکرد از یک ستون همزمانی استفاده می‌کنیم، در این مثال فیلد TimeStamp.

      • در ویژوال استودیو پروژه جدیدی از نوع WCF Service Library بسازید و نام آن را به Recipe6 تغییر دهید.
      • روی نام پروژه کلیک راست کنید و گزینه Add New Item را انتخاب کنید. سپس گزینه‌های Data -> Entity Data Model را برگزینید. از ویزارد ویژوال استودیو برای اضافه کردن مدل جاری و جدول Orders استفاده کنید. در EF Designer روی فیلد TimeStamp کلیک راست کنید و گزینه Properties را انتخاب کنید. سپس مقدار CuncurrencyMode آنرا به Fixed تغییر دهید.
      • فایل IService1.cs را باز کنید و تعریف سرویس را مطابق لیست زیر بروز رسانی کنید.
      [ServiceContract]
      public interface IService1
      {
          [OperationContract]
          Order InsertOrder();
          [OperationContract]
          void UpdateOrderWithoutRetrieving(Order order);
          [OperationContract]
          void UpdateOrderByRetrieving(Order order);
      }

      • فایل Service1.cs را باز کنید و پیاده سازی سرویس را مطابق لیست زیر تکمیل کنید.
      public class Service1 : IService1
      {
          public Order InsertOrder()
          {
              using (var context = new EFRecipesEntities())
              {
                  // remove previous test data
                  context.Database.ExecuteSqlCommand("delete from [orders]");
                  var order = new Order
                  {
                      Product = "Camping Tent",
                      Quantity = 3,
                      Status = "Received"
                  };
                  context.Orders.Add(order);
                  context.SaveChanges();
                  return order;
              }
          }
      
          public void UpdateOrderWithoutRetrieving(Order order)
          {
              using (var context = new EFRecipesEntities())
              {
                  try
                  {
                      context.Orders.Attach(order);
                      if (order.Status == "Received")
                      {
                          context.Entry(order).Property(x => x.Quantity).IsModified = true;
                          context.SaveChanges();
                      }
                  }
                  catch (OptimisticConcurrencyException ex)
                  {
                      // Handle OptimisticConcurrencyException
                  }
              }
          }
      
          public void UpdateOrderByRetrieving(Order order)
          {
              using (var context = new EFRecipesEntities())
              {
                  // fetch current entity from database
                  var dbOrder = context.Orders
                  .Single(o => o.OrderId == order.OrderId);
                  if (dbOrder != null &&
                      // execute concurrency check
                      StructuralComparisons.StructuralEqualityComparer.Equals(order.TimeStamp, dbOrder.TimeStamp))
                  {
                      dbOrder.Quantity = order.Quantity;
                      context.SaveChanges();
                  }
                  else
                  {
                      // Add code to handle concurrency issue
                  }
              }
          }
      }


      • برای تست این سرویس به یک کلاینت نیاز داریم. پروژه جدیدی از نوع Console Application به راه حل جاری اضافه کنید و کد آن را مطابق لیست زیر تکمیل کنید. با کلیک راست روی نام پروژه و انتخاب گزینه Add Service Reference سرویس پروژه را هم ارجاع کنید. دقت کنید که ممکن است پیش از آنکه بتوانید سرویس را ارجاع کنید نیاز باشد روی آن کلیک راست کرده و از منوی Debug گزینه Start Instance را انتخاب کنید تا وهله از سرویس به اجرا در بیاید.
      class Program
      {
          static void Main(string[] args)
          {
              var service = new Service1Client();
              var order = service.InsertOrder();
              order.Quantity = 5;
              service.UpdateOrderWithoutRetrieving(order);
              order = service.InsertOrder();
              order.Quantity = 3;
              service.UpdateOrderByRetrieving(order);
          }
      }
      اگر به خط اول متد ()Main یک breakpoint اضافه کنید و اپلیکیشن را اجرا کنید می‌توانید افزودن و بروز رسانی یک Order با هر دو رویکرد را بررسی کنید.


      شرح مثال جاری

      متد ()InsertOrder داده‌های پیشین را حذف می‌کند، سفارش جدیدی می‌سازد و آن را در دیتابیس ثبت می‌کند. در آخر موجودیت جدید به کلاینت باز می‌گردد. موجودیت بازگشتی هر دو مقدار OrderId و TimeStamp را دارا است که توسط دیتابیس تولید شده اند. سپس در کلاینت از دو رویکرد نسبتا متفاوت برای بروز رسانی موجودیت استفاده می‌کنیم.

      در رویکرد نخست، متد ()UpdateOrderWithoutRetrieving موجودیت دریافت شده از کلاینت را Attach می‌کند و چک می‌کند که مقدار فیلد Status چیست. اگر مقدار این فیلد "Received" باشد، فیلد Quantity را با EntityState.Modified علامت گذاری می‌کنیم و متد ()SaveChanges را فراخوانی می‌کنیم. EF دستورات لازم برای بروز رسانی را تولید می‌کند، که فیلد quantity را مقدار دهی کرده و یک عبارت where هم دارد که فیلدهای OrderId و TimeStamp را چک می‌کند. اگر مقدار TimeStamp توسط یک دستور بروز رسانی تغییر کرده باشد، بروز رسانی جاری با خطا مواجه خواهد شد. برای مدیریت این خطا ما بدنه کد را در یک بلاک try/catch قرار می‌دهیم، و استثنای OptimisticConcurrencyException را مهار می‌کنیم. این کار باعث می‌شود اطمینان داشته باشیم که موجودیت Order دریافت شده از متد ()InsertOrder تاکنون تغییری نکرده است. دقت کنید که در مثال جاری تمام خواص موجودیت بروز رسانی می‌شوند، صرفنظر از اینکه تغییر کرده باشند یا خیر.

      رویکرد دوم نشان می‌دهد که چگونه می‌توان وضعیت همزمانی موجودیت را پیش از بروز رسانی مشخصا دریافت و بررسی کرد. در اینجا می‌توانید مقدار TimeStamp موجودیت را از دیتابیس بگیرید و آن را با مقدار موجودیت کلاینت مقایسه کنید تا وجود تغییرات مشخص شود. این رویکرد در متد ()UpdateOrderByRetrieving نمایش داده شده است. گرچه این رویکرد برای تشخیص تغییرات خواص موجودیت‌ها و یا روابط شان مفید و کارآمد است، اما بهترین روش هم نیست. مثلا ممکن است از زمانی که موجودیت را از دیتابیس دریافت می‌کنید، تا زمانی که مقدار TimeStamp آن را مقایسه می‌کنید و نهایتا متد ()SaveChanges را صدا میزنید، موجودیت شما توسط کلاینت دیگری بروز رسانی شده باشد.

      مسلما رویکرد دوم هزینه بر‌تر از رویکرد اولی است، چرا که برای مقایسه مقادیر همزمانی موجودیت ها، یکبار موجودیت را از دیتابیس دریافت می‌کنید. اما این رویکرد در مواقعی که Object graph‌‌های بزرگ یا پیچیده (complex) دارید بهتر است، چون پیش از ارسال موجودیت‌ها به context در صورت برابر نبودن مقادیر همزمانی پروسس را لغو می‌کنید.

      نظرات مطالب
      استفاده از قابلیت پارتیشن بندی در آرشیو جداول بانک‌های اطلاعاتی SQL Server
      با سلام، ابتدا از سوال آخر شروع میکنم، چنانچه تمایل دارید از امکان پارتیشنینگ در آرشیو بانک اطلاعاتی تان استفاده کنید همانگونه که در متن اشاره شده، باید جداول به شکل Aligned Index تعریف شوند.(پس نیاز به Filegroup می‌باشد)
      در مورد فشرده سازی به هیچ وجه هدف افزایش سرعت در پرس و جو‌ها نیست بلکه کاهش حجم، هدف غائی است. همینطور هدف از ایجاد ایندکس در این است که هر رکورد در مکان صحیح خود به هنگام درج قرار گیرد و بدین ترتیب چنانچه مجموعه مرتب شده باشد، مرتبه اجرائی از (O(n به 
      (O( Log n کاهش می‌یابد و ... به همین خاطر است که در سیستم‌های OLTP توصیه شده از Index هوشمندانه استفاده شود (بدلیل اینکه عملیات درج، بروزرسانی و حذف به کندی صورت نگیرد) و در سیستم‌های DSS برعکس به دلیل ماهیت غیر عملیاتی بودن آنها استفاده فراوان از Index توصیه شده است.
      بطور کلی ساختار ایندکس پایه در SQL Server عبارتند از: ایندکس‌های
      Clustered ، ایندکس‌های Nonclustered در Heap و  ایندکس‌های Nonclustered در یک ایندکس‌های Clustered شده؛ روش ذخیره فیزیکی داده بین این ایندکس‌ها متفاوت است و همچنین روشی که SQL Server برای پیمایش ایندکس در B-Tree استفاده می‌کند بسته به این سه نوع متفاوت خواهد بود.  
      مطالب
      ایجاد سرویس چندلایه‎ی WCF با Entity Framework در قالب پروژه - 5
      پس از ایجاد متدها، نوبت به تغییرات App.Config می‎رسد. هرچند خود Visual Studio برای کلاس پیش‌گزیده‌ی خود تنظیماتی را در App.Config افزوده است ولی چنان‎چه در در خاطر دارید ما آن فایل‎ها را حذف کردیم و فایل‎های جدیدی به جای آن افزودیم. از این رو مراحل زیر را انجام دهید:
      1- فایل App.Config را از Solution Explorer باز کنید.
      2- به جای عبارت MyNewsWCFLibrary.Service1 در قسمت Service Name این عبارت را بنویسید: MyNewsWCFLibrary.MyNewsService
      3- در قسمت BaseAddress عبارت Design_Time_Addresses را حذف کنید.
      4- در قسمت BaseAddress شماره پورت را به 8080 تغییر دهید.
      5- در قسمت BaseAddress به جای Service1 بنویسید: MyNewsService
      6- در قسمت endpoint به جای عبارت MyNewsWCFLibrary.IService1 بنویسید: MyNewsWCFLibrary.IMyNewsService 
      در پایان تگ Service در App.Config باید همانند کد زیر باشد:
         <services>
            <service name="MyNewsWCFLibrary.MyNewsService">
              <host>
                <baseAddresses>
                  <add baseAddress="http://localhost:8080/MyNewsWCFLibrary/MyNewsService/" />
                </baseAddresses>
              </host>
              <!-- Service Endpoints -->
              <!-- Unless fully qualified, address is relative to base address supplied above -->
              <endpoint address="" binding="basicHttpBinding" contract="MyNewsWCFLibrary.IMyNewsService">
                <!-- 
                    Upon deployment, the following identity element should be removed or replaced to reflect the 
                    identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
                    automatically.
                -->
                <identity>
                  <dns value="localhost" />
                </identity>
              </endpoint>
              <!-- Metadata Endpoints -->
              <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. -->
              <!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
              <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
            </service>
          </services>
      تغییرات را ذخیره کنید و پروژه را اجرا کنید. باید پنجره‌ای شبیه به پنجره‌ی زیر نشان داده شود:

      در صورت مشاهده پیام خطا، ویژوال استودیو را ببندید و این‌بار به صورت Run as administrator باز کنید.

      برای نمونه روی متد AddCategory کلیک کنید. در پنجره نشان داده شده همانند شکل در برابر فیلد CatName مقداری وارد کنید و روی دکمه Invoke کلیک کنید. متد مورد نظر اجرا شده و مقداری که وارد کرده ایم در پایگاه داده‌ها ذخیره می‌شود. مقداری که در قسمت پایین دیده می‌شود خروجی متد است که در اینجا شناسه رکورد درج‌شده است.

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

      افزودن ویژگی Virtual به tblNews و tblCategory در بخش دوم  خواندید؛ باعث می‌شود که Entity Framework در هنگام اجرا کلاس‌هایی با عنوان "پروکسی‌های پویا" به کلاس‌های Address و Customer بیفزاید و بنابراین قابلیت Lazy Loading برای این کلاس‌ها در زمان اجرای برنامه فراهم می‌گردد. 

      ولی با افزودن پروکسی‌های پویا به کلاس‌های ما، این کلاس‌ها قابلیت انتقال خود از طریق سرویس‌های WCF را از دست می‌دهند زیرا پروکسی‌های پویا به طور پیش‌گزیده قابلیت سریالایز و دیسریالایز شدن را ندارند!

      خوشبختانه می‌توانیم این ویژگی را در کلاس DBContext غیرفعال کنیم. برای این منظور قالب سازنده‌ی آن یا MyNewsModel.Context.tt را از Solution Explorer باز کنید و کد زیر را در آن پیدا کنید:

      <#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
      {
          public <#=code.Escape(container)#>()
              : base("name=<#=container.Name#>")
          {

      سپس در ادامه‌ی آن کدغیرفعال‌کردن پروکسی پویا را به این شکل بنویسید:

      <#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
      {
          public <#=code.Escape(container)#>()
              : base("name=<#=container.Name#>")
          {
            Configuration.ProxyCreationEnabled = false;

      اکنون اگر فایل را ذخیره کنیم سپس فایل MyNewsModel.Context.cs را از Solution Explorer باز کنید؛ خواهید دید که این خط کد در جای خود قرارگرفته است.

      بار دیگر پروژه را اجرا کنید روی متد GetAllCategory کلیک کنید. این بار اگر دکمه Invoke را بفشارید با همانند شکل زیر را خواهید دید:

      در بخش ششم پیرامون ارتباط جدول‌های tblNews و tblCategory و نمایش محتویات وابسته جدول خبر به دسته و تنظیمات آن در t4 و کلاس Service

      در بخش هفتم پیرامون میزبانی WCFLibrary در یک Web Application

      و در بخش هشتم پیرامون ایجاد یک برنامه‌ی ویندوزی جهت استفاده از سرویس‌های WCF خواهم نوشت.