مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 16 - کار با Sessions
سشن‌ها نیز همانند تمام قسمت‌های دیگر یک برنامه‌ی ASP.NET Core، به صورت پیش فرض غیرفعال هستند و نیاز به مراحل خاصی است تا امکان استفاده‌ی از آن‌ها فراهم شود. همچنین روش کار کردن با آن‌ها نیز متفاوت است با نگارش‌های قبلی ASP.NET (تمام نگارش‌ها).


سشن چیست؟

شیء سشن، مجموعه‌ای از اشیاء serialized مرتبط با جلسه‌ی کاری جاری یک کاربر است. این اشیاء عموما در حافظه‌ی محلی سرور ذخیره می‌شوند؛ اما امکان ذخیره سازی توزیع شده‌ی آن‌ها در بانک‌های اطلاعاتی نیز پیش بینی شده‌است.
عموما استفاده‌ی از اشیاء سشن توصیه نمی‌شوند. از این جهت که این نوع اشیاء بسیار شبیه هستند به متغیرهای سراسری و وجود این نوع متغیرها اساسا ضعف طراحی شیء‌گرا به حساب می‌آیند. اما با توجه به ماهیت stateless بودن برنامه‌های وب، به این معنا که با پایان رندر یک صفحه، تمام اشیاء مرتبط با آن‌ها نیز در سمت سرور تخریب می‌شوند، نیاز است برای یک سری از داده‌های عمومی کاربر، راه حلی را پیدا کرد تا بتوان از اطلاعات آن‌ها استفاده‌ی مجدد کرد. برای مثال نگهداری رشته‌ی اتصالی بانک اطلاعاتی که کاربر در حین لاگین به سیستم آن‌را انتخاب کرده‌است (اگر برنامه به ازای هر سال از یک بانک اطلاعاتی مجزا استفاده می‌کند) و یا زمانیکه کاربری captcha را پر می‌کند و مقدار آن‌را به سمت سرور ارسال می‌کند، نیاز است مقدار ارسالی او را با مقدار ابتدایی captcha مقایسه کرد. یک چنین اطلاعاتی نباید با پایان رندر صفحه تخریب شوند و نیاز است تا زمانیکه جلسه‌ی کاری کاربر به پایان نرسیده‌است، در دسترس باشند. به همین جهت است که مفهومی را به نام «اشیاء سشن» طراحی کرده‌اند.
درکل خارج از این موارد بهتر است از سشن استفاده نکنید و در جای جای برنامه‌ی خود ردپای آن‌را باقی نگذارید و به خاطر داشته باشید:
متغیر سشن = متغیر سراسری = ضعف طراحی شی‌‌ءگرا


توصیه‌ی به استفاده‌ی از روش‌های سبک وزن‌تر

سشن‌ها تنها روش به اشتراک گذاری اطلاعات نیستند. اگر می‌خواهید اطلاعاتی را در بین میان افزارهای برنامه در طی یک درخواست به اشتراک بگذارید، شاید سشن هم یک راه حل باشد؛ اما راه حلی سنگین وزن. راه حل بهتر برای این موارد، استفاده‌ی از HttpContext.Items است. HttpContext.Items نیز همانند سشن، یک key/value store است؛ اما طول عمر آن محدود است به طول عمر درخواست جاری و در تمام میان افزارهای برنامه در دسترس است.
برای مثال در یک میان افزار آن‌را تنظیم می‌کنید:
app.Use(async (context, next) =>
{
   context.Items["isVerified"] = true;
   await next.Invoke();
});
و سپس در میان افزاری دیگر از آن استفاده خواهید کرد:
app.Run(async (context) =>
{
   await context.Response.WriteAsync("Verified request? " + context.Items["isVerified"]);
});


فعال سازی سشن‌ها در ASP.NET Core

