اشتراک‌ها
کتاب رایگان Support Vector Machines Succinctly

Support Vector Machines (SVMs) are some of the most performant off-the-shelf, supervised machine-learning algorithms. In Support Vector Machines Succinctly, author Alexandre Kowalczyk guides readers through the building blocks of SVMs, from basic concepts to crucial problem-solving algorithms. He also includes numerous code examples and a lengthy bibliography for further study. By the end of the book, SVMs should be an important tool in the reader’s machine-learning toolbox.

Table of Contents
  1. Prerequisites
  2. The Perceptron
  3. The SVM Optimization Problem
  4. Solving the Optimization Problem
  5. Soft Margin SVM
  6. Kernels
  7. The SMO Algorithm
  8. Multi-Class SVMs
  9. Conclusion
  10. Appendix A: Datasets
  11. Appendix B: The SMO Algorithm
کتاب رایگان Support Vector Machines Succinctly
نظرات مطالب
کار با یک مخزن کد GitHub‌ از طریق VSCode
یک نکته‌ی تکمیلی
در  صورتی که با خطای can not execute git برخورد کردید خروجی  Show Git Output  را بررسی نمایید خطای  fatal: unable to auto-detect email address  زمانی اتفاق می‌افتد که git برای commit می‌خواهد ایمیل و یوزرنیم شما را از تنظیمات ویندوز استخراج کند  برای تنظیم دستی به ترمینال vscode بروید و از طریق دستورات زیر تنظیمات لازم را انجام دهید.
git config --global user.email "you@example.com" 
git config --global user.name "Your Name"
مطالب
آشنایی با NHibernate - قسمت ششم

آشنایی با Automapping در فریم ورک Fluent NHibernate

اگر قسمت‌های قبل را دنبال کرده باشید، احتمالا به پروسه طولانی ساخت نگاشت‌ها توجه کرده‌اید. با کمک فریم ورک Fluent NHibernate می‌توان پروسه نگاشت domain model خود را به data model متناظر آن به صورت خودکار نیز انجام داد و قسمت عمده‌ای از کار به این صورت حذف خواهد شد. (این مورد یکی از تفاوت‌های مهم NHibernate با نمونه‌های مشابهی است که مایکروسافت تا تاریخ نگارش این مقاله ارائه داده است. برای مثال در نگار‌ش‌های فعلی LINQ to SQL یا Entity framework ، اول دیتابیس مطرح است و بعد ساخت کد از روی آن، در حالیکه در اینجا ابتدا کد و طراحی سیستم مطرح است و بعد نگاشت آن به سیستم داده‌ای و دیتابیس)

امروز قصد داریم یک سیستم ساده ثبت خبر را از صفر با NHibernate پیاده سازی کنیم و همچنین مروری داشته باشیم بر قسمت‌های قبلی.

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

ابتدا یک پروژه کنسول جدید را به نام NHSample2 آغاز کنید. سپس ارجاعاتی را به اسمبلی‌های زیر به آن اضافه نمائید:
FluentNHibernate.dll
NHibernate.dll
NHibernate.ByteCode.Castle.dll
NHibernate.Linq.dll
و ارجاعی به اسمبلی استاندارد System.Data.Services.dll دات نت فریم ورک سه و نیم

سپس پوشه‌ای را به نام Domain به این پروژه اضافه نمائید (کلیک راست روی نام پروژه در VS.Net و سپس مراجعه به منوی Add->New folder). در این پوشه تعاریف موجودیت‌های برنامه را قرار خواهیم داد. سه کلاس جدید Category ، User و News را در این پوشه ایجاد نمائید. محتویات این سه کلاس به شرح زیر هستند:

namespace NHSample2.Domain
{
public class User
{
public virtual int Id { get; set; }
public virtual string UserName { get; set; }
public virtual string Password { get; set; }
}
}


namespace NHSample2.Domain
{
public class Category
{
public virtual int Id { get; set; }
public virtual string CategoryName { get; set; }
}
}


using System;

namespace NHSample2.Domain
{
public class News
{
public virtual Guid Id { get; set; }
public virtual string Subject { get; set; }
public virtual string NewsText { get; set; }
public virtual DateTime DateEntered { get; set; }
public virtual Category Category { get; set; }
public virtual User User { get; set; }
}
}
همانطور که در قسمت‌های قبل نیز ذکر شد، تمام خواص پابلیک کلاس‌های Domain ما به صورت virtual تعریف شده‌اند تا lazy loading را در NHibernate فعال سازیم. در حالت lazy loading ، اطلاعات تنها زمانیکه به آن‌ها نیاز باشد بارگذاری خواهند شد. این مورد در حالتیکه نیاز به نمایش اطلاعات تنها یک شیء وجود داشته باشد بسیار مطلوب می‌باشد، یا هنگام ثبت و به روز رسانی اطلاعات نیز یکی از بهترین روش‌ها است. اما زمانیکه با لیستی از اطلاعات سروکار داشته باشیم باعث کاهش افت کارآیی خواهد شد زیرا برای مثال نمایش آن‌ها سبب خواهد شد که 100 ها کوئری دیگر جهت دریافت اطلاعات هر رکورد در حال نمایش اجرا شود (مفهوم دسترسی به اطلاعات تنها در صورت نیاز به آن‌ها). Lazy loading و eager loading (همانند مثال‌های قبلی) هر دو در NHibernate به سادگی قابل تنظیم هستند (برای مثال LINQ to SQL به صورت پیش فرض همواره lazy load است و تا این تاریخ راه استانداردی برای امکان تغییر و تنظیم این مورد پیش بینی نشده است).

اکنون کلاس جدید Config را به برنامه اضافه نمائید:

using FluentNHibernate.Automapping;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;

