پاسخ به بازخورد‌های پروژه‌ها
هدر دو تکه
در مثال تهیه‌ی کارت یک داکیومنت تعریف کرده و جدول رو بهش ادد کرده.
ولی اینجا نمیشه این کارو کرد.. خطای زمان اجرا می‌گیرم :
var table = new PdfPTable(numColumns: 3)
                {
                    WidthPercentage = 100,
                    RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                    ExtendLastRow = false,
                };
                Image i = Image.GetInstance(_imagePath);

                //logo
                table.AddCell(new PdfPCell(i)
                {
                    HorizontalAlignment = Element.ALIGN_LEFT,
                    Border = 0
                });

                //title
                table.AddCell(new PdfPCell(new Phrase("باسمه تعالی " + "\r\n" + "حوزه علمیه امیر المومنین (ع) - معاونت آموزش - کارنامه تحصیلی طلبه", font))
                {
                    RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                    HorizontalAlignment = Element.ALIGN_CENTER,
                    Border = 0,
                }
                );

                //date
                table.AddCell(new PdfPCell(new Phrase(DateTime.Now.Date.ToString(), font))
                {
                    HorizontalAlignment = 2,
                    Border = 0
                });

                int[] topTableColumnsWidth = { 5, 35, 5 };

                table.SetWidths(topTableColumnsWidth);
              
                return table.AddBorderToTable();

مطالب
استفاده از SQL Loader در Oracle
فرض کنید شما یک فایل txt دارید، که درون آن مشخصات نام و نام خانوادگی یک یا چندین میلیون نفر وجود دارد،و از شما خواسته شده، که این اطلاعات را درون جداول مربوطه، در یک دیتابیس Oracle درج نمایید. برای انجام چنین کاری می‌توانید از SQL * Loader در Oracle استفاده نمایید. که بسیار ابزار قدرتمندی میباشد.
موارد ذیل را به ترتیب انجام می‌دهیم:
1- در ابتدا یک فایل متنی به نام  LoaderTable با پسوند txt ایجاد نمایید و مشخصات زیر را درون آن کپی کنید.
1,Ahmad,Mohammadi
2,Farhad,Farahmandkhah
3,Amin,Esapor
4,Reza,shayesteh
5,Maryam,Ebrahimi
6,Farnaz,Akrami    
در اینجا، چون هدف یادگیری می‌باشد،بنابراین تعداد رکوردهای زیادی در نظر گرفته نشده است،اما شما برای تست می‌توانید،هر تعداد رکورد را درون فایل خود قرار دهید.
 2-سپس جدولی با عنوان Testloader ایجاد می‌کنیم،که شامل سه فیلد میباشد1- شناسه 2-نام 3- نام خانوادگی، همانند Script زیر:
Create Table Testloader
(ID int,
FirstName varchar(255),
LastName Varchar(255))
3-در این مرحله فایلی به نام loader با پسوند ctl ایجاد می‌کنیم.درون فایل فوق،اطلاعات زیر را کپی و فایل خود را ذخیره نمایید:
LOAD DATA
INFILE 'c:\LoaderTable.txt'
Insert INTO TABLE Testloader
FIELDS TERMINATED BY ','
optionally enclosed by '"'
TRAILING NULLCOLS
(
ID,
FirstName,
LastName
)
خط  'INFILE 'c:\LoaderTable.txt بیانگر مسیر فایلی میباشد،که می‌خواهیم درون جدول درج نماییم.
خط ',' FIELDS TERMINATED BY بیانگر این مطلب می‌باشد که، بین مقادیر ستونها با کاما (,) جدا شده است. به عبارت دیگر، انتهای مقدار هر ستون به کاما ختم شده است.
خط '"' optionally enclosed by ، این دستور در این مثال کاربردی ندارد،اما مفهومش این است که، محتویاتی را که بین  یک کوتیشن محصور شده اند، به عنوان یک مقدار در نظر بگیرد.
برای درک دستورTRAILING NULLCOLS، مثالی می‌زنم، در جدول فرضی سه ستون داریم، که شامل شناسه،نام و نام خانوادگی است، حال فرض کنید، چنانچه مقادیر هریک از ستونها در فایل تهی یا خالی باشد، Oracle در زمان درج در جدول، آن رکورد را به عنوان Bad Data در فایلی به نام Bad فایل قرار می‌دهد و درون جدول درج نمی‌نماید، برای آنکه چنین مشکلی پیش نیاید، و در صورتی که، خالی بودن مقدار هریک از فیلد‌ها برای شما اهمیتی ندارد، با قرار دادن TRAILING NULLCOLS ، به Oracle می‌فهمانید، چنانچه رکوردی در فایل وجود داشته باشد، و یکی از مقادیر ستونهایش Null  یا خالی باشد، Oracle عملیات درج آن رکورد را ،در جدول انجام دهد.
4- در این مرحله برای درج محتویات، فایل LoaderTable به جدول Testloader خط فرمان زیر را دریک CMD ویندوز کپی نمایید و آن را اجرا کنید.
D:\>sqlldr userid=Username/Password@Servicename data='c:\LoaderTable.txt' control='c:\loader.ctl' log='c:\log.txt' bad='c:\logbad.bad'
به جای Username ، یوزر دیتابیس خود را درج نمایید، به جای Password ، کلمه عبور و به جای ServiceName، نام Servicename ارتباطی با دیتابیس Oracle را درج نمایید.
در پایان باید بگویم، SQL * Loader یک ابزار بسیار قدرتمند در Oracle محسوب می‌شود، و حالتهای بسیار پیشرفته ای در آن وجود دارد، قصد من در این مقاله فقط ،آشنایی و نحوه استفاده از چنین ابزاری بوده است، برای مطالعه بیشتر می‌توانید به دو سایت زیر مراجعه نمایید:
موفق باشید.
 