ASP.NET Core یک choose-what-you-need framework است. به این معنا که تا زمانیکه قابلیتی را به صورت صریح فعال سازی نکرده باشید، در دسترس نخواهد بود. همین مساله در نهایت به کاهش مصرف منابع این نوع برنامه‌ها و همچنین طراحی ماژولار سیستم ختم می‌شوند. برای مثال در نگارش‌های قبلی ASP.NET (تمام نگارش‌ها)، سشن‌ها به صورت پیش فرض فعال هستند، مگر آنکه HTTP Module آن‌را در فایل web.config حذف کنید؛ اما در اینجا برعکس است.
اگر تنها در موارد خاصی که ذکر شد، نیاز به استفاده‌ی از متغیرهای سشن را داشتید، روش فعال سازی آن به صورت ذیل است:
الف) نصب بسته‌ی نیوگت Microsoft.AspNetCore.Session
برای این منظور وابستگی ذیل را به فایل project.json اضافه کنید:
{
    "dependencies": {
      //same as before
      "Microsoft.AspNetCore.Session": "1.0.0"
    }
}
ب) انجام تنظیمات آغازین برنامه
برای این کار به کلاس آغازین برنامه مراجعه کرده و ابتدا سرویس سشن‌ها را فعال کنید:
public void ConfigureServices(IServiceCollection services)
{
    services.AddSession();
و سپس در متد Configure، استفاده‌ی از سشن‌ها را نیز باید ذکر کنید:
public void Configure(IApplicationBuilder app)
{
    app.UseSession();
در اینجا امکان سفارشی سازی مقادیر پیش فرض مدیریت سشن‌ها نیز وجود دارند. برای مثال زمان پیش فرض انقضای سشن کاربر، پس از 20 دقیقه عدم فعالیت او است که قابل تغییر می‌باشد:
app.UseSession(options: new SessionOptions
{
    IdleTimeout = TimeSpan.FromMinutes(30),
    CookieName = ".MyApplication"
});


روش استفاده‌ی از سشن‌ها

در مثال ذیل نحوه‌ی ذخیره سازی اطلاعات را در شیء سشن جلسه‌ی جاری یک کاربر، ملاحظه می‌کنید:
public ActionResult TestSession()
{
 
   this.HttpContext.Session.Set("key-1", BitConverter.GetBytes(DateTime.Now.Ticks));
   this.HttpContext.Session.SetInt32("key-2", 1);
   this.HttpContext.Session.SetString("key-3", "DNT");
 
   return Content("OK!");
}
به همراه نحوه‌ی بازیابی این اطلاعات در متدهای دیگر برنامه:
 public IActionResult Index()
{
   byte[] key1 = this.HttpContext.Session.Get("key-1");
   long key1Value = BitConverter.ToInt64(key1, 0);
 
   int? key2Value = this.HttpContext.Session.GetInt32("key-2");
   string key3Value = this.HttpContext.Session.GetString("key-3");
 
   return Content("OK!");
}
همانطور که ملاحظه می‌کنید، سه متد Set، SetInt32 و SetString در اینجا برای تنظیم key/valueهای سشن، از پیش موجود هستند.
حالت Set آن آرایه‌ای از بایت‌ها را دریافت می‌کند و می‌توان برای حالت serialization اشیاء، مفید باشد.
دقیقا معادل همین سه متد، متدهای Get، GetInt32 و GetString برای بازیابی مقادیر سشن طراحی شده‌اند و باید دقت داشت که خروجی‌های این‌ها می‌توانند نال نیز باشند. به همین جهت خروجی GetInt32 آن نال پذیر است.


توسعه‌ی متدهای پیش فرض کار با سشن‌ها

سه متد یاد شده‌ی کار با سشن‌ها در ASP.NET Core هرچند ضروری هستند، اما کافی نیستند. برای توسعه‌ی آن‌ها می‌توان متدهای الحاقی را تدارک دید که نمونه‌ای از آن‌ها را ذیل مشاهده می‌کنید:
using System;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
 
namespace Core1RtmEmptyTest.StartupCustomizations
{
    public static class SessionExts
    {
        public static void SetDateTime(this ISession collection, string key, DateTime value)
        {
            collection.Set(key, BitConverter.GetBytes(value.Ticks));
        }
 
        public static DateTime? GetDateTime(this ISession collection, string key)
        {
            var data = collection.Get(key);
            if (data == null)
            {
                return null;
            }
            var dateInt = BitConverter.ToInt64(data, 0);
            return new DateTime(dateInt);
        }
 
        public static void SetObject(this ISession session, string key, object value)
        {
            var stringValue = JsonConvert.SerializeObject(value);
            session.SetString(key, stringValue);
        }
 
        public static T GetObject<T>(this ISession session, string key)
        {
            var stringValue = session.GetString(key);
            return JsonConvert.DeserializeObject<T>(stringValue);
        }
    }
}
در اینجا با استفاده از کلاس BitConverter و امکان سریالایز مقادیر توسط آن به آرایه‌ای از بایت‌ها، امکان کار با متد‌های عمومی Set و Get را یافته‌ایم.
و یا جهت کار با اشیاء پیچیده‌تر می‌توان از کتابخانه‌ی JSON.NET استفاده کرد. به عبارتی در این نگارش از ASP.NET، کار سریالایز و دی‌سریالایز اشیاء، به برنامه نویس واگذار شده‌است و اینکه در پشت صحنه از چه کتابخانه‌ای می‌خواهید استفاده کنید، در اختیار خودتان است.
البته باید دقت داشت که در اینجا وابستگی JSON.NET به صورت خودکار در دسترس است. از این جهت که بسیاری از وابستگی‌های ASP.NET Core مانند مورد ذیل، به JSON.NET وابسته‌اند و نصب آن‌ها به معنای نصب خودکار JSON.NET نیز هست:
{
    "dependencies": {
     //same as before
     "Microsoft.Extensions.Configuration.Json": "1.0.0"
   }
}
اگر لیست بسته‌های وابسته‌ی به JSON.NET را می‌خواهید مشاهده کنید، فایل project.lock.json را گشوده و در آن Newtonsoft.Json را جستجو کنید.


یک مطلب تکمیلی

در اینجا نیز امکان ذخیره سازی سشن‌ها در بانک اطلاعاتی بجای حافظه‌ی فرار سرور درنظر گرفته شده‌است و برای این حالت، بانک‌های اطلاعاتی NoSQL ویژه‌ای به نام key/value stores مانند بانک اطلاعاتی فوق سریع Redis پیشنهاد می‌شود؛ هرچند امکان کار با SQL Server نیز در اینجا وجود دارد، اما برای کش سرورهای مبتنی بر key/value ها، بانک اطلاعاتی Redis، انتخاب اول است.
Managing Application State
مطالب
آغاز کار با WPF
من خودم به شخصه هنوز تا به حال با WPF کار نکرده‌ام؛ اما قصد دارم از امروز در هر فرصتی که پیش می‌آید به یادگیری این فناوری پر سر و صدا بپردازم. از آنجا که مجموعه‌ی مرتب و به ترتیبی مثل MVC و EF در این زمینه در سایت موجود نبود، تصمیم گرفتم که خودم استارت این کار را بزنم که باعث میشه هم خودم بهتر یاد بگیرم و هم این سری برای افراد تازه کار موجود باشه.

آشنایی اولیه
WPF مخفف عبارات Windows Presentation Foundation است که ویکی پدیا این گونه ترجمه می‌کند : بنیاد نمایش ویندوزی. در برنامه نویسی «ویندوز فرم» ما تمرکز دقیقی بر ساخت رابط کاربری برنامه به خصوص در رزولوشن‌های مختلف نداریم و در بسیاری از اوقات کد با رابط کاربری به شدت وابسته میشد که با ارائه WPF از نسخه‌ی سوم دات نت فریم ورک به بعد، این مشکل حل شد و همچنین عملیات refactoring  را بسیار ساده‌تر کرد. در حالت ویندوز فرم به خاطر وابستگی شدید کد و UI، عملیات بهینه سازی کد اصلا موفق نبود.
 WPF از ترکیب عناصر دو بعدی و سه بعدی، اسناد، موارد چند رسانه‌ای و رابط کاربری تشکیل شده‌است و موتور رندر آن بر اساس اطلاعات برداری از کارت گرافیک جهت نمایش ظاهر برنامه کمک می‌گیرد که باعث تهیه برنامه‌ای با رابط کاربری سریعتر، مقیاس پذیرتر و بدون وابستگی به رزولوشن می‌شود.

جداسازی رفتارها و ظاهر برنامه

همانطور که گفتیم بخش رابط کاربری دیگر مستقل از کد برنامه شده است و ظاهر برنامه توسط زبان نشانه گذاری XAML ایجاد می‌شود و بخش کد هم با یکی از زبان‌های موجود در مجموعه دات نت نوشته خواهد شد. نهایتا این دو بخش توسط رویدادها، فرامین و DataBinding با یکدیگر متصل می‌شوند. از مزایای جدا بودن این ویژگی:

  • عدم وابستگی این دو بخش
  • طراح و کدنویس می‌توانند هر کدام به طور جداگانه کار کنند.
  • ابزارهای طراحی میتوانند به طور جداگانه‌ای بر روی اسناد XML کار کنند بدون اینکه نیاز به درگیری با کدنویسی داشته باشند.
یکی از برنامه هایی که به طراحی رابط کاربری با پشتیبانی از XAML می‌پردازد برنامه Microsoft Experssion Blend از مجموعه Blend است


Rich Composition
یکی از ویژگی‌های XAML، ساخت اشیاء ترکیبی هست که به راحتی با ترکیب تگ‌ها با یکدیگر و قرار دادن هر شیء داخل یک شیء دیگر می‌توان به یک شیء جدید دست یافت؛ مثل قرار دادن مجموعه ویدیوها در یک لیست. شیء زیر از ترکیب سه شیء تصویر و متن و دکمه ایجاد شده است:
<Button Margin="148,123,126,130">
            <StackPanel Orientation="Horizontal">
                <Image Source="speaker.png" Stretch="Uniform"/>
                <TextBlock Text="Play Sound" VerticalAlignment="Center" Margin="10" />
            </StackPanel>
        </Button>


Highly Customizable
با استفاده از مفهوم Style همانند آنچه که در Html و CSS دارید می‌توانید اشیاء خود را خصوصی سازی کنید و ظاهر آن شیء را به طور کل تغییر دهید.



Resolution Independence
عدم وابستگی به رزولوشن یا وضوح تصویر دارد و به جای واحد پیکسل، از یک واحد منطقی که یک نود و ششم اینچ است، بهره می‌برد. از آنجا که این سیستم بر اساس وکتور ایجاد شده است، مقیاس پذیری آن در تغییر اندازه یا وضوح تصویر به شدت بالا رفته است.

به زودی در قسمت اول این سری کار را با XAML آغاز خواهیم کرد.
نظرات مطالب
ارسال انواع بی نام (Anonymous) بازگشتی توسط Entity framework به توابع خارجی
IList<Types> typesList = context.Types.Include(x=>x.Categories)
                                               .Include(x=>x.Items).Include(x=>x.Comments).ToList();
اگر از EF استفاده می‌کنید، استفاده از متد Include کار جوین رو برای شما انجام می‌ده. بعدش نیازی به استفاده از متد CreateGenericListFromAnonymous که فقط یک سطح رو بررسی می‌کنه نیست. برای بررسی بیشتر از یک سطح باید متد بازگشتی نوشته بشه ولی در حالت شما واقعا نیازی نیست. ضمنا متد تصویر شما با متد نوشته شده یکی نیست.
مطالب
EF Code First #13

استفاده مستقیم از عبارات SQL در EF Code first

طراحی اکثر ORMهای موجود به نحوی است که برنامه نهایی شما را مستقل از بانک اطلاعاتی کنند و این پروایدر نهایی است که معادل‌های صحیح بسیاری از توابع توکار بانک اطلاعاتی مورد استفاده را در اختیار EF قرار می‌دهد. برای مثال در یک بانک اطلاعاتی تابعی به نام substr تعریف شده، در بانک اطلاعاتی دیگری همین تابع substring نام دارد. اگر برنامه را به کمک کوئری‌های LINQ تهیه کنیم، نهایتا پروایدر نهایی مخصوص بانک اطلاعاتی مورد استفاده است که این معادل‌ها را در اختیار EF قرار می‌دهد و برنامه بدون مشکل کار خواهد کرد. اما یک سری از موارد شاید معادلی در سایر بانک‌های اطلاعاتی نداشته باشند؛ برای مثال رویه‌های ذخیره شده یا توابع تعریف شده توسط کاربر. امکان استفاده از یک چنین توانایی‌هایی نیز با اجرای مستقیم عبارات SQL در EF Code first پیش بینی شده و بدیهی است در این حالت برنامه به یک بانک اطلاعاتی خاص گره خواهد خورد؛ همچنین مزیت استفاده از کوئری‌های Strongly typed تحت نظر کامپایلر را نیز از دست خواهیم داد. به علاوه باید به یک سری مسایل امنیتی نیز دقت داشت که در ادامه بررسی خواهند شد.


کلاس‌های مدل مثال جاری

در مثال جاری قصد داریم نحوه استفاده از رویه‌های ذخیره شده و توابع تعریف شده توسط کاربر مخصوص SQL Server را بررسی کنیم. در اینجا کلاس‌های پزشک و بیماران او، کلاس‌های مدل برنامه را تشکیل می‌دهند:

using System.Collections.Generic;

namespace EF_Sample08.DomainClasses
{
public class Doctor
{
public int Id { set; get; }
public string Name { set; get; }

public virtual ICollection<Patient> Patients { set; get; }
}
}

namespace EF_Sample08.DomainClasses
{
public class Patient
{
public int Id { set; get; }
public string Name { set; get; }

public virtual Doctor Doctor { set; get; }
}
}

کلاس Context برنامه به نحو زیر تعریف شده:

using System.Data.Entity;
using EF_Sample08.DomainClasses;

namespace EF_Sample08.DataLayer.Context
{
public class Sample08Context : DbContext
{
public DbSet<Doctor> Doctors { set; get; }
public DbSet<Patient> Patients { set; get; }
}
}

و اینبار کلاس DbMigrationsConfiguration تعریف شده اندکی با مثال‌های قبلی متفاوت است:

using System.Data.Entity.Migrations;
using EF_Sample08.DomainClasses;
using System.Collections.Generic;

namespace EF_Sample08.DataLayer.Context
{
public class Configuration : DbMigrationsConfiguration<Sample08Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}

protected override void Seed(Sample08Context context)
{
addData(context);
addSP(context);
addFn(context);
base.Seed(context);
}

private static void addData(Sample08Context context)
{
var patient1 = new Patient { Name = "p1" };
var patient2 = new Patient { Name = "p2" };
var doctor1 = new Doctor { Name = "doc1", Patients = new List<Patient> { patient1, patient2 } };
context.Doctors.Add(doctor1);
}

private static void addFn(Sample08Context context)
{
context.Database.ExecuteSqlCommand(
@"IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[FindDoctorPatientsCount]')
AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[FindDoctorPatientsCount]");
context.Database.ExecuteSqlCommand(
@"CREATE FUNCTION FindDoctorPatientsCount(@Doctor_Id INT)
RETURNS INT
BEGIN
RETURN
(
SELECT COUNT(*)
FROM Patients
WHERE Doctor_Id = @Doctor_Id
);
END");
}

private static void addSP(Sample08Context context)
{
context.Database.ExecuteSqlCommand(
@"IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[FindDoctorsStartWith]')
AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[FindDoctorsStartWith]
");
context.Database.ExecuteSqlCommand(
@"CREATE PROCEDURE FindDoctorsStartWith(@name NVARCHAR(400))
AS
SELECT *
FROM Doctors
WHERE [Name] LIKE @name + '%'");
}
}
}

در اینجا از متد Seed علاوه بر مقدار دهی اولیه جداول، برای تعریف یک رویه ذخیره شده به نام FindDoctorsStartWith و یک تابع سفارشی به نام FindDoctorPatientsCount نیز استفاده شده است. متد context.Database.ExecuteSqlCommand مستقیما یک عبارت SQL را بر روی بانک اطلاعاتی اجرا می‌کند.

در ادامه کدهای کامل برنامه نهایی را ملاحظه می‌کنید:
using System;
using System.Data;
using System.Data.Entity;
using System.Data.Objects.SqlClient;
using System.Data.SqlClient;
using System.Linq;
using EF_Sample08.DataLayer.Context;
using EF_Sample08.DomainClasses;

namespace EF_Sample08
{
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Sample08Context, Configuration>());

using (var db = new Sample08Context())
{
runSp(db);
runFn(db);
usingSqlFunctions(db);
}
}

private static void usingSqlFunctions(Sample08Context db)
{
var doctorsWithNumericNameList = db.Doctors.Where(x => SqlFunctions.IsNumeric(x.Name) == 1).ToList();
if (doctorsWithNumericNameList.Any())
{
//do something
}
}

private static void runFn(Sample08Context db)
{
var doctorIdParameter = new SqlParameter
{
ParameterName = "@doctor_id",
Value = 1,
SqlDbType = SqlDbType.Int
};
var patientsCount = db.Database.SqlQuery<int>("select dbo.FindDoctorPatientsCount(@doctor_id)", doctorIdParameter).FirstOrDefault();
Console.WriteLine(patientsCount);
}

private static void runSp(Sample08Context db)
{
var nameParameter = new SqlParameter
{
ParameterName = "@name",
Value = "doc",
Direction = ParameterDirection.Input,
SqlDbType = SqlDbType.NVarChar
};
var doctors = db.Database.SqlQuery<Doctor>("exec FindDoctorsStartWith @name", nameParameter).ToList();
if (doctors.Any())
{
foreach (var item in doctors)
{
Console.WriteLine(item.Name);
}
}
}
}
}

توضیحات

همانطور که ملاحظه می‌کنید، برای اجرای مستقیم یک عبارت SQL صرفنظر از اینکه یک رویه ذخیره شده است یا یک تابع و یا یک کوئری معمولی، باید از متد db.Database.SqlQuery استفاده کرد. خروجی این متد از نوع IEnumerable است و این توانایی را دارد که رکوردهای بازگشت داده شده از بانک اطلاعاتی را به خواص یک کلاس به صورت خودکار نگاشت کند.
پارامتر اول متد db.Database.SqlQuery، عبارت SQL مورد نظر است. پارامتر دوم آن باید توسط وهله‌هایی از کلاس SqlParameter مقدار دهی شود. به کمک SqlParameter نام پارامتر مورد استفاده، مقدار و نوع آن مشخص می‌گردد. همچنین Direction آن نیز برای استفاده از رویه‌های ذخیره شده ویژه‌ای که دارای پارامتری از نوع out هستند درنظر گرفته شده است.

چند نکته

- در متد runSp فوق، متد الحاقی ToList را حذف کرده و برنامه را اجرا کنید. بلافاصله پیغام خطای «The SqlParameter is already contained by another SqlParameterCollection.» ظاهر خواهد شد. علت هم این است که با بکارگیری متد ToList، تمام عملیات یکبار انجام شده و نتیجه بازگشت داده می‌شود اما اگر به صورت مستقیم از خروجی IEnumerable آن استفاده کنیم، در حلقه foreach تعریف شده، ممکن است این فراخوانی چندبار انجام شود. به همین جهت ذکر متد ToList در اینجا ضروری است.

- عنوان شد که در اینجا باید به مسایل امنیتی دقت داشت. بدیهی است امکان نوشتن یک چنین کوئری‌هایی نیز وجود دارد:

db.Database.SqlQuery<Doctor>("exec FindDoctorsStartWith "+ txtName.Text, nameParameter).ToList()

در این حالت به ظاهر مشغول به استفاده از رویه‌های ذخیره شده‌ای هستیم که عنوان می‌شود در برابر حملات تزریق SQL در امان هستند، اما چون در کدهای ما به نحو ناصحیحی با جمع زدن رشته‌ها مقدار دهی شده است، برنامه و بانک اطلاعاتی دیگر در امان نخواهند بود. بنابراین در این حالت استفاده از پارامترها را نباید فراموش کرد.
زمانیکه از کوئری‌های LINQ استفاده می‌شود تمام این مسایل توسط EF مدیریت خواهد شد. اما اگر قصد دارید مستقیما عبارات SQL را فراخوانی کنید، تامین امنیت برنامه به عهده خودتان خواهد بود.

- در متد usingSqlFunctions از SqlFunctions.IsNumeric استفاده شده است. این مورد مختص به SQL Server است و امکان استفاده از توابع توکار ویژه SQL Server را در کوئری‌های LINQ برنامه فراهم می‌سازد. برای مثال متدالحاقی از پیش تعریف شده‌ای به نام IsNumeric به صورت مستقیم در دسترس نیست، اما به کمک کلاس SqlFunctions این تابع و بسیاری از توابع دیگر توکار SQL Server قابل استفاده خواهند بود.
اگر علاقمند هستید که لیست این توابع را مشاهده کنید، در ویژوال استودیو بر روی SqlFunctions کلیک راست کرده و گزینه Go to definition را انتخاب کنید.


نظرات مطالب
فعال سازی قسمت آپلود تصویر و فایل Kendo UI Editor
بارگذاری تصویر در این ادیتور بر اساس تنظیم پارامتر imageUrl در سمت کلاینت انجام می‌شود. یعنی تصویر قرار گرفته در ادیتور برای ذخیره‌ی نهایی، src تگ img آن بر اساس آدرس و پارامتر imageUrl درج خواهد شد (و نه یک آدرس مستقیم) که معادل است با اکشن متد زیر در سمت سرور:
[HttpGet]
public ActionResult GetFile(string path)
{
    path = GetSafeFileAndDirPath(path);
    return File(path, "image/png");
}
این روش در سایر ادیتورها هم بکار رفته‌است (^ و ^).
البته مثال فوق آزمایش شده‌است و در این بین از روش دیباگ اسکریپت‌ها و یافتن خطاهای سمت سرور و کاربر کمک گرفته شد (کار کردن با کتابخانه‌های جاوا اسکریپتی، بدون باز نگه داشتن کنسول developer مرورگرها تقریبا غیر ممکن است).
مطالب دوره‌ها
استفاده از async و await در برنامه‌های کنسول و سرویس‌های ویندوز NT
فرض کنید می‌خواهید از await در متد Main یک برنامه‌ی کنسول به نحو ذیل استفاده کنید:
using System;
using System.Net;

namespace Async15
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var webClient = new WebClient { })
            {
                webClient.Headers.Add("User-Agent", "AsyncContext 1.0");
                var data = await webClient.DownloadStringTaskAsync("https://www.dntips.ir");
                Console.WriteLine(data);
            }
        }
    }
}
کامپایلر چنین اجازه‌ای را نمی‌دهد. زیرا از await جایی می‌توان استفاده کرد که متد فراخوان آن با async مزین شده باشد و همچنین دارای یک Context باشد تا نتیجه را بتواند دریافت کند. اگر در اینجا سعی کنید async را به امضای متد Main اضافه نمائید، کامپایلر مجددا خطای an entry point cannot be marked with the 'async' modifier را صادر می‌کند.
اضافه کردن واژه‌ی کلیدی async به روال‌های رخدادگردان void برنامه‌های دسکتاپ مجاز است؛ با توجه به اینکه متد async پیش از پایان کار به فراخوان بازگشت داده می‌شوند (ذات متدهای async به این نحو است). در برنامه‌های دسکتاپ، این بازگشت به UI event loop است؛ بنابراین برنامه بدون مشکل به کار خود ادامه خواهد داد. اما در اینجا، بازگشت متد Main، به معنای بازگشت به OS است و خاتمه‌ی برنامه. به همین جهت کامپایلر از async کردن آن ممانعت می‌کند.
برای حل این مشکل در برنامه‌های کنسول و همچنین برنامه‌های سرویس ویندوز NT که دارای یک async-compatible context نیستند، می‌توان از یک کتابخانه‌ی کمکی سورس باز به نام Nito AsyncEx استفاده کرد. برای نصب آن دستور ذیل را در کنسول پاورشل نیوگت وارد کنید:
 PM> Install-Package Nito.AsyncEx