namespace NHSample2
{
class Config
{
public static Configuration GenerateMapping(IPersistenceConfigurer dbType)
{
var cfg = dbType.ConfigureProperties(new Configuration());

new AutoPersistenceModel()
.Where(x => x.Namespace.EndsWith("Domain"))
.AddEntityAssembly(typeof(NHSample2.Domain.News).Assembly).Configure(cfg);

return cfg;
}

public static void GenerateDbScript(Configuration config, string filePath)
{
bool script = true;//فقط اسکریپت دیتابیس تولید گردد
bool export = false;//نیازی نیست بر روی دیتابیس هم اجرا شود
new SchemaExport(config).SetOutputFile(filePath).Create(script, export);
}

public static void BuildDbSchema(Configuration config)
{
bool script = false;//آیا خروجی در کنسول هم نمایش داده شود
bool export = true;//آیا بر روی دیتابیس هم اجرا شود
bool drop = false;//آیا اطلاعات موجود دراپ شوند
new SchemaExport(config).Execute(script, export, drop);
}

public static void CreateSQL2008DbPlusScript(string connectionString, string filePath)
{
Configuration cfg =
GenerateMapping(
MsSqlConfiguration
.MsSql2008
.ConnectionString(connectionString)
.ShowSql()
);
GenerateDbScript(cfg, filePath);
BuildDbSchema(cfg);
}

public static ISessionFactory CreateSessionFactory(IPersistenceConfigurer dbType)
{
return
Fluently.Configure().Database(dbType)
.Mappings(m => m.AutoMappings
.Add(
new AutoPersistenceModel()
.Where(x => x.Namespace.EndsWith("Domain"))
.AddEntityAssembly(typeof(NHSample2.Domain.News).Assembly))
)
.BuildSessionFactory();
}
}
}

در متد GenerateMapping از قابلیت Automapping موجود در فریم ورک Fluent Nhibernate استفاده شده است (بدون نوشتن حتی یک سطر جهت تعریف این نگاشت‌ها). این متد نوع دیتابیس مورد نظر را جهت ساخت تنظیمات خود دریافت می‌کند. سپس با کمک کلاس AutoPersistenceModel این فریم ورک، به صورت خودکار از اسمبلی برنامه نگاشت‌های لازم را به کلاس‌های موجود در پوشه Domain ما اضافه می‌کند (مرسوم است که این پوشه در یک پروژه Class library مجزا تعریف شود که در این برنامه جهت سهولت کار در خود برنامه قرار گرفته است). قسمت Where ذکر شده به این جهت معرفی گردیده است تا Fluent Nhibernate برای تمامی کلاس‌های موجود در اسمبلی جاری، سعی در تعریف نگاشت‌های لازم نکند. این نگاشت‌ها تنها به کلاس‌های موجود در پوشه دومین ما محدود شده‌اند.
سه متد بعدی آن، جهت ایجاد اسکریپت دیتابیس از روی این نگاشت‌های تعریف شده و سپس اجرای این اسکریپت بر روی دیتابیس جاری معرفی شده، تهیه شده‌اند. برای مثال CreateSQL2008DbPlusScript یک مثال ساده از استفاده دو متد قبلی جهت ایجاد اسکریپت و دیتابیس متناظر اس کیوال سرور 2008 بر اساس نگاشت‌های برنامه است.
با متد CreateSessionFactory در قسمت‌های قبل آشنا شده‌اید. تنها تفاوت آن در این قسمت، استفاده از کلاس AutoPersistenceModel جهت تولید خودکار نگاشت‌ها است.

در ادامه دیتابیس متناظر با موجودیت‌های برنامه را ایجاد خواهیم کرد:

using System;