مطالب
دریافت اطلاعات بیشتر از Social Provider ها در VS 2013
هنگامی که یک پروژه جدید ASP.NET را در VS 2013 می‌سازید و متد احراز هویت آن را Individual User Accounts انتخاب می‌کنید، قالب پروژه، امکانات لازم را برای استفاده از تامین کنندگان ثالث، فراهم می‌کند، مثلا مایکروسافت، گوگل، توییتر و فیسبوک. هنگامی که توسط یکی از این تامین کننده‌ها کاربری را احراز هویت کردید، می‌توانید اطلاعات بیشتری درخواست کنید. مثلا عکس پروفایل کاربر یا لیست دوستان او. سپس اگر کاربر به اپلیکیشن شما سطح دسترسی کافی داده باشد می‌توانید این اطلاعات را دریافت کنید و تجربه کاربری قوی‌تر و بهتری ارائه کنید.

در این پست خواهید دید که چطور می‌شود از تامین کننده Facebook اطلاعات بیشتری درخواست کرد. پیش فرض این پست بر این است که شما با احراز هویت فیسبوک و سیستم کلی تامین کننده‌ها آشنایی دارید. برای اطلاعات بیشتر درباره راه اندازی احراز هویت فیسبوک به  این لینک  مراجعه کنید.

برای دریافت اطلاعات بیشتر از فیسبوک مراحل زیر را دنبال کنید.

  • یک اپلیکیشن جدید ASP.NET MVC با تنظیمات Individual User Accounts بسازید.
  • احراز هویت فیسبوک را توسط کلید هایی که از Facebook دریافت کرده اید فعال کنید. برای اطلاعات بیشتر در این باره می‌توانید به این لینک مراجعه کنید.
  • برای درخواست اطلاعات بیشتر از فیسبوک، فایل Startup.Auth.cs را مطابق لیست زیر ویرایش کنید.
 List<string> scope = newList<string>() { "email", "user_about_me", "user_hometown", "friends_about_me", "friends_photos" };
 var x = newFacebookAuthenticationOptions();
 x.Scope.Add("email");
 x.Scope.Add("friends_about_me");
 x.Scope.Add("friends_photos");
 x.AppId = "636919159681109";
 x.AppSecret = "f3c16511fe95e854cf5885c10f83f26f";
 x.Provider = newFacebookAuthenticationProvider()
{
    OnAuthenticated = async context =>
    {
         //Get the access token from FB and store it in the database and
        //use FacebookC# SDK to get more information about the user
        context.Identity.AddClaim(
        new System.Security.Claims.Claim("FacebookAccessToken",
                                             context.AccessToken));
    }
};
 x.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
 app.UseFacebookAuthentication(x);

در خط 1 مشخص می‌کنیم که چه scope هایی از داده را می‌خواهیم درخواست کنیم.
از خط 10 تا 17 رویداد OnAuthenticated را مدیریت می‌کنیم که از طرف Facebook OWIN authentication اجرا می‌شود. این متد هر بار که کاربری با فیسبوک خودش را احراز هویت می‌کند فراخوانی می‌شود. پس از آنکه کاربر احراز هویت شد و به اپلیکیشن سطح دسترسی لازم را اعطا کرد، تمام داده‌ها در FacebookContext ذخیره می‌شوند. 
خط 14 شناسه FacebookAccessToken را ذخیره می‌کند. ما این آبجکت را از فیسبوک دریافت کرده و از آن برای دریافت لیست دوستان کاربر استفاده می‌کنیم.
نکته: در این مثال تمام داده‌ها بصورت Claims ذخیره می‌شوند، اما اگر بخواهید می‌توانید از ASP.NET Identity برای ذخیره آنها در دیتابیس استفاده کنید.
در قدم بعدی لیست دوستان کاربر را از فیسبوک درخواست می‌کنیم. ابتدا فایل Views/Shared/_LoginPartial.cshtml را باز کنید و لینک زیر را به آن بیافزایید.
 <li>
      @Html.ActionLink("FacebookInfo", "FacebookInfo","Account")
</li>

 هنگامی که کاربری وارد سایت می‌شود و این لینک را کلیک می‌کند، ما لیست دوستان او را از فیسبوک درخواست می‌کنیم و بهمراه عکس‌های پروفایل شان آنها را لیست می‌کنیم.
تمام Claim‌ها را از UserIdentity بگیرید و آنها را در دیتابیس ذخیره کنید. در این قطعه کد ما تمام Claim هایی که توسط OWIN دریافت کرده ایم را می‌خوانیم، و شناسه FacebookAccessToken را در دیتابیس عضویت ASP.NET Identity ذخیره می‌کنیم.
//
        // GET: /Account/LinkLoginCallback
        publicasyncTask<ActionResult> LinkLoginCallback()
        {
            var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
            if (loginInfo == null)
            {
                return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
            }
            var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
            if (result.Succeeded)
            {
                var currentUser = await UserManager.FindByIdAsync(User.Identity.GetUserId());
                //Add the Facebook Claim
                await StoreFacebookAuthToken(currentUser);
                return RedirectToAction("Manage");
            }
            return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
        }
خط 14-15 شناسه FacebookAccessToken را در دیتابیس ذخیره می‌کند.
StoreFacebookAuthToken تمام اختیارات (claim)‌های کاربر را از UserIdentity می‌گیرد و Access Token را در قالب یک User Claim در دیتابیس ذخیره می‌کند. اکشن LinkLoginCallback هنگامی فراخوانی می‌شود که کاربر وارد سایت شده و یک تامین کننده دیگر را می‌خواهد تنظیم کند.
اکشن ExternalLoginConfirmation هنگام اولین ورود شما توسط تامین کنندگان اجتماعی مانند فیسبوک فراخوانی می‌شود.
در خط 26 پس از آنکه کاربر ایجاد شد ما یک FacebookAccessToken را بعنوان یک Claim برای کاربر ذخیره می‌کنیم.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
        {
            if (User.Identity.IsAuthenticated)
            {
                return RedirectToAction("Manage");
            }
 
            if (ModelState.IsValid)
            {
                // Get the information about the user from the external login provider
                var info = await AuthenticationManager.GetExternalLoginInfoAsync();
                if (info == null)
                {
                    return View("ExternalLoginFailure");
                }
                var user = newApplicationUser() { UserName = model.Email };
                var result = await UserManager.CreateAsync(user);
                if (result.Succeeded)
                {
                    result = await UserManager.AddLoginAsync(user.Id, info.Login);
                    if (result.Succeeded)
                    {
                        await StoreFacebookAuthToken(user);
                        await SignInAsync(user, isPersistent: false);
                        return RedirectToLocal(returnUrl);
                    }
                }
                AddErrors(result);
            }
 
            ViewBag.ReturnUrl = returnUrl;
            return View(model);
        }