پس از نصب برای استفاده از آن خواهیم داشت:
using System;
using System.Net;
using Nito.AsyncEx;

namespace Async15
{
    class Program
    {
        static void Main(string[] args)
        {
            AsyncContext.Run(async () =>
            {
                using (var webClient = new WebClient())
                {
                    webClient.Headers.Add("User-Agent", "AsyncContext 1.0");
                    var data = await webClient.DownloadStringTaskAsync("https://www.dntips.ir");
                    Console.WriteLine(data);
                }
            });
        }
    }
}
Context ارائه شده در اینجا برخلاف مثال‌های قسمت‌های قبل، نیازی به فراخوانی متد همزمان Wait و یا خاصیت Result که هر دو از نوع blocking هستند ندارد و یک فراخوانی async واقعی است. همچنین می‌شد یک متد async void را نیز در اینجا برای استفاده از DownloadStringTaskAsync تعریف کرد (تا برنامه کامپایل شود). اما پیشتر عنوان شد که هدف از این نوع متدهای خاص async void صرفا استفاده از آن‌ها در روال‌های رخدادگردان UI هستند. زیرا ماهیت آن‌ها fire and forget است و برای دریافت نتیجه‌ی نهایی به نحوی باید ترد اصلی را قفل کرد. برای مثال در یک برنامه‌ی کنسول متد Console.ReadLine را در انتهای کار فراخوانی کرد. اما با استفاده از AsyncContext.Run نیازی به این کارها نیست.