namespace NHSample2
{
class Program
{
static void Main(string[] args)
{
Config.CreateSQL2008DbPlusScript(
"Data Source=(local);Initial Catalog=HelloNHibernate;Integrated Security = true",
"db.sql");

Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}

پس از اجرای برنامه، ابتدا فایل اسکریپت دیتابیس به نام db.sql در پوشه اجرایی برنامه تشکیل خواهد شد و سپس این اسکریپت به صورت خودکار بر روی دیتابیس معرفی شده اجرا می‌گردد. دیتابیس دیاگرام حاصل را در شکل زیر می‌توانید ملاحظه نمائید:



همچنین اسکریپت تولید شده آن، صرفنظر از عبارات drop اولیه، به صورت زیر است:

create table [Category] (
Id INT IDENTITY NOT NULL,
CategoryName NVARCHAR(255) null,
primary key (Id)
)

create table [User] (
Id INT IDENTITY NOT NULL,
UserName NVARCHAR(255) null,
Password NVARCHAR(255) null,
primary key (Id)
)

create table [News] (
Id UNIQUEIDENTIFIER not null,
Subject NVARCHAR(255) null,
NewsText NVARCHAR(255) null,
DateEntered DATETIME null,
Category_id INT null,
User_id INT null,
primary key (Id)
)

alter table [News]
add constraint FKE660F9E1C9CF79
foreign key (Category_id)
references [Category]

alter table [News]
add constraint FKE660F95C1A3C92
foreign key (User_id)

references [User]

اکنون یک سری گروه خبری، کاربر و خبر را به دیتابیس خواهیم افزود:

using System;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHSample2.Domain;

namespace NHSample2
{
class Program
{
static void Main(string[] args)
{
using (ISessionFactory sessionFactory = Config.CreateSessionFactory(
MsSqlConfiguration
.MsSql2008
.ConnectionString("Data Source=(local);Initial Catalog=HelloNHibernate;Integrated Security = true")
.ShowSql()
))
{
using (ISession session = sessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
//با توجه به کلیدهای خارجی تعریف شده ابتدا باید گروه‌ها را اضافه کرد
Category ca = new Category() { CategoryName = "Sport" };
session.Save(ca);
Category ca2 = new Category() { CategoryName = "IT" };
session.Save(ca2);
Category ca3 = new Category() { CategoryName = "Business" };
session.Save(ca3);

//سپس یک کاربر را به دیتابیس اضافه می‌کنیم
User u = new User() { Password = "123$5@1", UserName = "VahidNasiri" };
session.Save(u);

//اکنون می‌توان یک خبر جدید را ثبت کرد

News news = new News()
{
Category = ca,
User = u,
DateEntered = DateTime.Now,
Id = Guid.NewGuid(),
NewsText = "متن خبر جدید",
Subject = "عنوانی دلخواه"
};
session.Save(news);

transaction.Commit(); //پایان تراکنش
}
}
}

Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}
جهت بررسی انجام عملیات ثبت هم می‌توان به دیتابیس مراجعه کرد، برای مثال:



و یا می‌توان از LINQ استفاده کرد:
برای مثال کاربر VahidNasiri تعریف شده را یافته، اطلاعات آن‌را نمایش دهید؛ سپس نام او را به Vahid ویرایش کرده و دیتابیس را به روز کنید.

برای اینکه کوئری‌های LINQ ما شبیه به LINQ to SQL شوند، کلاس NewsContext را به صورت ذیل تشکیل می‌دهیم. این کلاس از کلاس پایه NHibernateContext مشتق شده و سپس به ازای تمام موجودیت‌های برنامه، یک متد از نوع IOrderedQueryable را تشکیل خواهیم داد.

using System.Linq;
using NHibernate;
using NHibernate.Linq;
using NHSample2.Domain;

namespace NHSample2
{
class NewsContext : NHibernateContext
{
public NewsContext(ISession session)
: base(session)
{ }

public IOrderedQueryable<News> News
{
get { return Session.Linq<News>(); }
}

public IOrderedQueryable<Category> Categories
{
get { return Session.Linq<Category>(); }
}

public IOrderedQueryable<User> Users
{
get { return Session.Linq<User>(); }
}
}
}
اکنون جهت یافتن کاربر و به روز رسانی اطلاعات او در دیتابیس خواهیم داشت:

using System;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using System.Linq;
using NHSample2.Domain;

namespace NHSample2
{
class Program
{
static void Main(string[] args)
{
using (ISessionFactory sessionFactory = Config.CreateSessionFactory(
MsSqlConfiguration
.MsSql2008
.ConnectionString("Data Source=(local);Initial Catalog=HelloNHibernate;Integrated Security = true")
.ShowSql()
))
{
using (ISession session = sessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
using (NewsContext db = new NewsContext(session))
{
var query = from x in db.Users
where x.UserName == "VahidNasiri"
select x;

//اگر چیزی یافت شد
if (query.Any())
{
User vahid = query.First();
//نمایش اطلاعات کاربر
Console.WriteLine("Id: {0}, UserName: {0}", vahid.Id, vahid.UserName);
//به روز رسانی نام کاربر
vahid.UserName = "Vahid";
session.Update(vahid);

transaction.Commit(); //پایان تراکنش
}
}
}
}
}

Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}
مباحث تکمیلی AutoMapping

اگر به اسکریپت دیتابیس تولید شده دقت کرده باشید، عملیات AutoMapping یک سری پیش فرض‌هایی را اعمال کرده است. برای مثال فیلد Id را از نوع identity و به صورت کلید تعریف کرده، یا رشته‌ها را به صورت nvarchar با طول 255 ایجاد نموده است. امکان سفارشی سازی این موارد نیز وجود دارد.

مثال:

using FluentNHibernate.Conventions.Helpers;

public static Configuration GenerateMapping(IPersistenceConfigurer dbType)
{
var cfg = dbType.ConfigureProperties(new Configuration());

new AutoPersistenceModel()
.Conventions.Add()
.Where(x => x.Namespace.EndsWith("Domain"))
.Conventions.Add(
PrimaryKey.Name.Is(x => "ID"),
DefaultLazy.Always(),
ForeignKey.EndsWith("ID"),
Table.Is(t => "tbl" + t.EntityType.Name)
)
.AddEntityAssembly(typeof(NHSample2.Domain.News).Assembly)
.Configure(cfg);

return cfg;
}

تابع GenerateMapping معرفی شده را اینجا با قسمت Conventions.Add تکمیل کرده‌ایم. به این صورت دقیقا مشخص شده است که فیلدهایی با نام ID باید primary key در نظر گرفته شوند، همواره lazy loading صورت گیرد و نام کلید خارجی به ID ختم شود. همچنین نام جداول با tbl شروع گردد.
روش دیگری نیز برای معرفی این قرار دادها و پیش فرض‌ها وجود دارد. فرض کنید می‌خواهیم طول رشته پیش فرض را از 255 به 500 تغییر دهیم. برای اینکار باید اینترفیس IPropertyConvention را پیاده سازی کرد:

using FluentNHibernate.Conventions;
using FluentNHibernate.Conventions.Instances;

namespace NHSample2.Conventions
{
class MyStringLengthConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
instance.Length(500);
}
}
}
سپس نحوه‌ی معرفی آن به صورت زیر خواهد بود:

public static Configuration GenerateMapping(IPersistenceConfigurer dbType)
{
var cfg = dbType.ConfigureProperties(new Configuration());

new AutoPersistenceModel()
.Conventions.Add()
.Where(x => x.Namespace.EndsWith("Domain"))
.Conventions.Add<MyStringLengthConvention>()
.AddEntityAssembly(typeof(NHSample2.Domain.News).Assembly)
.Configure(cfg);

return cfg;
}

نکته:
اگر برای یافتن اطلاعات بیشتر در این مورد در وب جستجو کنید، اکثر مثال‌هایی را که مشاهده خواهید کرد بر اساس نگارش بتای fluent NHibernate هستند و هیچکدام با نگارش نهایی این فریم ورک کار نمی‌کنند. در نگارش رسمی نهایی ارائه شده، تغییرات بسیاری صورت گرفته که آن‌ها را در این آدرس می‌توان مشاهده کرد.

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


ادامه دارد ...

اشتراک‌ها
Authentication با JWT در AngularJs

In this tutorial I'm going to take you through building a simple authentication mechanism for AngularJS apps using JWTs (JSON web tokens) combined with local storage. 

Authentication با JWT در AngularJs
مطالب
Globalization در ASP.NET MVC - قسمت سوم
قبل از ادامه، بهتر است یک مقدمه کوتاه درباره انواع منابع موجود در ASP.NET ارائه شود تا درک مطالب بعدی آسانتر شود.

نکات اولیه
- یک فایل Resource درواقع یک فایل XML شامل رشته هایی برای ذخیره سازی مقادیر (منابع) موردنیاز است. مثلا رشته هایی برای ترجمه به زبانهای دیگر، یا مسیرهایی برای یافتن تصاویر یا فایلها و ... . پسوند این فایلها resx. است (مثل MyResource.resx).
- این فایلها برای ذخیره منابع از جفت داده‌های کلید-مقدار (key-value pair) استفاده می‌کنند. هر کلید معرف یک ورودی مجزاست. نام این کلیدها حساس به حروف بزرگ و کوچک نیست (Not Case-Sensitive).
- برای هر زبان (مثل fa برای فارسی) یا کالچر موردنظر (مثل fa-IR برای فارسی ایرانی) می‌توان یک فایل Resource جداگانه تولید کرد. عناون زبان یا کالچر باید جزئی از نام فایل Resource مربوطه باشد (مثل MyResource.fa.resx یا MyResource.fa-IR.resx). هر منبع باید دارای یک فایل اصلی (پیش‌فرض) Resource باشد. این فایل، فایلی است که برای حالت پیش‌فرض برنامه (بدون کالچر) تهیه شده است و در عنوان آن از نام زیان یا کالچری استفاده نشده است (مثل MyResource.resx). برای اطلاعات بیشتر به قسمت اول این سری مراجعه کنید.
- تمامی فایل‌های Resource باید دارای کلیدهای یکسان با فایل اصلی Resource باشند. البته لزومی ندارد که این فایل‌ها حاوی تمامی کلیدهای منبع پیش‌فرض باشند. درصورت عدم وجود کلیدی در یک فایل Resource عملیات پیش فرض موجود در دات نت با استفاده از فرایند مشهور به fallback مقدار کلید موردنظر را از نزدیکترین و مناسبترین فایل موجود انتخاب می‌کند (درباره این رفتار در قسمت اول توضیحاتی ارائه شده است).
- در زمان اجرا موتور پیش فرض مدیریت منابع دات نت با توجه به کالچر UI در ثرد جاری اقدام به انتخاب مقدار مناسب برای کلیدهای درخواستی (به همراه فرایند fallback) می‌کند. فرایند نسبتا پیچیده fallback در اینجا شرح داده شده است.

منابع Global و Local
در ASP.NET دو نوع کلی Resource وجود دارد که هر کدام برای موقعیت‌های خاصی مورد استفاده قرار می‌گیرند:

- Resourceهای Global: منابعی کلی هستند که در تمام برنامه در دسترسند. این فایل‌ها در مسیر رزرو شده APP_GlobalResources در ریشه سایت قرار می‌گیرند. محتوای هر فایل resx. موجود در این فولدر دارای دسترسی کلی خواهد بود.

- Resourceهای Local: این منابع همان‌طور که از نامشان پیداست محلی! هستند و درواقع مخصوص همان مسیری هستند که در آن تعبیه شده اند! در استفاده از منابع محلی به ازای هر صفحه وب (aspx. یا master.) یا هر یوزرکنترل (ascx.) یک فایل resx. تولید می‌شود که تنها در همان صفحه یا یوزرکنترل در دسترس است. این فایل‌ها درون فولدر رزرو شده APP_LocalResources در مسیرهای موردنظر قرار می‌گیرند. درواقع در هر مسیری که نیاز به این نوع از منابع باشد، باید فولدری با عنوان App_LocalResources ایجاد شود و فایلهای resx. مرتبط با صفحه‌ها یا یوزرکنترل‌های آن مسیر در این فولدر مخصوص قرار گیرد.
در تصویر زیر چگونگی افزودن این فولدرهای مخصوص به پروژه وب اپلیکیشن نشان داده شده است:

نکته: دقت کنید که تنها یک فولدر App_GlobalResources به هر پروزه می‌توان افزود. همچنین در ریشه هر مسیر موجود در پروژه تنها می‌توان یک فولدر Appp_LocalResources داشت. پس از افزودن هر یک از این فولدرهای مخصوص، منوی فوق به صورت زیر در خواهد آمد:

نکته: البته با تغییر نام یک فولدر معمولی به این نام‌های رزرو شده نتیجه یکسانی بدست خواهد آمد.
 
نکته: در زمان اجرا، عملیات استخراج داده‌های موجود در این نوع منابع، به صورت خودکار توسط ASP.NET انجام می‌شود. این داده‌ها پس از استخراج در حافظه سرور کش خواهند شد.

برای روشن‌تر شدن مطالب اشاره شده در بالا به تصویر فرضی! زیر توجه کنید (اسمبلی‌های تولید شده برای منابع کلی و محلی فرضی است):

در تصویر بالا محل قرارگیری انواع مختلف فایلهای Resource و نیز محل نهایی فرضی اسمبلی‌های ستلایت تولید شده، برای حداقل یک زبان غیر از زبان پیش فرض برنامه، نشان داده شده است.

نکته: نحوه برخورد با این نوع از فایل‌های Resource در پروژه‌های Web Site و Web Application کمی باهم فرق می‌کند. موارد اشاره شده در این مطلب بیشتر درباره Web Applicationها صدق می‌کند.

برای آشنایی بیشتر بهتر است یک برنامه وب اپلیکیشن جدید ایجاد کرده و همانند تصویر زیر یکسری فایل Resource به فولدرهای اشاره شده در بالا اضافه کنید:

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

می‌بینید که خاصیت Build Action آن به Content مقداردهی شده است. این مقدار موجب می‌شود تا این فایل به همین صورت و در همین مسیر مستقیما در پابلیش نهایی برنامه ظاهر شود. در قسمت قبل به خاصیت Buil Action و مقادیر مختلف آن اشاره شده است.
هم‌چنین می‌بینید که مقدار پراپرتی Custom Tool به GlobalResourceProxyGenerator تنظیم شده است. این ابزار مخصوص تولید کلاس مربوط به منابع کلی در ویژوال استودیو است. با استفاده از این ابزار فایل Resource1.Designer.cs که در تصویر قبلی نیز نشان داده شده، تولید می‌شود.
حالا پنجره پراپرتی‌های منبع محلی را باز کنید:

می‌بینید که همانند منبع کلی خاصیت Build Action آن به Content تنظیم شده است. همچنین مقداری برای پراپرتی Custom Tool تنظیم نشده است. این مقدار پیش فرض را تغییر ندهید، چون با تنظیم مقداری برای آن چیز مفیدی عایدتان نمی‌شود! 

نکته: برای به روز رسانی مقادیر کلیدهای منابعی که با توجه به توضیحات بالا به همراه برنامه به صورت فایلهای resx. پابلیش می‌شوند، کافی است تا محتوای فایلهای resx. مربوطه با استفاده از یک ابزار (همانند نمونه ای که در قسمت قبل شرح داده شد) تغییر داده شوند. بقیه عملیات توسط ASP.NET انجام خواهد شد. اما با تغییر محتوای این فایلهای resx. با توجه به رفتار FCN در ASP.NET (که در قسمت قبل نیز توضیح داده شد) سایت Restart خواهد شد. البته این روش تنها برای منابع کلی و محلی درون مسیرهای مخصوص اشاره شده کار خواهد کرد.

استفاده از منابع Local و Global
پس از تولید فایل‌های Resource، می‌توان از آن‌ها در صفحات وب استفاده کرد. معمولا از این نوع منابع برای مقداردهی پراپرتی کنترل‌ها در صفحات وب استفاده می‌شود. برای استفاده از کلیدهای منابع محلی می‌توان از روشی همانند زیر بهره برد:
<asp:Label ID="lblLocal" runat="server" meta:resourcekey="lblLocalResources" ></asp:Label> 
اما برای منابع کلی تنها می‌توان از روش زیر استفاده کرد (یعنی برای منابع محلی نیز می‌توان از این روش استفاده کرد):
<asp:Label ID="lblGlobal" runat="server" Text="<%$ Resources:CommonTerms, HelloText %>" ></asp:Label> 
به این عبارات که با فوت پررنگ مشخص شده اند اصطلاحا «عبارات بومی‌سازی» (Localization Expression) می‌گویند. در ادامه این سری مطالب با نحوه تعریف نمونه‌های سفارشی آن آشنا خواهیم شد.
به نمونه اول که برای منابع محلی استفاده می‌شود نوع ضمنی (Implicit Localization Expression) می‌گویند. زیرا نیازی نیست تا محل کلید موردنظر صراحتا ذکر شود!
به نمونه دوم که برای منابع کلی استفاه می‌شود نوع صریح (Explicit Localization Expression) می‌گویند. زیرا برای یافتن کلید موردنظر باید آدرس دقیق آن ذکر شود!

بومی سازی ضمنی (Implicit Localization) با منابع محلی
عنوان کلید مربوطه در این نوع عبارات همانطور که در بالا نشان داده شده است، با استفاده از پراپرتی مخصوص meta:resoursekey مشخص می‌شود. در استفاده از منابع محلی تنها یک نام برای کل خواص کنترل مربوطه در صفحات وب کفایت می‌کند. زیرا عنوان کلیدهای این منبع باید از طرح زیر پیروی کند:
ResourceKey.Property
ResourceKey.Property.SubProperty    یا    ResourceKey.Property-SubProperty
برای مثال در لیبل بالا که نام کلید Resource آن به lblLocalResources تنظیم شده است، اگر نام صفحه وب مربوطه page1.aspx باشد، برای تنظیم خواص آن در فایل page1.aspx.resx مربوطه باید از کلیدهایی با عناوینی مثل عنوان‌های زیر استفاه کرد:
lblLocalResources.Text
lblLocalResources.BackColor
برای نمونه به تصاویر زیر دقت کنید:


بومی سازی صریح (Explicit Localization)
در استفاده از این نوع عبارات، پراپرتی مربوطه و نام فایل منبع صراحتا در تگ کنترل مربوطه آورده می‌شود. بنابراین برای هر خاصیتی که می‌خواهیم مقدار آن از منبعی خاص گرفته شود باید از عبارتی با طرح زیر استفاده کنیم:
<%$ Resources: Class, ResourceKey %>
در این عبارت، رشته Resources پیشوند (Prefix) نام دارد و مشخص کننده استفاده از نوع صریح عبارات بومی سازی است. Class نام کلاس مربوط به فایل منبع بوده و اختیاری است که تنها برای منابع کلی باید آورده شود. ResourceKey نیز کلید مربوطه را در فایل منبع مشخص می‌کند.
برای نمونه به تصاویر زیر دقت کنید:


نکته: استفاده همزمان از این دو نوع عبارت بومی سازی در یک کنترل مجاز نیست!

نکته: به دلیل تولید کلاسی مخصوص منابع کلی (با توجه به توضیحات ابتدای این مطلب راجع به پراپرتی Custom Tool)، امکان استفاده مستقیم از آن درون کد نیز وجود دارد. این کلاسها که به صورت خودکار تولید می‌شوند، به صورت مستقیم از کلاس ResourceManager برای یافتن کلیدهای منابع استفاده می‌کنند. اما روش مستقیمی برای استفاده از کلیدهای منابع محلی درون کد وجود ندارد. 

نکته: درون کلاس System.Web.UI.TemplateControl و نیز کلاس HttpContext دو متد با نامهای GetGlobalResourceObject و GetLocalResourceObject وجود دارد که برای یافتن کلیدهای منابع به صورت غیرمستقیم استفاده می‌شوند. مقدار برگشتی این دو متد از نوع object است. این دو متد به صورت مستقیم از کلاس ResourceManager استفاده نمیکنند! هم‌چنین ازآنجاکه کلاس Page از کلاس TemplateControl مشتق شده است، بنابراین این دو متد در صفحات وب در دسترس هستند.

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

کلاس TemplateControl
این دو متد در کلاس TemplateControl از نوع Instance (غیر استاتیک) هستند. امضای (Signature) این دو متد در این کلاس به صورت زیر است:

متد GetLocalResourceObject:
protected object GetLocalResourceObject(string resourceKey)
protected object GetLocalResourceObject(string resourceKey, Type objType, string propName)
در متد اول، پارامتر resourceKey در متد GetLocalResourceObject معرف کلید منبع مربوطه در فایل منبع محلی متناظر با صفحه جاری است. مثلا lblLocalResources.Text. ازآنجاکه به صورت پیش‌فرض موقعیت فایل منبع محلی مرتبط با صفحات وب مشخص است بنابراین تنها ارائه کلید مربوطه برای یافتن مقدار آن کافی است. مثال:
txtTest.Text = GetLocalResourceObject("txtTest.Text") as string;
متد دوم برای استخراج کلیدهای منبع محلی با مشخص کردن نوع داده محتوا (معمولا برای داده‌های غیر رشته‌ای) و پراپرتی موردنظر به کار می‌رود. در این متد پارامتر objType برای معرفی نوع داده متناظر با داده موجود در کلید resourceKey استفاده می‌شود. از پارامتر propName نیز همانطور که از نامش پیداست برای مشخص کردن پراپرتی موردنظر از این نوع داده معرفی شده استفاده می‌شود.

متد GetGlobalResourceObject:
protected object GetGlobalResourceObject(string className, string resourceKey)
protected object GetGlobalResourceObject(string className, string resourceKey, Type objType, string propName)
در این دو متد، پارامتر className مشخص کننده نام کلاس متناظر با فایل منبع اصلی (فایل منبع اصلی که کلاس مربوطه با نام آن ساخته می‌شود) است. سایر پارامترها همانند دو متد قبلی است. مثال:
TextBox1.Text = GetGlobalResourceObject("Resource1", "String1") as string;

کلاس HttpContext
در این کلاس دو متد موردبحث از نوع استاتیک و به صورت زیر تعریف شده‌اند:

متد GetLocalResourceObject: 
public static object GetLocalResourceObject(string virtualPath, string resourceKey)
public static object GetLocalResourceObject(string virtualPath, string resourceKey, CultureInfo culture)
در این دو متد، پارامتر virtualPath مشخص کننده مسیر نسبی صفحه وب متناظر با فایل منبع محلی موردنظر است، مثل "Default.aspx/~". پارامتر resourceKey نیز کلید منبع را تعیین می‌کند و پارامتر culture نیز به کالچر موردنظر اشاره دارد. مثال:
txtTest.Text = HttpContext.GetLocalResourceObject("~/Default.aspx", "txtTest.Text") as string;
 
متد GetGlobalResourceObject:
public static object GetGlobalResourceObject(string classKey, string resourceKey)
public static object GetGlobalResourceObject(string classKey, string resourceKey, CultureInfo culture)
در این دو متد، پارامتر className مشخص کننده نام کلاس متناظر با فایل منبع اصلی (فایل منبع بدون نام زبان که کلاس مربوطه با نام آن ساخته می‌شود) است. سایر پارامترها همانند دو متد قبلی است. مثال:
TextBox1.Text = HttpContext.GetGlobalResourceObject("Resource1", "String1") as string;
 
نکته: بدیهی است که در MVC تنها می‌توان از متدهای کلاس HttpContext استفاده کرد.

روش دیگری که تنها برای منابع کلی در دسترس است، استفاده مستقیم از کلاسی است که به صورت خودکار توسط ابزارهای Visual Studio برای فایل منبع اصلی تولید می‌شود. نمونه‌ای از این کلاس را که برای یک فایل Resource1.resx (که تنها یک ورودی با نام String1 دارد) در پوشه App_GlobalResources تولید شده است، در زیر مشاهده می‌کنید:
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.17626
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Resources {
    using System;
    
    /// <summary>
    ///   A strongly-typed resource class, for looking up localized strings, etc.
    /// </summary>
    // This class was auto-generated by the StronglyTypedResourceBuilder
    // class via a tool like ResGen or Visual Studio.
    // To add or remove a member, edit your .ResX file then rerun ResGen
    // with the /str option or rebuild the Visual Studio project.
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Web.Application.StronglyTypedResourceProxyBuilder", "10.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    internal class Resource1 {
        
        private static global::System.Resources.ResourceManager resourceMan;
        
        private static global::System.Globalization.CultureInfo resourceCulture;
        
        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal Resource1() {
        }
        
        /// <summary>
        ///   Returns the cached ResourceManager instance used by this class.
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Resources.ResourceManager ResourceManager {
            get {
                if (object.ReferenceEquals(resourceMan, null)) {
                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Resources.Resource1", global::System.Reflection.Assembly.Load("App_GlobalResources"));
                    resourceMan = temp;
                }
                return resourceMan;
            }
        }
        
        /// <summary>
        ///   Overrides the current thread's CurrentUICulture property for all
        ///   resource lookups using this strongly typed resource class.
        /// </summary>
        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
        internal static global::System.Globalization.CultureInfo Culture {
            get {
                return resourceCulture;
            }
            set {
                resourceCulture = value;
            }
        }
        
        /// <summary>
        ///   Looks up a localized string similar to String1.
        /// </summary>
        internal static string String1 {
            get {
                return ResourceManager.GetString("String1", resourceCulture);
            }
        }
    }
}

نکته: فضای نام پیش‌فرض برای منابع کلی در این کلاس‌ها همیشه Resources است که برابر پیشوند (Prefix) عبارت بومی سازی صریح است.

نکته: در کلاس بالا نحوه نمونه سازی کلاس ResourceManager نشان داده شده است. همانطور که مشاهده می‌کنید تعیین کردن مشخصات فایل اصلی Resource مربوطه که در اسمبلی نهایی تولید و کش می‌شود، اجباری است! در مطلب بعدی با این کلاس بیشتر آشنا خواهیم شد.

نکته: همانطور که قبلا نیز اشاره شد، کار تولید اسمبلی مربوط به فایل‌های منابع کلی و محلی و کش کردن آن‌ها در اسمبلی در زمان اجرا کاملا بر عهده ASP.NET است. مثلا در نمونه کد بالا می‌بینید که کلاس ResourceManager برای استخراج نوع Resources.Resource1 از اسمبلی App_GlobalResources نمونه‌سازی شده است، با اینکه این اسمبلی و نوع مذبور در زمان کامپایل و پابلیش وجود ندارد!

برای استفاده از این کلاس می‌توان به صورت زیر عمل کرد:
TextBox1.Text = Resources.Resource1.String1;

نکته: همانطور که قبلا هم اشاره شد، متاسفانه روش بالا (برخلاف دو متدی که در قسمت قبل توضیح داده شد) به صورت مستقیم از کلاس ResourceManager استفاده می‌کند، که برای بحث سفارشی سازی پرووایدرهای منابع مشکل‌زاست. در مطالب بعدی با معایب آن و نیز راه حل‌های موجود آشنا خواهیم شد.

نکات نهایی
حال که با مفاهیم کلی بیشتری آشنا شدیم بهتر است کمی هم به نکات ریزتر بپردازیم:

نکته: فایل تولیدی توسط ویژوال استودیو در فرایند مدیریت منابع ASP.NET تاثیرگذار نیست! باز هم تاکید می‌کنم که کار استخراج کلیدهای Resource از درون فایلهای resx. کاملا به صورت جداگانه و خودکار و در زمان اجرا انجام می‌شود (درباره این فرایند در مطالب بعدی شرح مفصلی خواهد آمد). درواقع شما می‌توانید خاصیت Custom Tool مربوط به منابع کلی را نیز همانند منابع محلی به رشته‌ای خالی مقداردهی کنید و ببینید که خللی در فرایند مربوطه رخ نخواهد داد!

نکته: تنها برای حالتی که بخواهید از روش آخری که در بالا اشاره شد برای دسترسی با برنامه‌نویسی به منابع کلی بهره ببرید (روش مستقیم)، به این کلاس تولیدی توسط ویژوال استودیو نیاز خواهید داشت. دقت کنید که در این کلاس نیز کار اصلی برعهده کلاس ResourceManager است. درواقع می‌توان کلا از این فایل خودکارتولیدشده صرفنظر کرد و کار استخراج کلیدهای منابع را به صورت مستقیم به نمونه‌ای از کلاس ResourceManager سپرد. این روش نیز در قسمت‌های بعدی شرح داده خواهد شد.

نکته: اگر فایل‌های Resource درون اسمبلی‌های جداگانه‌ای باشند (مثلا در یک پروژه جداگانه، همانطور که در قسمت اول این سری مطالب پیشنهاد شده است)، موتور پیش فرض منابع در ASP.NET بدرد نخواهد خورد! بنابراین یا باید از نمونه‌های اختصاصی کلاس ResourceManager استفاده کرد (کاری که کلاس‌های خودکار تولیدشده توسط ابزارهای ویژوال استودیو انجام می‌دهند)، یا باید از پرووایدرهای سفارشی استفاده کرد که در مطالب بعدی نحوه تولید آن‌ها شرح داده خواهد شد.
 
همانطور که در ابتدای این مطلب اشاره شد، این مقدمه در اینجا صرفا برای آشنایی بیشتر با این دونوع Resource آورده شده تا ادامه مطلب روشن‌تر باشد، زیرا با توجه به مطالب ارائه شده در قسمت اول این سری، در پروژه‌های MVC استفاده از یک پروژه جداگانه برای نگهداری این منابع راه حل مناسبتری است.
در مطلب بعدی به شرح نحوه تولید پرووایدرهای سفارشی می‌پردازم.
مطالب
آشنایی با NHibernate - قسمت نهم

استفاده از Log4Net جهت ثبت خروجی‌های SQL حاصل از NHibernate

هنگام استفاده از NHibernate، پس از افزودن ارجاعات لازم به اسمبلی‌های مورد نیاز آن به برنامه، یکی از اسمبلی‌هایی که به پوشه build برنامه به صورت خودکار کپی می‌شود، فایل log4net.dll است (حتی اگر ارجاعی را به آن اضافه نکرده باشیم) که جهت ثبت وقایع مرتبط با NHibernate مورد استفاده قرار می‌گیرد. خوب اگر مجبوریم که این وابستگی کتابخانه NHibernate را نیز در پروژه‌های خود داشته باشیم، چرا از آن استفاده نکنیم؟!
شرح مفصل استفاده از این کتابخانه سورس باز را در سایت اصلی آن می‌توان مشاهده کرد:


برای اینکه از این کتابخانه در برنامه خود جهت ثبت عبارات SQL تولیدی توسط NHibernate استفاده کنیم، باید مراحل زیر طی شوند:
الف) ارجاعی را به اسمبلی log4net.dll اضافه نمائید (کنار اسمبلی NHibernate در پوشه build برنامه باید موجود باشد)
ب) فایل app.config برنامه را (برنامه ویندوزی) به صورت زیر ویرایش کرده و چند سطر مربوطه را اضافه نمائید (در مورد برنامه‌های وب هم به همین شکل است. configSections فایل web.config تنظیم شده و سپس تنظیمات log4net را قبل از بسته شدن تگ configuration اضافه نمائید ) :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>