اکشن ExternalLoginCallback هنگامی فراخوانی می‌شود که شما برای اولین بار یک کاربر را به یک تامین کننده اجتماعی اختصاص می‌دهید. در خط 17 شناسه دسترسی فیسبوک را بصورت یک claim برای کاربر ذخیره می‌کنیم.
//
        // GET: /Account/ExternalLoginCallback
        [AllowAnonymous]
        publicasyncTask<ActionResult> ExternalLoginCallback(string returnUrl)
        {
            var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
            if (loginInfo == null)
            {
                return RedirectToAction("Login");
            }
 
            // Sign in the user with this external login provider if the user already has a login
            var user = await UserManager.FindAsync(loginInfo.Login);
            if (user != null)
            {
                //Save the FacebookToken in the database if not already there
                await StoreFacebookAuthToken(user);
                await SignInAsync(user, isPersistent: false);
                return RedirectToLocal(returnUrl);
            }
            else
            {
                // If the user does not have an account, then prompt the user to create an account
                ViewBag.ReturnUrl = returnUrl;
                ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
                return View("ExternalLoginConfirmation", newExternalLoginConfirmationViewModel { Email = loginInfo.Email });
            }
        }
در آخر شناسه FacebookAccessToken را در دیتابیس ASP.NET Identity ذخیره کنید.
privateasyncTask StoreFacebookAuthToken(ApplicationUser user)
        {
            var claimsIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
            if (claimsIdentity != null)
            {
                // Retrieve the existing claims for the user and add the FacebookAccessTokenClaim
                var currentClaims = await UserManager.GetClaimsAsync(user.Id);
                var facebookAccessToken = claimsIdentity.FindAll("FacebookAccessToken").First();
                if (currentClaims.Count() <=0 )
                {
                    await UserManager.AddClaimAsync(user.Id, facebookAccessToken);
                }

پکیج Facebook C#SDK را نصب کنید. http://nuget.org/packages/Facebook
فایل AccountViewModel.cs را باز کنید و کد زیر را اضافه کنید.
    public class FacebookViewModel
     {
         [Required]
         [Display(Name = "Friend's name")]
         public string Name { get; set; }
  
        public string ImageURL { get; set; }
    }

کد زیر را به کنترلر Account اضافه کنید تا عکس‌های دوستان تان را دریافت کنید.
//GET: Account/FacebookInfo
[Authorize]
publicasyncTask<ActionResult> FacebookInfo()
{
    var claimsforUser = await UserManager.GetClaimsAsync(User.Identity.GetUserId());
    var access_token = claimsforUser.FirstOrDefault(x => x.Type == "FacebookAccessToken").Value;
    var fb = newFacebookClient(access_token);
    dynamic myInfo = fb.Get("/me/friends");
    var friendsList = newList<FacebookViewModel>();
    foreach (dynamic friend in myInfo.data)
    {
        friendsList.Add(newFacebookViewModel()
           {
               Name = friend.name,
               ImageURL = @"https://graph.facebook.com/" + friend.id + "/picture?type=large"
           });
    }
 
    return View(friendsList);
}

در پوشه Views/Account یک نمای جدید با نام FacebookInfo.cshtml بسازید و کد Markup آن را مطابق لیست زیر تغییر دهید.
@model IList<WebApplication96.Models.FacebookViewModel>
 @if (Model.Count > 0)
 {
     <h3>List of friends</h3>
     <div class="row">
             @foreach (var friend in Model)
             {
               <div class="col-md-3">
                <a href="#" class="thumbnail">
                  <img src=@friend.ImageURL alt=@friend.Name />
                 </a>
               </div>
              }
     </div>
 }
در این مرحله، شما می‌توانید لیست دوستان خود را بهمراه عکس‌های پروفایل شان دریافت کنید.
پروژه را اجرا کنید و توسط Facebook وارد سایت شوید. باید به سایت فیسبوک هدایت شوید تا احراز هویت کنید و دسترسی لازم را به اپلیکیشن اعطا کنید. پس از آن مجددا به سایت خودتان باید هدایت شوید.
حال هنگامی که روی لینک FacebookInfo کلیک می‌کنید باید صفحه ای مشابه تصویر زیر ببینید.

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

اشتراک‌ها
مستند سازی Database

dbdescis a powerful tool to help you document your databases. It can produce detailed documents describing your databases

Currently dbdesc supports the following databases:
SQL Server 2000, 2005, 2008, 2008 R2, 2012, 2014
Microsoft Desktop Engine 2000 (MSDE) and SQL Server Express editions
MySQL 5.0
Oracle 9 and above
Microsoft Access 97 and above
Firebird 

با استفاده از کوئری زیر نیز می‌توانید نام و Description ستون‌ها و دیتاتایپ و ... آنها را بدست آورید.


 SELECT p.name
 ,p.value
 ,t.name AS TableName
 ,c.name AS ColumnName
 ,c.is_nullable
 ,c.max_length
 ,TYPE_NAME(c.system_type_id)
FROM sys.tables t
JOIN sys.columns c
ON t.object_id = c.object_id
LEFT JOIN sys.extended_properties p
ON p.major_id = t.object_id
AND p.minor_id = c.column_id
ORDER BY t.name DESC,c.name DESC

جهت بررسی بیشتر و آشنایی با Extended Properties in SQL Server به لینک Towards the Self-Documenting SQL Server Database  مراجعه کنید.

مستند سازی Database
مطالب
یافتن لیست اسمبلی‌های ارجاعی

اگر برنامه‌ی شما برای مثال با SMO مربوط به اس کیوال سرور 2008 کامپایل شود، روی سروری با SQL Server 2005 کار نخواهد کرد و پیغام می‌دهد که نگارش 10 اسمبلی Microsoft.SqlServer.Management.Sdk.Sfc یافت نشد.
یک راه حل آن، نصب Microsoft SQL Server 2008 Management Objects بر روی سرور است، یا راه حل دوم، پیدا کردن اسمبلی‌هایی که برنامه به آن‌ها ارجاع دارد و کپی کردن آن‌ها کنار فایل اجرایی برنامه در سرور. (درست کردن یک برنامه پرتابل دات نتی، یا نسبتا پرتابل!)
برای این منظور کلاس زیر تهیه شده است که مسیر فایل اجرایی یا dll یک پروژه را دریافت کرده و لیست تمام ارجاعات به آن‌را به صورت بازگشتی پیدا می‌کند. (البته در قسمت یافتن مسیر اسمبلی‌ها، اسمبلی‌های سیستمی که با خود دات نت فریم ورک نصب می‌شوند، حذف شده است)

using System.Collections.Generic;
using System.Reflection;
using System.IO;

namespace App
{
class CFindRef
{
#region Fields (2)

/// <summary>
/// لیستی جهت نگهداری نام اسمبلی‌ها
/// </summary>
private readonly List<string> _assemblies = new List<string>();
/// <summary>
/// لیستی جهت نگهداری مسیر اسمبلی‌ها
/// </summary>
private readonly List<string> _filePath = new List<string>();

#endregion Fields

#region Constructors (1)

/// <summary>
/// سازنده کلاس
/// </summary>
/// <param name="fileName">مسیر اولیه اسمبلی مورد نظر</param>
public CFindRef(string fileName)
{
ListReferences(fileName);
}

#endregion Constructors

#region Properties (2)

/// <summary>
/// لیست مسیر اسمبلی‌هایی که به آن‌ها ارجاعی وجود دارد منهای موارد سیستمی
/// </summary>
public List<string> ReferencedFiles
{
get
{
_filePath.Sort();
return _filePath;
}
}

/// <summary>
/// لیست کامل اسمبلی‌هایی که اسمبلی ما به آن‌ها وابسته است
/// </summary>
public List<string> ReferencedNames
{
get
{
_assemblies.Sort();
return _assemblies;
}
}

#endregion Properties

#region Methods (1)

// Private Methods (1)

/// <summary>
/// متدی بازگشتی جهت یافتن لیست ارجاعات
/// </summary>
/// <param name="path">مسیر یا نام اسمبلی</param>
private void ListReferences(string path)
{
//آیا تکراری است؟
if (_assemblies.Contains(path))
return;

Assembly asm;
// آیا فایل است یا نام کامل اسمبلی
if (File.Exists(path))
{
// load the assembly from a path
asm = Assembly.LoadFrom(path);
}
else
{
// سعی در بارگذاری اسمبلی
try
{
asm = Assembly.Load(path);
}
catch
{
asm = null; //جای بهبود دارد
}
}

if (asm == null) return;

// افزودن به لیست نام‌ها
_assemblies.Add(path);
string asmLocation = asm.Location;
//حذف موارد سیستمی از لیست مسیر فایل‌ها
if (asmLocation != null && !asmLocation.Contains("\\System.")
&& !asmLocation.Contains("\\mscorlib"))
_filePath.Add(asmLocation);

// پیدا کردن ارجاع‌ها
AssemblyName[] imports = asm.GetReferencedAssemblies();
// iterate
foreach (AssemblyName asmName in imports)
{
// فراخوانی بازگشتی جهت یافتن تمامی ارجاعات
ListReferences(asmName.FullName);
}
}

#endregion Methods
}
}

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

CFindRef cfr = new CFindRef(@"C:\App\test.exe");
foreach (var asmRef in cfr.ReferencedFiles)
{
textBox1.Text += asmRef + Environment.NewLine;
//Application.DoEvents();
}
برای نمونه، برنامه‌ای که از SMO‌ مربوط به اس کیوال سرور 2008 استفاده می‌کند، این لیست ارجاعات را دارد:

C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.ConnectionInfo\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.ConnectionInfo.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.Dmf\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.Dmf.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.Management.Sdk.Sfc\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.Management.Sdk.Sfc.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.ServiceBrokerEnum\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.ServiceBrokerEnum.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.Smo\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.Smo.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.SqlClrProvider\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.SqlClrProvider.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.SqlEnum\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.SqlEnum.dll
با کپی کردن همین فایل‌ها کنار فایل اجرایی برنامه‌ای که قرار است روی سروری با SQL Server 2005 کار کند، مشکل برطرف می‌شود و برنامه بدون مشکل کار خواهد کرد.

برنامه آماده هم در این زمینه موجود است، برای مثال CheckAsm
مشاهده سایت

مطالب
آموزش سیلورلایت 4 - قسمت‌های 6 تا 10

فصل عنوان فایل مرتبط
6 استفاده از کنترل‌های Silverlight +
7 آشنایی با سیستم Navigation در Silverlight +
8 آشنایی با سیستم Binding در Silverlight +
9 معرفی الگوی M-V-VM +
10 معرفی مثالی مقدماتی از پیاده سازی الگوی M-V-VM در Silverlight +
دریافت تمام قسمت‌ها از GitHub  
نظرات مطالب
Blazor 5x - قسمت 33 - احراز هویت و اعتبارسنجی کاربران Blazor WASM - بخش 3- بهبود تجربه‌ی کاربری عدم دسترسی‌ها
یا اگر نمونه‌ی AuthorizationHandler سفارشی آن‌را نیاز داشتید، به صورت زیر است:
- ابتدا یک IAuthorizationRequirement و AuthorizationHandler سفارشی را ایجاد می‌کنیم که در هندلر آن دسترسی کاملی به اطلاعات کاربر وارد شده‌ی به سیستم وجود دارد:
public class UserCanSeeProjectRequirement : IAuthorizationRequirement
{
    public UserCanSeeProjectRequirement() { }

}


public class UserCanSeeProjectHandler : AuthorizationHandler<UserCanSeeProjectRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, 
                                                   UserCanSeeProjectRequirement requirement)
    {
        //claim-based validation
        if (context.User.HasClaim("permission.cansee", "CanSee"))
                context.Succeed(requirement);

        //role-based validation
        if (context.User.IsInRole("admin") || context.User.IsInRole("user"))
                context.Succeed(requirement);

        return Task.CompletedTask;
    }
}
سپس نیاز است این اطلاعات را به برنامه‌ی کلاینت معرفی کرد:
namespace BlazorWasm.Client
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            // ...

            services.AddScoped<IAuthorizationHandler, UserCanSeeProjectHandler>();
            services.AddAuthorizationCore(options => {
                options.AddPolicy("UserCanSeeProjectPolicy", policy => policy.Requirements.Add(new UserCanSeeProjectRequirement()));
            });

            // ...
        }
    }
}
و در نهایت می‌توان Policy جدید فوق را که با نام UserCanSeeProjectPolicy معرفی شده، یا به صورت زیر در ابتدای یک صفحه:
@attribute [Authorize(Policy = "UserCanSeeProjectPolicy")]
و یا در قسمتی از آن صفحه استفاده کرد:
<AuthorizeView Policy="UserCanSeeProjectPolicy">
    <NotAuthorized>
       <h2 class="mt-5">You are not authorized to view this page</h2>
    </NotAuthorized>
    <Authorized>
      <div class="container my-profile">
        --- Place here all the content you want your user to view ----
      </div>
    </Authorized>