async lambda
در مثال فوق از یک async lambda، برای فراخوانی استفاده شده است که به همراه دات نت 4.5 ارائه شده‌اند:
Action, () => { }
Func<Task>, async () => { await Task.Yield(); }

Func<TResult>, () => { return 13; }
Func<Task<TResult>>, async () => { await Task.Yield(); return 13; }
آرگومان متد AsyncContext.Run از نوع Func of Task است. بنابراین برای مقدار دهی inline آن توسط lambda expressions مطابق مثال‌های فوق می‌توان از async lambda استفاده کرد.
روش دوم استفاده از AsyncContext.Run و مقدار دهی Func of Task، تعریف یک متد مستقل async Task دار، به نحو ذیل است:
class Program
{
  static async Task<int> AsyncMain()
  {
    ..
  }

  static int Main(string[] args)
  {
    return AsyncContext.Run(AsyncMain);
  }
}


رخ‌دادهای مرتبط با طول عمر برنامه را async تعریف نکنید

همانند متد Main که async تعریف کردن آن سبب بازگشت آنی روال کار به OS می‌شود و برنامه خاتمه می‌یابد، روال‌های رخدادگردانی که با طول عمر یک برنامه‌ی UI سر و کار دارند مانند Application_Launching، Application_Closing، Application_Activated و Application_Deactivated (خصوصا در برنامه‌های ویندوز 8) نیز نباید async void تعریف شوند (چون مطابق ذات متدهای async، بلافاصله به برنامه اعلام می‌کنند که کار تمام شد). در این موارد خاص نیز می‌توان از متد AsyncContext.Run برای انجام اعمال async استفاده کرد.
مطالب دوره‌ها
شی گرایی در #F
برنامه نویسی شی گرای سومین نسل از الگوهای اصلی برنامه نویسی است. در توضیحات فصل اول گفته شد که #F یک زبان تابع گرا است ولی این بدان معنی نیست که #F از مفاهیمی نظیر کلاس و یا interface پشتیبانی نکند. برعکس در #F امکان تعریف کلاس و interface و هم چنین پیاده سازی مفاهیم شی گرایی وجود دارد.