<connectionStrings>
<!--NHSessionManager-->
<add name="DbConnectionString"
connectionString="Data Source=(local);Initial Catalog=HelloNHibernate;Integrated Security = true"/>
</connectionStrings>

<log4net>
<appender name="rollingFile"
type="log4net.Appender.RollingFileAppender,log4net" >
<param name="File" value="NHibernate_Log.txt" />
<param name="AppendToFile" value="true" />
<param name="DatePattern" value="yyyy.MM.dd" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="500KB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout,log4net">
<conversionPattern value="%d %p %m%n" />
</layout>
</appender>
<logger name="NHibernate.SQL">
<level value="ALL" />
<appender-ref ref="rollingFile" />
</logger>
</log4net>

</configuration>
ج) سپس باید فراخوانی زیر نیز در ابتدای کار برنامه صورت گیرد:

log4net.Config.XmlConfigurator.Configure();
در یک برنامه ASP.Net این فراخوانی باید در Application_Start فایل Global.asax.cs صورت گیرد.
یا در یک برنامه از نوع WinForms تنها کافی است سطر زیر را به فایل AssemblyInfo.cs برنامه اضافه کرد:

// Configure log4net using the .config file
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
یا این سطر را به فایل Global.asax.cs یک برنامه ASP.Net نیز می‌توان اضافه کرد. Watch=true آن، با کمک FileSystemWatcher تغییرات فایل کانفیگ را تحت نظر داشته و هر بار که تغییر کند بلافاصله، تغییرات جدید را اعمال خواهد کرد.