</AuthorizeView>
مطالب
ارسال فایل در ASP.NET MVC و اعتبار سنجی سمت کاربر
پیشنیازها:
- نحوه ارسال فایل به سرور توسط ASP.NET MVC
- نحوه اعتبار سنجی سمت سرور ارسال فایل‌ها

در ASP.NET MVC برای آپلود فایل‌ها عموما عنوان می‌شود که از تگ input به نحو زیر استفاده شود:
<input type="file" name="file" />
مشکلی که با این روش وجود دارد، عدم فعال شدن اعتبار سنجی سمت کاربر در حد مثلا «لطفا یک فایل را انتخاب کنید» است. برای فعال سازی آن می‌توان از همان روش unobtrusive معرفی شده در ASP.NET MVC 3 به نحو زیر به صورت دستی استفاده کرد:
<input type="file" data-val="true" data-val-required="لطفا فایلی را انتخاب کنید" name="file" />
@Html.ValidationMessage("file")
در اینجا ویژگی‌های data-val و data-val-required به صورت خودکار اعتبار سنجی سمت کاربر را فعال خواهند کرد (شبیه به required field validator عمل می‌کنند).

از این نکته خصوصا در طراحی html helperهای سفارشی نیز می‌توان استفاده کرد.
برای مثال فرض کنید مشغول طراحی یک کنترل captcha هستید. قسمتی از آن مربوط به دریافت ورودی از کاربر است. به علاوه، پیغام خطایی هم که باید نمایش داده شود نیز باید توسط کاربر قابلیت سفارشی شدن را داشته باشد (و نمی‌توان آن‌را توسط یک attribute به سادگی به یک مدل خاص انتساب داد).
در این حالت تنظیم data-val و data-val-required به کمک anonymously typed objects پارامتر htmlAttributes میسر نیست (چون بین حروف آن dash وجود دارد) و باید از overload و نوع dictionary دار آن به نحو زیر استفاده کرد:

htmlHelper.TextBox("CaptchaInputText", string.Empty,
                          new Dictionary<string, object>
                          {
                            { "data-val", "true"},
                            { "data-val-required", validationErrorMessage},
                          })

مطالب
C# 12.0 - Primary Constructors
قابلیتی تحت عنوان Primary Constructors به C# 12 اضافه شده‌است که ... البته جدید نیست! این قابلیت از زمان C# 9، با ارائه‌ی رکوردها، به زبان #C اضافه شد و در طی چند نگارش بعدی، توسعه و تکامل یافت (برای مثال اضافه شدن records for structs به C# 10) تا در C# 12، به کلاس‌های معمولی نیز تعمیم پیدا کرد. این ویژگی را در ادامه با جزئیات بیشتری بررسی می‌کنیم.


Primary Constructors چیست؟

Primary Constructors، قابلیتی است که به C# 12 اضافه شده‌است تا توسط آن بتوان خواص را مستقیما توسط پارامترهای سازنده‌ی یک کلاس تعریف و همچنین مقدار دهی کرد. هدف از آن، کاهش قابل ملاحظه‌ی یکسری کدهای تکراری و مشخص است تا به کلاس‌هایی زیباتر، کم‌حجم‌تر و خواناتر برسیم. برای مثال کلاس متداول زیر را درنظر بگیرید:
public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime HireDate { get; set; }
    public decimal Salary { get; set; }

    public Employee(string firstName, string lastName, DateTime hireDate, decimal salary)
    {
        FirstName = firstName;
        LastName = lastName;
        HireDate = hireDate;
        Salary = salary;
    }
}
در زبان ‍#C، سازنده، متد ویژه‌ای است که در حین ساخت نمونه‌ای از یک کلاس، فراخوانی می‌شود. هدف از آن‌، آغاز و مقدار دهی حالت شیء ایجاد شده‌است که عموما با مقدار دهی خواص آن شیء، انجام می‌شود.
اکنون اگر بخواهیم همین کلاس را با استفاده از ویژگی Primary Constructor اضافه شده به C# 12.0 بازنویسی کنیم، به قطعه کد زیر می‌رسیم:
public class Employee(string firstName, string lastName, DateTime hireDate, decimal salary)
{
    public string FirstName { get; set; } = firstName;
    public string LastName { get; set; } = lastName;
    public DateTime HireDate { get; set; } = hireDate;
    public decimal Salary { get; set; } = salary;
}
و نحوه‌ی نمونه سازی از آن به صورت زیر است:
var employee = new Employee("John", "Doe", new DateTime(2020, 1, 1), 50000);