*با توجه به این موضوع که فرض است دوستان با مفاهیم شی گرایی آشنایی دارند از توضیح و تشریح این مفاهیم خودداری می‌کنم.

Classes
کلاس چارچوبی از اشیا است برای نگهداری خواص(Properties) و رفتار ها(Methods) و رخدادها(Events). کلاس پایه ای‌ترین مفهوم در برنامه نویسی شی گراست. ساختار کلی تعربف کلاس در #F به صورت زیر است:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] =
   [ class ]
     [ inherit base-type-name(base-constructor-args) ]
     [ let-bindings ]
     [ do-bindings ]
     member-list
      ...
   [ end ]

type [access-modifier] type-name1 ...
and [access-modifier] type-name2 ...
...
همان طور که در ساختار بالا می‌بینید مفاهیم access-modifier و inherit و constructor هم در #F وجود دارد.

انواع access-modifier در #F
  • public : دسترسی برای تمام فراخوان‌ها امکان پذیر است
  • internal : دسترسی برای تمام فراخوان هایی که در همین assembly هستند امکان پذیر است
  • private : دسترسی فقط برای فراخوان‌های موجود در همین ماژول امکان پذیر است

نکته : protected access modifier در #F پشتیبانی نمی‌شود.

مثالی از تعریف کلاس:

type Account(number : int, name : string) = class
    let mutable amount = 0m
   
end
کلاس بالا دارای یک سازنده است که دو پارامتر ورودی می‌گیرد. کلمه end به معنای انتهای کلاس است. برای استفاده کلاس باید به صورت زیر عمل کنید:
let myAccount = new Account(123456, "Masoud")
توابع و خواص در کلاس ها
برای تعریف خاصیت در #F باید از کلمه کلیدی member استفاده کنید. در مثال بعدی برای کلاس بالا تابع و خاصیت تعریف خواهیم کرد.
type Account(number : int, name: string) = class
    let mutable amount = 0m
 
    member x.Number = number
    member x.Name= name
    member x.Amount = amount
 
    member x.Deposit(value) = amount <- amount + value
    member x.Withdraw(value) = amount <- amount - value
end
کلاس بالا دارای سه خاصیت به نام‌های Number و Name و Amount است و دو تابع به نام‌های Deposit و Withdraw دارد. اما x استفاده شده قبل از هر member به معنی this در #C  است. در #F شما برای اشاره به شناسه‌های یک محدوده خودتون باید یک نام رو برای اشاره گر مربوطه تعیین کنید.
open System
 
type Account(number : int, name: string) = class
    let mutable amount = 0m
 
    member x.Number = number
    member x.Name= name
    member x.Amount = amount
 
    member x.Deposit(value) = amount <- amount + value
    member x.Withdraw(value) = amount <- amount - value
end
 let masoud= new Account(12345, "Masoud") let saeed = new Account(67890, "Saeed") let transfer amount (source : Account) (target : Account) = source.Withdraw amount target.Deposit amount let printAccount (x : Account) = printfn "x.Number: %i, x.Name: %s, x.Amount: %M" x.Number x.Name x.Amount let main() = let printAccounts() = [masoud; saeed] |> Seq.iter printAccount printfn "\nInializing account" homer.Deposit 50M marge.Deposit 100M printAccounts() printfn "\nTransferring $30 from Masoud to Saeed" transfer 30M masoud saeed
 printAccounts() printfn "\nTransferring $75 from Saeed to Masoud" transfer 75M saeed masoud printAccounts() main()
استفاده از کلمه do
در #F زمانی که قصد داشته باشیم در بعد از وهله سازی از کلاس و فراخوانی سازنده، عملیات خاصی انجام شود(مثل انجام برخی عملیات متداول در سازنده‌های کلاس‌های دات نت) باید از کلمه کلیدی do به همراه یک بلاک از کد استفاده کنیم.
open System
open System.Net
 
type Stock(symbol : string) = class

    let mutable _symbol = String.Empty
    do
     //کد مورد نظر در این جا نوشته  میشود
end
یک مثال در این زمینه:

open System

type MyType(a:int, b:int) as this =
    inherit Object()
    let x = 2*a
    let y = 2*b
    do printfn "Initializing object %d %d %d %d %d %d"
               a b x y (this.Prop1) (this.Prop2)
    static do printfn "Initializing MyType." 
    member this.Prop1 = 4*x
    member this.Prop2 = 4*y
    override this.ToString() = System.String.Format("{0} {1}", this.Prop1, this.Prop2)

let obj1 = new MyType(1, 2)
در مثال بالا دو عبارت do  یکی به صورت static و دیگری به صورت غیر static تعریف شده اند. استفاده از do  به صورت غیر static این امکان را به ما می‌دهد که بتوانیم به تمام شناسه‌ها و توابع تعریف شده در کلاس استفاده کنیم ولی do به صورت static فقط به خواص و توابع از نوع static در کلاس دسترسی دارد.
خروجی مثال بالا:
Initializing MyType.
Initializing object 1 2 2 4 8 16
خواص static:
برای تعریف خواص به صورت استاتیک مانند #C از کلمه کلیدی static استفاده کنید.مثالی در این زمینه:
type SomeClass(prop : int) = class
    member x.Prop = prop
    static member SomeStaticMethod = "This is a static method"