د) هنگام استفاده از کتابخانه Fluent NHibernate حتما باید متد ShowSql در جایی که دیتابیس برنامه را تنظیم می‌کنیم (Fluently.Configure().Database) ذکر گردد (که نمونه آن‌را در مثال‌های قسمت‌های قبل ملاحظه‌ کرده‌اید).

توضیحاتی در مورد تنظیمات فوق:
configSections حتما باید در ابتدای فایل app.config‌ ذکر شود و گرنه برنامه کار نخواهد کرد.
سپس کانکشن استرینگ مورد استفاده در قسمت کانفیگ برنامه ذکر شده است.
در ادامه تنظیمات استاندارد مربوط به log4net را مشاهده می‌کنید.
در تنظیمات این کتابخانه، appender مشخص کننده محل ثبت وقایع است. زمانیکه که از RollingFileAppender استفاده کنیم، اطلاعات را در یک سری فایل ذخیره خواهد کرد (امکان ثبت وقایع در EventLog ویندوز، ارسال از طریق ایمیل و غیره نیز میسر است که جهت توضیحات بیشتر می‌توان به مستندات آن رجوع نمود).
سپس نام فایلی که اطلاعات وقایع در آن ثبت خواهند شد ذکر شده است (برای مثال NHibernate_Log.txt)، در ادامه مشخص گردیده که اطلاعات باید هر بار به این فایل Append و اضافه شوند. سطرهای بعدی مشخص می‌کنند که هر زمانیکه این لاگ فایل به 10 مگابایت رسید، یک فایل جدید تولید کن و هر بار 10 فایل آخر را نگه دار و مابقی فایل‌های قدیمی را حذف کن.
در قسمت PatternLayout مشخصات می‌کنیم که خروجی ثبت شده با چه فرمتی باشد. برای مثال یک سطر خروجی مطابق با تنظیمات فوق به شکل زیر خواهد بود:

2009-10-18 20:03:54,187 DEBUG INSERT INTO [Student] (Name) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'Vahid'
در قسمت Logger یک نام دلخواه ذکر شده و میزان اطلاعاتی که باید درج شود، از طریق مقدار level مورد نظر، قابل تنظیم است که می‌تواند یکی از مقادیر ALL ،DEBUG ،INFO ،WARN ،ERROR ،FATAL و یا OFF باشد. اینجا level در نظر گرفته شده ALL است که تمامی اطلاعات مرتبط با اعمال پشت صحنه NHibernate را لاگ خواهد کرد.
توسط appender-ref آن appender ایی را که در ابتدای کار تعریف و تنظیم کردیم، مشخص خواهیم کرد.

اگر هم با برنامه نویسی بخواهیم اطلاعاتی را به این لاگ فایل اضافه کنیم تنها کافی است بنویسیم:

log4net.LogManager.GetLogger("NHibernate.SQL").Info("test1");

اطلاعات بیشتر

ادامه دارد ...