یک نکته: اگر از Rider و یا ReSharper استفاده می‌کنید، یک چنین Refactoring توکاری جهت سهولت کار، به آن‌ها اضافه شده‌است و به سرعت می‌توان این تبدیلات را توسط آن‌ها انجام داد.




توضیحات:
- متد سازنده در این حالت، به ظاهر حذف شده و به قسمت تعریف کلاس منتقل شده‌است.
- تمام مقدار دهی‌های آغازین موجود در متد سازنده‌ی پیشین نیز حذف شده‌اند و مستقیما به قسمت تعریف خواص، منتقل شده‌اند.
در نتیجه از یک کلاس 15 سطری، به کلاسی 7 سطری رسیده‌ایم که کاهش حجم قابل ملاحظه‌ای را پیدا کرده‌است.

نکته 1: هیچ ضرورتی وجود ندارد که به همراه یک primary constructor، خواصی هم مانند مثال فوق ارائه شوند؛ چون پارامترهای آن در تمام اعضای این کلاس، به همین شکل، قابل دسترسی هستند. در این مثال صرفا جهت بازسازی کد قبلی، این خواص اضافی را مشاهده می‌کنید. یعنی اگر تنها قرار بود، کار تزریق وابستگی‌ها صورت گیرد که عموما به همراه تعریف فیلدهایی جهت انتساب پارامترهای متد سازنده به آن‌ها است، استفاده از یک primary constructor، کدهای فوق را بیش از این هم فشرده‌تر می‌کرد و ... یک سطری می‌شد.

نکته 2: استفاده از پارامترهای سازنده‌ی اولیه، صرفا جهت مقدار دهی خواص عمومی یک کلاس، یک code smell هستند! چون می‌توان یک چنین کارهایی را به نحو شکیل‌تری توسط required properties معرفی شده‌ی در C# 11، پیاده سازی کرد.


بررسی تاریخچه‌ی primary constructors

همانطور که در مقدمه‌ی بحث نیز عنوان شد، primary constructors قابلیت جدیدی نیست و برای نمونه به همراه C# 9 و مفهوم جدید رکوردهای آن، ارائه شد:
public record class Book(string Title, string Publisher);
مثال فوق که به positional syntax هم معروف است، به همراه بکارگیری primary constructors است. در اینجا کامپایلر به صورت خودکار، کار تولید کدهای خواص متناظر را که از نوع get و init دار هستند، انجام می‌دهد. در این حالت به علت استفاده از init accessors، پس از نمونه سازی شیءای از آن، دیگر نمی‌توان مقدار خواص متناظر را تغییر داد.
پس از آن در C# 10، این توسعه ادامه یافت و به امکان تعریف record structها، بسط یافت که در اینجا هم قابلیت تعریف primary constructors وجود دارد:
public record struct Color(int R, int G, int B);
که البته در این حالت برخلاف record classها، کامپایلر، کدی را که برای خواص تولید می‌کند، get و set دار است. در اینجا اگر نیاز است به همان حالت خواص get و init دار رسید، می‌توان یک readonly record struct را تعریف کرد.

پس از این مقدمات، اکنون در C# 12 نیز می‌توان primary constructors را به تمام کلاس‌ها و structهای معمولی هم اعمال کرد؛ با این تفاوت که در اینجا برخلاف رکوردها، کدهای خواص‌های متناظر، به صورت خودکار تولید نمی‌شوند و اگر به آن‌ها نیاز دارید، باید آن‌ها را همانند مثال ابتدای بحث، خودتان به صورت دستی تعریف کنید.


primary constructors کلاس‌ها و structهای معمولی، با primary constructors رکوردها یکی نیست

در C# 12 و به همراه معرفی primary constructors مخصوص کلاس‌ها و structهای معمولی آن، از روش متفاوتی برای دسترسی به پارامترهای تعریف شده، استفاده می‌کند که به آن capturing semantics هم می‌گویند. در این حالت پارامترهای تعریف شده‌ی در یک primary constructor، توسط هر عضوی از آن کلاس قابل استفاده‌است که یکی از کاربردهای آن، ساده کردن تعاریف تزریق وابستگی‌ها است. در این حالت دیگر نیازی نیست تا ابتدا یک فیلد را برای انتساب به پارامتر تزریق شده تعریف کرد و سپس از آن فیلد، استفاده نمود؛ مستقیما می‌توان با همان پارامتر تعریف شده، در متدها و اعضای کلاس، کار کرد.
برای مثال سرویس زیر را که از تزریق وابستگی‌ها، در سازنده‌ی خود استفاده می‌کند، درنظر بگیرید:
public class MyService
{
    private readonly IDepedent _dependent;
  
    public MyService(IDependent dependent)
    {
        _dependent = dependent;
    }
  
    public void Do() 
    {
        _dependent.DoWork();
    }
}
این کلاس در C# 12 به صورت زیر خلاصه شده و پارامتر dependent تعریف شده‌ی در سازنده‌ی اولیه‌ی آن، به همان شکل و بدون نیاز به کد اضافی، در سایر متدهای این کلاس قابل استفاده‌است:
public class MyService(IDependent dependent)
{
    public void Do() 
    {
        dependent.DoWork();
    }
}

البته مفهوم Captures هم در زبان #C جدید نیست و در ابتدا به همراه anonymous methods و بعدها به همراه lambda expressions، معرفی و بکار گرفته شد. برای مثال درون یک lambda expression، اگر از متغیری خارج از آن lambda expressions استفاده شود، کامپایلر یک capture از آن متغیر را تهیه کرده و استفاده می‌کند.