end
SomeStaticMethod به صورت استاتیک تعریف شده در حالی که x.Prop به صورت غیر استاتیک. دسترسی به متد‌ها یا خواص static باید بدون وهله سازی از کلاس انجام بگیرد در غیر این صورت با خطای کامپایلر روبرو خواهید شد.
let instance = new SomeClass(5);;
instance.SomeStaticMethod;; 

output:
stdin(81,1): error FS0191: property 'SomeStaticMethod' is static.
روش استفاده درست:
SomeClass.SomeStaticMethod;; (* invoking static method *)
متد‌های get , set در خاصیت ها:
همانند #C و سایر زبان‌های دات نت امکان تعریف متد‌های get و set برای خاصیت‌های یک کلاس وجود دارد.
ساختار کلی:
 member alias.PropertyName
        with get() = some-value
        and set(value) = some-assignment
مثالی در این زمینه:
type MyClass() = class
   let mutable num = 0 
    member x.Num
        with get() = num
        and set(value) = num <- value
end;;
کد متناظر در #C:
public int Num
{
   get{return num;}
   set{num=value;}
}
یا به صورت:
type MyClass() = class
    let mutable num = 0
 
    member x.Num
        with get() = num
        and set(value) =
            if value > 10 || value < 0 then
                raise (new Exception("Values must be between 0 and 10"))
            else
                num <- value
end

Interface ها
اینترفیس به تمامی خواص و توابع عمومی اشئایی که آن را پیاده سازی کرده اند اشاره می‌کند. (توضیحات بیشتر (^ ) و (^ ))ساختار کلی برای تعریف آن به صورت زیر است:
type type-name = 
   interface
       inherits-decl 
       member-defns 
   end
مثال:
type IPrintable =
   abstract member Print : unit -> unit
استفاده از حرف I برای شروع نام اینترفیس طبق قوانین تعریف شده (اختیاری) برای نام گذاری است.
نکته: در هنگام تعریف توابع و خاصیت در interface‌ها باید از کلمه abstract استفاده کنیم. هر کلاسی که از یک یا چند تا اینترفیس ارث ببرد باید تمام خواص و توابع اینتریس‌ها را پیاده سازی کند. در مثال بعدی کلاس SomeClass1 اینترفیس بالا را پیاده سازی می‌کند. دقت کنید که کلمه this توسط من به عنوان اشاره گر به اشیای کلاس تعیین شده و شما می‌تونید از هر کلمه یا حرف دیگری استفاده کنید.
type SomeClass1(x: int, y: float) =
   interface IPrintable with 
      member this.Print() = printfn "%d %f" x y
نکته مهم: اگر قصد فراخوانی متد Print را در کلاس بالا دارید نمی‌تونید به صورت مستقیم متد بالا را فراخوانی کنید. بلکه حتما باید کلاس به اینترفیس مربوطه cast شود.
روش نادرست:
let instance = new SomeClass1(10,20)
instance.Print//فراخوانی این متد باعث ایجاد خطای کامپایلری می‌شود.
روش درست:
let instance = new SomeClass1(10,20) 
let instanceCast = instance :> IPrintable// استفاده از (<:)  برای عملیات تبدیل کلاس به اینترفیس
instanceCast.Print
برای عملیات cast ازاستفاده کنید.
در مثال بعدی کلاسی خواهیم داشت که از سه اینترفیس ارث می‌برد. در نتیجه باید تمام متد‌های هر سه اینترفیس را پیاده سازی کند.
type Interface1 =
    abstract member Method1 : int -> int

type Interface2 =
    abstract member Method2 : int -> int

type Interface3 =
    inherit Interface1
    inherit Interface2
    abstract member Method3 : int -> int

type MyClass() =
    interface Interface3 with 
        member this.Method1(n) = 2 * n
        member this.Method2(n) = n + 100
        member this.Method3(n) = n / 10
فراخوانی این متد‌ها نیز به صورت زیر خواهد بود:
let instance = new MyClass()
let instanceToCast = instance :> Interface3
instanceToCast.Method3 10
کلاس‌های Abstract
#F از کلاس‌های abstract هم پشتیبانی می‌کند. اگر با کلاس‌های abstract در #C آشنایی ندارید می‌تونید مطالب مورد نظر رو در  (^ ) و (^ ) مطالعه کنید. به صورت خلاصه کلاس‌های abstract به عنوان کلاس‌های پایه در برنامه نویسی شی گرا استفاده می‌شوند. این کلاس‌ها دارای خواص و متد‌های پیاده سازی شده و نشده هستند. خواص و متد هایی که در کلاس پایه abstract پیاده سازی نشده اند باید توسط کلاس هایی که از این کلاس پایه ارث می‌برند حتما پیاده سازی شوند.
ساختار کلی تعریف کلاس‌های abstract:
[<AbstractClass>]
type [ accessibility-modifier ] abstract-class-name =
    [ inherit base-class-or-interface-name ]
    [ abstract-member-declarations-and-member-definitions ]

    abstract member member-name : type-signature
در #F برای این که مشخص کنیم که یک کلاس abstract است حتما باید [<AbstractClass>] در بالای کلاس تعریف شود.
[<AbstractClass>]
type Shape(x0 : float, y0 : float) =
    let mutable x, y = x0, y0
    let mutable rotAngle = 0.0

    abstract Area : float with get
    abstract Perimeter : float  with get
    abstract Name : string with get
کلاس بالا تعریفی از کلاس abstract است که سه خصوصیت abstract دارد (برای تعیین خصوصیت‌ها و متد هایی که در کلاس پایه پیاده سازی نمی‌شوند از کلمه کلیدی abstract در هنگام تعریف آن‌ها استفاده می‌کنیم). حال دو کلاس ایجاد می‌کنیم که این کلاس پایه را پیاده سازی کنند.