بنابراین به صورت خلاصه primary constructors در رکوردها، با هدف تعریف خواص عمومی فقط خواندنی، ارائه شدند؛ اما primary constructors ارائه شده‌ی در C# 12 که اینبار قابل اعمال به کلاس‌ها و structs معمولی است، بیشتر هدف ساده سازی تعریف کدهای تکراری private fields را دنبال می‌کند. برای نمونه این کدی است که کامپایلر برای primary constructor مثال ابتدای بحث تولید می‌کند و در اینجا نحوه‌ی تولید خودکار این فیلدهای خصوصی را مشاهده می‌کنید:
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace CS8Tests
{
  [NullableContext(1)]
  [Nullable(0)]
  public class Employee
  {
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string <FirstName>k__BackingField;
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string <LastName>k__BackingField;
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private DateTime <HireDate>k__BackingField;
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private Decimal <Salary>k__BackingField;

    public Employee(string firstName, string lastName, DateTime hireDate, Decimal salary)
    {
      this.<FirstName>k__BackingField = firstName;
      this.<LastName>k__BackingField = lastName;
      this.<HireDate>k__BackingField = hireDate;
      this.<Salary>k__BackingField = salary;
      base..ctor();
    }

    public string FirstName
    {
      [CompilerGenerated] get
      {
        return this.<FirstName>k__BackingField;
      }
      [CompilerGenerated] set
      {
        this.<FirstName>k__BackingField = value;
      }
    }

    public string LastName
    {
      [CompilerGenerated] get
      {
        return this.<LastName>k__BackingField;
      }
      [CompilerGenerated] set
      {
        this.<LastName>k__BackingField = value;
      }
    }

    public DateTime HireDate
    {
      [CompilerGenerated] get
      {
        return this.<HireDate>k__BackingField;
      }
      [CompilerGenerated] set
      {
        this.<HireDate>k__BackingField = value;
      }
    }

    public Decimal Salary
    {
      [CompilerGenerated] get
      {
        return this.<Salary>k__BackingField;
      }
      [CompilerGenerated] set
      {
        this.<Salary>k__BackingField = value;
      }
    }
  }
}
بنابراین آیا پارامترهای سازنده‌ی اولیه، به صورت خواص تعریف می‌شوند و قابلیت تغییر میدان دید آن‌ها میسر است؟ پاسخ: خیر. این پارامترها توسط کامپایلر، به صورت فیلدهای خصوصی در سطح کلاس، تعریف و استفاده می‌شوند. یعنی تمام اعضای کلاس، البته منهای سازنده‌های ثانویه، به این پارامترها دسترسی دارند. همچنین، این تولید کد هم بهینه‌است و صرفا برای پارامترهایی انجام می‌شود که واقعا در کلاس استفاده شده باشند؛ درغیر اینصورت، فیلد خصوصی متناظری برای آن‌ها تولید نخواهد شد.

یک نکته: برای مشاهده‌ی یک چنین کدهایی می‌توانید از منوی Tools->IL Viewer برنامه‌ی Rider استفاده کرده و در برگه‌ی ظاهر شده، گزینه‌ی #Low-Level C آن‌را انتخاب نمائید.


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

اگر به کدهای #Low-Level C تولیدی فوق دقت کنید، این کلاس، به همراه یک سازنده‌ی خالی بدون پارامتر (parameter less constructor) نیست و سازنده‌ی پیش‌فرضی (default constructor) برای آن درنظر گرفته نشده‌است ... اما اگر کلاسی به همراه یک primary constructor تعریف شد، می‌توان با استفاده از واژه‌ی کلیدی this، سازنده‌ی ثانویه‌ای را هم برای آن تعریف کرد:
public class Person(string firstName, string lastName) 
{
    public Person() : this("John", "Smith") { }
    public Person(string firstName) : this(firstName, "Smith") { }
    public string FullName => $"{firstName} {lastName}";
}
در اینجا نحوه‌ی تعریف یک Default constructor بدون پارامتر را هم ملاحظه می‌کنید.


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

مثال زیر را درنظر بگیرید که در آن کلاس مشتق شده‌ی از کلاس User، یک سازنده‌ی اولیه را تعریف کرده:
public class User
{
    public User(string firstName, string lastName) { }
}

public class Editor(string firstName, string lastName) : User
{
}
در این حالت برنامه با خطای «Base class 'CS8Tests.User' does not contain parameterless constructor» کامپایل نمی‌شود. عنوان می‌کند که اگر کلاس مشتق شده می‌خواهد سازنده‌ی اولیه‌ای داشته باشد، باید کلاس پایه را به همراه یک سازنده‌ی پیش‌فرض بدون پارامتر تعریف کنید.
البته این محدودیت با structها وجود ندارد؛ چون structها، value type هستند و همواره به صورت پیش‌فرض، به همراه یک سازنده‌ی پیش فرض بدون پارامتر، تولید می‌شوند.
یک مثال: قطعه کد متداول ارث‌بری زیر را درنظر بگیرید که در آن، کلاس مشتق شده به کمک واژه‌ی کلید base، امکان تعریف سازنده‌ی جدیدی را یافته و یکی از پارامترهای سازنده‌ی کلاس پایه را مقدار دهی می‌کند:
public class Automobile
{
    public Automobile(int wheels, int seats)
    {
        Wheels = wheels;
        Seats = seats;
    }

    public int Wheels { get; }
    public int Seats { get; }
}

public class Car : Automobile
{
    public Car(int seats) : base(4, seats)
    {
    }
}
این تعاریف در C# 12 به صورت زیر خلاصه می‌شوند:
public class Automobile(int wheels, int seats)
{
    public int Wheels { get; } = wheels;
    public int Seats { get; } = seats;
}

public class Car(int seats) : Automobile(4, seats);

و یا یک نمونه مثال دیگر آن به صورت زیر است که در آن، ذکر بدنه‌ی کلاس در C# 12، الزامی ندارد:
public class MyBaseClass(string s); // no body required

public class Derived(int i, string s, bool b) : MyBaseClass(s)
{
    public int I { get; set; } = i;
    public string B => b.ToString();
}


توصیه به پرهیز از double capturing

با مفهوم capture در این مطلب آشنا شدیم. در مثال زیر دوبار از پارامتر سازنده‌ی age، در دو قسمت عمومی شده، استفاده شده‌است:
public class Human(int age)
{
    // initialization
    public int Age { get; set; } = age;

    // capture
    public string Bio => $"My age is {age}!";
}
در این حالت ممکن است استفاده کننده در طول برنامه، با وضعیت ناخواسته‌ی زیر مواجه شود:
var p = new Human(42);
Console.WriteLine(p.Age); // Output: 42
Console.WriteLine(p.Bio); // Output: My age is 42!

p.Age++;
Console.WriteLine(p.Age); // Output: 43
Console.WriteLine(p.Bio); // Output: My age is 42! // !
در اینجا پس از افزودن مقداری به خاصیت عمومی Age، زمانیکه به مقدار عبارت Bio مراجعه می‌شود، خروجی قبلی را دریافت می‌کنیم!
درک بهتر آن، نیاز به #Low-Level C کلاس Human را دارد:
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace CS8Tests
{
  [NullableContext(1)]
  [Nullable(0)]
  public class Human
  {
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int <age>P;
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int <Age>k__BackingField;

    public Human(int age)
    {
      this.<age>P = age;
      this.<Age>k__BackingField = this.<age>P;
      base..ctor();
    }

    public int Age
    {
      [CompilerGenerated] get
      {
        return this.<Age>k__BackingField;
      }
      [CompilerGenerated] set
      {
        this.<Age>k__BackingField = value;
      }
    }

    public string Bio
    {
      get
      {
        DefaultInterpolatedStringHandler interpolatedStringHandler = new DefaultInterpolatedStringHandler(11, 1);
        interpolatedStringHandler.AppendLiteral("My age is ");
        interpolatedStringHandler.AppendFormatted<int>(this.<age>P);
        interpolatedStringHandler.AppendLiteral("!");
        return interpolatedStringHandler.ToStringAndClear();
      }
    }
  }
}
همانطور که مشاهده می‌کنید، کامپایلر، پارامتر age را دوبار، جداگانه capture کرده‌است:
public Human(int age)
{
   this.<age>P = age;
   this.<Age>k__BackingField = this.<age>P;
   base..ctor();
}
به همین جهت است که ++p.Age، فقط بر روی یکی از فیلدهای capture شده تاثیر داشته و بر روی دیگری خیر. به این مورد، double capturing گفته می‌شود و توصیه شده از آن پرهیز کنید و بجای استفاده‌ی دوباره از پارامتر age، از خود خاصیت Age استفاده نمائید.
مطالب
منسوخ شدن DllImport در دات نت 7
دات نت 7 به همراه یک source generator جدید به نام LibraryImport است که کار جایگزینی DllImport قدیمی را انجام می‌دهد. برای مثال تا پیش از دات نت 7 برای فراخوانی یک متد native موجود در یک DLL نوشته شده‌ی به زبان‌های ++C/C، به صورت زیر عمل می‌شد:
[DllImport(
   "nativelib",
   EntryPoint = "to_lower",
   CharSet = CharSet.Unicode)]
internal static extern string ToLower(string str);

// string lower = ToLower("StringToConvert");
کاری که در اینجا در پشت صحنه انجام می‌شود، نوشتن کدهای IL مرتبطی، توسط NET runtime. است تا تبادل اطلاعات بین دو محیط متفاوت managed و unmanaged را میسر کند. چون این کدها در زمان اجرا تولید می‌‌شوند، در اختیار امکانات AOT کامپایلر (ahead-of-time) نیستند و به همین جهت برای مثال سناریوهای IL trimming و کاهش حجم، در مورد آن‌ها اعمال نمی‌شود. همچنین باید درنظر داشت که سکوهای کاری هم هستند که امکان تولید کدهای پویا را در زمان اجرای برنامه ندارند. در یک چنین حالت‌هایی، استفاده از روش‌هایی مانند تولید کد خودکار توسط کامپایلر، ارجحیت بیشتری دارد. همچنین باید درنظر داشت که امکان دیباگ کدهای پشت صحنه‌ی DllImport هم وجود ندارد.


معرفی LibraryImportAttribute در دات نت 7

تولید کننده‌ی کد مخصوص P/Invoke در دات نت 7، به دنبال ویژگی جدید LibraryImportAttribute بر روی متدهای استاتیک و partial می‌گردد تا کدهای متناظر با آن‌ها را تولید کند. به این ترتیب نیاز به تولید اینگونه کدها در زمان اجرای برنامه مرتفع می‌شود و همچنین می‌توان این کدها را در IDE خود بررسی و حتی دیباگ کرد.
[LibraryImport(
   "nativelib",
   EntryPoint = "to_lower",
   StringMarshalling = StringMarshalling.Utf16)]
internal static partial string ToLower(string str);
همانطور که مشاهده می‌کنید، کارکرد این ویژگی بسیار شبیه به DllImportAttribute است که برای استفاده‌ی از آن، متد قبلی، از حالت extern، به static partial تبدیل شده‌است.


امکان تبدیل خودکار کدهای قدیمی مبتنی بر DllImportAttribute به نمونه‌های جدید

برای تبدیل خودکار کدهای قدیمی موجود، فقط کافی است یک سطر زیر را به فایل editorconfig. پروژه‌ی خود اضافه کنید:
dotnet_diagnostic.SYSLIB1054.severity = suggestion
پس از آن یک code fix و analyzer خودکار و توکار ظاهر شده و امکان تبدیل خودکار کدهای DllImport دار قدیمی را به نمونه‌های جدید LibraryImport دار، می‌دهد.


تغییرات صورت گرفته نسبت به DllImport قدیمی

نحوه‌ی تعریف LibraryImportAttribute در اکثر موارد با DllImportAttribute تطابق دارد، منهای موارد زیر:
- در اینجا معادلی برای CallingConvention وجود ندارد. برای اینکار از UnmanagedCallConvAttribute استفاده می‌شود.
- CharSet با StringMarshalling تعویض شده‌است. ANSI حذف شده‌است و UTF-8 حالت پیش‌فرض است. برای مثال:
 // Before

public static class Native
{
   [DllImport(nameof(Native), CharSet = CharSet.Unicode)]
   public extern static string ToLower(string str);
}

// After

public static partial class Native
{
   [LibraryImport(nameof(Native), StringMarshalling = StringMarshalling.Utf16)]
   public static partial string ToLower(string str);
}