#1 کلاس اول
type Square(x, y,SideLength) =
    inherit Shape(x, y)
  override this.Area = this.SideLength * this.SideLength override this.Perimeter = this.SideLength * 4. override this.Name = "Square"
#2 کلاس دوم
type Circle(x, y, radius) =
    inherit Shape(x, y)
 let PI = 3.141592654 member this.Radius = radius override this.Area = PI * this.Radius * this.Radius override this.Perimeter = 2. * PI * this.Radius
Structures
structure‌ها در #F دقیقا معال struct در #C هستند. توضیحات بیشتر درباره struct در #C (^ ) و (^ )). اما به طور خلاصه باید ذکر کنم که strucure‌ها تقریبا دارای مفهوم کلاس هستند با اندکی تفاوت که شامل موارد زیر است:
  • structure‌ها از نوع مقداری هستند و این بدین معنی است مستقیما درون پشته ذخیره می‌شوند.
  • ارجاع به structure‌ها از نوع ارجاع با مقدار است بر خلاف کلاس‌ها که از نوع ارجاع به منبع هستند.(^ )
  • structure‌ها دارای خواص ارث بری نیستند.
  • عموما از structure برای ذخیره مجموعه ای از داده‌ها با حجم و اندازه کم استفاده می‌شود.

ساختار کلی تعریف structure

[ attributes ]
type [accessibility-modifier] type-name =
   struct
      type-definition-elements
   end

//یا به صورت زیر

[ attributes ]
[<StructAttribute>]
type [accessibility-modifier] type-name =
   type-definition-elements
یک نکته مهم هنگام کار با struct‌ها در #F این است که امکان استفاده از let و Binding در struct‌ها وجود ندارد. به جای آن باید از val استفاده کنید.
type Point3D =
   struct 
      val x: float
      val y: float
      val z: float
   end
تفاوت اصلی بین val و let در این است که هنگام تعریف شناسه با val امکان مقدار دهی اولیه به شناسه وجود ندارد. در مثال بالا مقادیر برای x و y و z برابر 0.0 است که توسط کامپایلر انجام می‌شود. در ادامه یک struct به همراه سازنده تعریف می‌کنیم:
type Point2D =
   struct 
      val X: float
      val Y: float
      new(x: float, y: float) = { X = x; Y = y }
   end
توسط سازنده struct بالا مقادیر اولیه x و y دریافت می‌شود به متغیر‌های متناظر انتساب می‌شود.

  در پایان یک مثال مشترک رو در #C و #F پیاده سازی می‌کنیم:


مطالب
ایجاد شناسه ی منحصر به فرد برای هر سیستم
چند روز پیش یک افزونه در nuget نظرم رو به خودش جلب کرد . بعد از دانلود و نصب اون و مقداری کار کردن باهاش جای خودش رو تو دلم باز کرد ولی متاسفانه این افزونه تا 21 روز رایگان بود. توی نت برای پیدا کردن سریال و یا کرکش زیاد گشتم ولی هیچ چیز یافت نشد . شاید به خاطر اینکه از زمان تولیدش زیاد نمیگذره ... در هر حال گذشتن از خیرش برام سخت بود بنابر این به یاد قدیم تصمیم گرفتم خودم دست به کار بشم و release کنمش ... 
بعد از deobfuscate  کردنش سیستم امنیتیش نکات خیلی جالبی داشت که یکیش ایجاد شناسه‌ی منحصر به فرد برای هر سیستم بود . البته شاید باورش سخت باشه ولی برای بخش امنیتش 23 تا کلاس داشت که هر کدوم کلی تابع و ... داشتن که همه هم به هم مرتبط بود . تا به حال ندیده بودم توی هیچ افزونه ای اینقدر روی امنیتش کار بشه و خب همینم باعث شد 5-4 ساعت وقتمو بگیره ...

کد زیر رو از یکی از کلاس هاش استخراج کردم که توسط اون میتونید یک شناسه منحصر به فرد بر اساس مشخصات پردازنده - برد اصلی و ... تولید کنید . فقط در هنگام استفاده توجه داشته باشید به ارجاع‌های برنامه و اینکه باید System.Management رو به reference‌های برنامه اضافه بکنید حتما ...

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Win32;
using System.Management;

namespace ConsoleApplication1
{

    class Program
    {
        private static string GetSystemCode_int1(byte[] byte_0)
        {
            if (byte_0 != null)
            {
                return Convert.ToBase64String((new MD5CryptoServiceProvider()).ComputeHash(byte_0));
            }
            else
            {
                return string.Empty;
            }
        }

        private static string GetSystemCode_int0(string string_2)
        {
            if (!string.IsNullOrEmpty(string_2))
            {
                return GetSystemCode_int1(Encoding.UTF8.GetBytes(string_2));
            }
            else
            {
                return string.Empty;
            }
        }

        public static string GetSystemCode()
        {
            string key = null;
            if (key == null)
            {
                string empty = string.Empty;
                try
                {
                    ManagementClass managementClass = new ManagementClass("win32_processor");
                    ManagementObjectCollection instances = managementClass.GetInstances();
                    foreach (ManagementBaseObject instance in instances)
                    {
                        try
                        {
                            empty = string.Concat(empty, instance.Properties["processorID"].Value.ToString());
                            break;
                        }
                        catch
                        {
                        }
                    }
                }
                catch
                {
                    try
                    {
                        ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher("Select * From Win32_BaseBoard");
                        foreach (ManagementBaseObject managementBaseObject in managementObjectSearcher.Get())
                        {
                            empty = string.Concat(empty, managementBaseObject["SerialNumber"].ToString().Trim());
                        }
                    }
                    catch
                    {
                    }
                }
                try
                {
                    ManagementObject managementObject = new ManagementObject("win32_logicaldisk.deviceid=\"C:\"");
                    managementObject.Get();
                    empty = string.Concat(empty, managementObject["VolumeSerialNumber"].ToString());
                }
                catch
                {
                }
                if (string.IsNullOrWhiteSpace(empty))
                {
                    empty = Environment.MachineName;
                }
                key = GetSystemCode_int0(empty);
            }
            return key;
        }

        static void Main(string[] args)
        {
            Console.WriteLine(GetSystemCode());
            Console.ReadKey();
        }
    }
}