اشتراک‌ها
معرفی و مفاهیم اولیه Cassandra
کاساندرا، فرزند دختر پادشاه شهر تروا بود که علاوه بر داشتن سیمایی زیبا، قدرت پیش‌بینی آینده را با جزئیاتی بسیار دقیق داشت، به‌طوری که به ندرت کسی حرف‌هایش را باور می‌کرد. او نابودی شهر تروا را پیش‌بینی کرده بود اما توان جلوگیری از آن را نداشت. هکتور، شاهزاده‌ای که توسط آشیل در جنگ تروا کشته شد، برادر کاساندرا بود. در سال 2008 دو کارمند سابق آمازون و مایکروسافت که در یک شبکه اجتماعی بزرگ مشغول به کار بودند، یک پایگاه داده جدید  NoSQL را ایجاد کردند که کاساندرا نامیده شد.  ...
معرفی و مفاهیم اولیه Cassandra
نظرات مطالب
تاریخ شمسی با Extension Method برای DateTime
سلام . من وقتی تاریخ رو به فرمت خورشیدی با همین روش ودر global اعمال میکنم ، در kendoui در grid وقتی حالت ajax() رو فعال میکنم این Error رو میگیرم .
Specified time is not supported in this calendar. It should be between 03/21/0622 00:00:00 (Gregorian date) and 12/31/9999 23:59:59 (Gregorian date), inclusive.
Parameter name: time
اما  در حالت server()  مشکلی نداره . اشکال از کجاست و چه جوری میشه حل کرد ؟
نظرات مطالب
تنظیمات امنیتی SMTP Server متعلق به IIS 6.0 جهت قرارگیری بر روی اینترنت
یک نکته‌ی تکمیلی: یکی از دلایل رد نشدن ایمیل‌ها
چند روزی بود ایمیل‌های سایت رد نمی‌شدند. تمام موارد بررسی شدند؛ اما ایمیل‌ها در پوشه‌ی C:\Inetpub\mailroot\Queue باقی مانده و ارسال نمی‌شدند. مشخص شد که (احتمالا به دلیل نصب وصله‌های امنیتی جدید سیستم عامل)، تعداد اتصالات قابل قبول توسط SMTP Server، راسا انتخاب (SMTP Virtual Server > Properties > Delivery > Outbound Connections) و به صفر تنظیم شده بود. با لغو این مورد، مشکل برطرف شد:

مطالب
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 را انتخاب کنید.


نظرات مطالب
چه زمانی بهتر است از بانک‌های اطلاعاتی NoSQL استفاده کرد و چه زمانی خیر؟
با سلام، با توجه به این که Raven Db یکی از دیتابیس‌های No SQL، در پشت صحنه خود از دیتابیس ویندوز با نام ESENT استفاده می‌کند، که مطابق با مستندات سایت‌های معتبر چون MSDN و ویکی پدیا هم Transactional است، و هم دارای امکانات دیگر، آیا می‌توان Transactional بودن را مزیتی صرف برای دیتابیس‌های رابطه ای به شمار آورد، و برای دیتابیس‌های No SQL این مزیت را قائل نشد ؟

Windows comes with an embeddable, transactional database engine 
ESE provides transacted data update and retrieva 
با سپاس
مطالب
مونیتور کردن میزان فضای خالی باقیمانده در سرور توسط اس کیوال سرور

با توجه به در حال اجرا بودن 24 ساعته‌ی سرویس SQL server agent، استفاده‌های ارزنده‌ای از آن می‌توان کرد. برای مثال هر از گاهی بررسی کند که آیا هارد سرور پر شده یا نه؟ و اگر بله (کمبود میزان فضای خالی به حد خطرناکی رسیده)، یک ایمیل خودکار به مسؤول مربوطه ارسال کند.
عمده‌ی مطالبی که در این مقاله بررسی خواهند شد همانند مطلب مونیتور کردن میزان مصرف CPU توسط اس کیوال سرور است و از تکرار آن‌ها در این‌جا صرفنظر خواهد شد (راه اندازی دیتابیس میل و همچنین تعریف یک job جدید که در مورد آن‌ها صحبت شد، همانند قبل است). تنها مطلب جدیدی که به آن اشاره خواهد شد، اسکریپت بررسی میزان فضای خالی و سپس ارسال ایمیل است که در یک job جدید همانند مقاله‌ی قبل باید به سرور اضافه شود. این اسکریپت به شرح زیر است:

DECLARE @DriveBenchmark INT
DECLARE @MachineName NVARCHAR(1000)
DECLARE @DiskFreeSpace INT
DECLARE @DriveLetter CHAR(1)
DECLARE @AlertMessage NVARCHAR(MAX)
DECLARE @MailSubject NVARCHAR(MAX)
DECLARE @NewLine CHAR(2)

SET @NewLine = CHAR(13) + CHAR(10)

SET @DriveBenchmark = 2048 -- 2GB
SET @MailSubject = 'Free space is low on ' + @@SERVERNAME
SET @AlertMessage = ''

IF EXISTS (
SELECT *
FROM tempdb..sysobjects
WHERE id = OBJECT_ID(N'[tempdb]..[#disk_free_space]')
)
DROP TABLE #disk_free_space

CREATE TABLE #disk_free_space
(
DriveLetter CHAR(1) NOT NULL,
FreeMB INTEGER NOT NULL
)

/* Populate #disk_free_space with data */
INSERT INTO #disk_free_space
EXEC MASTER..xp_fixeddrives


DECLARE DriveSpace CURSOR FAST_FORWARD
FOR
SELECT DriveLetter,
FreeMB
FROM #disk_free_space

OPEN DriveSpace
FETCH NEXT FROM DriveSpace INTO @DriveLetter, @DiskFreeSpace

WHILE (@@FETCH_STATUS = 0)
BEGIN
IF @DiskFreeSpace < @DriveBenchmark
BEGIN
SET @AlertMessage = @AlertMessage + 'Drive ' + @DriveLetter + ' on ' + @@SERVERNAME
+ ' has only ' + CAST(@DiskFreeSpace AS VARCHAR) + ' MB left.' + @NewLine
END

FETCH NEXT FROM DriveSpace INTO @DriveLetter, @DiskFreeSpace
END
CLOSE DriveSpace
DEALLOCATE DriveSpace

DROP TABLE #disk_free_space

IF @AlertMessage <> ''
BEGIN
EXECUTE msdb.dbo.sp_send_dbmail
@recipients = 'nasiri@site.net', -- Change This
@copy_recipients = 'Administrator@site.net', -- Change This
@Subject = @MailSubject,
@Body = @AlertMessage
,@importance = 'High'
END

بررسی اسکریپت فوق:

همه چیز از رویه‌ی سیستمی xp_fixeddrives شروع می‌شود. حاصل اجرای این رویه، دریافت میزان فضای خالی هر درایو موجود در سرور خواهد بود. همانطور که در اسکریپت نیز مشخص است، برای ذخیره سازی خروجی این رویه، یک جدول موقتی (disk_free_space) ایجاد شده و خروجی آن به درون این جدول اضافه خواهد شد. سپس یک cursor ایجاد شده و تک تک رکوردهای حاصل با مقدار متغیر DriveBenchmark که در اینجا 2 گیگابایت در نظر گرفته شده است، مقایسه می‌گردند. سپس هر کدام از رکوردها که کمتر از 2 گیگابایت بود، متغیر AlertMessage ما را مقدار دهی خواهد کرد. در پایان اگر این متغیر مقدار دهی شده بود، یعنی مشکل حاصل شده و نتیجه‌ی بررسی به صورت یک ایمیل ارسال می‌گردد. بدیهی است که در صورت نیاز مقدار متغیر DriveBenchmark و آرگومان‌های recipients و copy_recipients کد فوق باید اصلاح شوند.

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


مطالب
بازگردانی پایگاه داده بدون فایل لاگ
در بعضی مواقع ممکن است که در حین کار و با تراکنش‌های باز، دیتابیس SQL Server دچار مشکل شود و از دسترس خارج شود و این فرض را هم در نظر بگیرید که بک آپ دیتابیس مربوط به زمانی بیش از حد انتظار است. آیا ممکن است که دیتابیس را با وجود از دست دادن فایل لاگ آن بازگردانی کرد؟
جواب: بله ولی بدون عواقب نیست.
بطور معمول در زمانی که تراکنش‌های باز بر روی سرور دیتابیس وجود دارد و بانک کرش میکند، کرش ریکاوری، تراکنش‌های باز را رول بک میکند. این امر مانع از اثرات پراکنده از تراکنش‌های فعلی در پایگاه داده می‌باشد.
اگر لاگ در دسترس نباشد هنگامی که کار سرور SQL شروع می‌شود، پایگاه داده در حالت SUSPECT  قرار داده می‌شود. در این مواقع تنها راه آن لاین نمودن بانک ( البته منظور آماده بکار نمودن بانک نیست) استفاده از حالت قابلیت تعمیر اورژانسی است که از نسخه 2005 افزده شده است که با ساخت یک فایل لاگ جدید و سپس اجرا نمودن DBCC CHECKDB با استفاده از REPAIR_ALLOW_DATA_LOSS انجام می‌شود.
مشکل اینجاست که اگر شما به همین طریق بخواهید ادامه بدهید و از این قابلیت اورژانسی استفاده کنید، مسلما امکان وجود دسته‌ای از تغییرات در بانک که ممکن است در میانه اعمال بروز رسانی چند رکورد فروش در یک جدول نیمی از تراکنش‌های آن‌ها روی بانک اعمال شده و بعد از راه اندازی دوباره با لاگ جدید، امکان برگرداندن آن‌ها وجود داشته باشد، مواجه شوید. به این معنی که در بهترین حالت، پایگاه داده بدست آمده با برنامه هماهنگ نیست و یا اینکه به بک آپی که وجود دارد رضایت بدهید.
حالت تعمیر اورژانسی، زمانیکه همه راه‌حل‌های بازگردانی بانک مغلوب شوند آخرین روش مانده است. این حالت، حالتی بین دو شر، ریکاور کردن بانک به حالتی نا هماهنگ با نرم افزار و یا برگرداندن به زمانی خیلی عقب‌تر است که در نهایت اقدام به انجام هر دو و هماهنگ‌تر کردن بانک برای کار با برنامه میشوید که بسیار وقت گیر و مشکل ساز است.
ولی باز ممکن است این وضعیت پیش بیاد چون شما از اتفاقی که در زمان کرش در بانک افتاده اطلاعی ندارید. برای جلوگیری از این موارد در آینده سازوکار بک آپ گیری از بانک را باید تغییر بدهید و دفعات بک آپ گیری را افزایش بدهید و نیز از روش‌های جدید که قابلیت دسترسی بالا دارند استفاده کنید همانند mirroring و SQL Server 2012 Availability Groups است.

 منبع: ^

لینکهای مرتبط:
1. Is the recovery interval guaranteed? ^
2. Scaling and Sharding Deep Freeze ^
3. Why Database Operations Hit the Wall ^
4. Avoiding logging for user operations ^
5. Resolving the Database Performance Problem Blame Game ^
مطالب
آشنایی با NHibernate - قسمت چهارم

در این قسمت یک مثال ساده از insert ، load و delete را بر اساس اطلاعات قسمت‌های قبل با هم مرور خواهیم کرد. برای سادگی کار از یک برنامه Console استفاده خواهد شد (هر چند مرسوم شده است که برای نوشتن آزمایشات از آزمون‌های واحد بجای این نوع پروژه‌ها استفاده شود). همچنین فرض هم بر این است که database schema برنامه را مطابق قسمت قبل در اس کیوال سرور ایجاد کرده اید (نکته آخر بحث قسمت سوم).

یک پروژه جدید از نوع کنسول را به solution برنامه (همان NHSample1 که در قسمت‌های قبل ایجاد شد)، اضافه نمائید.
سپس ارجاعاتی را به اسمبلی‌های زیر به آن اضافه کنید:
FluentNHibernate.dll
NHibernate.dll
NHibernate.ByteCode.Castle.dll
NHSample1.dll : در قسمت‌های قبل تعاریف موجودیت‌ها و نگاشت‌ آن‌ها را در این پروژه class library ایجاد کرده بودیم و اکنون قصد استفاده از آن را داریم.

اگر دیتابیس قسمت قبل را هنوز ایجاد نکرده‌اید، کلاس CDb را به برنامه افزوده و سپس متد CreateDb آن‌را به برنامه اضافه نمائید.

using FluentNHibernate;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHSample1.Mappings;

namespace ConsoleTestApplication
{
class CDb
{
public static void CreateDb(IPersistenceConfigurer dbType)
{
var cfg = Fluently.Configure().Database(dbType);

PersistenceModel pm = new PersistenceModel();
pm.AddMappingsFromAssembly(typeof(CustomerMapping).Assembly);
var sessionSource = new SessionSource(
cfg.BuildConfiguration().Properties,
pm);

var session = sessionSource.CreateSession();
sessionSource.BuildSchema(session, true);
}
}
}
اکنون برای ایجاد دیتابیس اس کیوال سرور بر اساس نگاشت‌های قسمت قبل، تنها کافی است دستور ذیل را صادر کنیم:

CDb.CreateDb(
MsSqlConfiguration
.MsSql2008
.ConnectionString("Data Source=(local);Initial Catalog=HelloNHibernate;Integrated Security = true")
.ShowSql());

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

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

using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHSample1.Mappings;

namespace ConsoleTestApplication
{
class Config
{
public static ISessionFactory CreateSessionFactory(IPersistenceConfigurer dbType)
{
return
Fluently.Configure().Database(dbType
).Mappings(m => m.FluentMappings.AddFromAssembly(typeof(CustomerMapping).Assembly))
.BuildSessionFactory();
}
}
}
اگر بحث را دنبال کرده باشید، این کلاس را پیشتر در کلاس FixtureBase آزمون واحد خود، به نحوی دیگر دیده بودیم. برای کار با NHibernate‌ نیاز به یک سشن مپ شده به موجودیت‌های برنامه می‌باشد که توسط متد CreateSessionFactory کلاس فوق ایجاد خواهد شد. این متد را به این جهت استاتیک تعریف کرده‌ایم که هیچ نوع وابستگی به کلاس جاری خود ندارد. در آن نوع دیتابیس مورد استفاده ( برای مثال اس کیوال سرور 2008 یا هر مورد دیگری که مایل بودید)، به همراه اسمبلی حاوی اطلاعات نگاشت‌های برنامه معرفی شده‌اند.

اکنون سورس کامل مثال برنامه را در نظر بگیرید:

کلاس CDbOperations جهت اعمال ثبت و حذف اطلاعات:

using System;
using NHibernate;
using NHSample1.Domain;

namespace ConsoleTestApplication
{
class CDbOperations
{
ISessionFactory _factory;

public CDbOperations(ISessionFactory factory)
{
_factory = factory;
}

public int AddNewCustomer()
{
using (ISession session = _factory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
Customer vahid = new Customer()
{
FirstName = "Vahid",
LastName = "Nasiri",
AddressLine1 = "Addr1",
AddressLine2 = "Addr2",
PostalCode = "1234",
City = "Tehran",
CountryCode = "IR"
};

Console.WriteLine("Saving a customer...");

session.Save(vahid);
session.Flush();//چندین عملیات با هم و بعد

transaction.Commit();

return vahid.Id;
}
}
}

public void DeleteCustomer(int id)
{
using (ISession session = _factory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
Customer customer = session.Load<Customer>(id);
Console.WriteLine("Id:{0}, Name: {1}", customer.Id, customer.FirstName);

Console.WriteLine("Deleting a customer...");
session.Delete(customer);

session.Flush();//چندین عملیات با هم و بعد

transaction.Commit();
}
}
}
}
}
و سپس استفاده از آن در برنامه

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

namespace ConsoleTestApplication
{
class Program
{
static void Main(string[] args)
{
//CDb.CreateDb(SQLiteConfiguration.Standard.ConnectionString("data source=sample.sqlite").ShowSql());
//return;

//todo: Read ConnectionString from app.config or web.config
using (ISessionFactory session = Config.CreateSessionFactory(
MsSqlConfiguration
.MsSql2008
.ConnectionString("Data Source=(local);Initial Catalog=HelloNHibernate;Integrated Security = true")
.ShowSql()
))
{
CDbOperations db = new CDbOperations(session);
int id = db.AddNewCustomer();
Console.WriteLine("Loading a customer and delete it...");
db.DeleteCustomer(id);
}

Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}
توضیحات:
نیاز است تا ISessionFactory را برای ساخت سشن‌های دسترسی به دیتابیس ذکر شده در تنظمیات آن جهت استفاده در تمام تردهای برنامه، ایجاد نمائیم. لازم به ذکر است که تا قبل از فراخوانی BuildSessionFactory این تنظیمات باید معرفی شده باشند و پس از آن دیگر اثری نخواهند داشت.
ایجاد شیء ISessionFactory هزینه بر است و گاهی بر اساس تعداد کلاس‌هایی که باید مپ شوند، ممکن است تا چند ثانیه به طول انجامد. به همین جهت نیاز است تا یکبار ایجاد شده و بارها مورد استفاده قرار گیرد. در برنامه به کرات از using استفاده شده تا اشیاء IDisposable را به صورت خودکار و حتمی، معدوم نماید.

بررسی متد AddNewCustomer :
در ابتدا یک سشن را از ISessionFactory موجود درخواست می‌کنیم. سپس یکی از بهترین تمرین‌های کاری جهت کار با دیتابیس‌ها ایجاد یک تراکنش جدید است تا اگر در حین اجرای کوئری‌ها مشکلی در سیستم، سخت افزار و غیره پدید آمد، دیتابیسی ناهماهنگ حاصل نشود. زمانیکه از تراکنش استفاده شود، تا هنگامیکه دستور transaction.Commit آن با موفقیت به پایان نرسیده باشد، اطلاعاتی در دیتابیس تغییر نخواهد کرد و از این لحاظ استفاده از تراکنش‌ها جزو الزامات یک برنامه اصولی است.
در ادامه یک وهله از شیء Customer را ایجاد کرده و آن‌را مقدار دهی می‌کنیم (این شیء در قسمت‌های قبل ایجاد گردید). سپس با استفاده از session.Save دستور ثبت را صادر کرده، اما تا زمانیکه transaction.Commit فراخوانی و به پایان نرسیده باشد، اطلاعاتی در دیتابیس ثبت نخواهد شد.
نیازی به ذکر سطر فلاش در این مثال نبود و NHibernate اینکار را به صورت خودکار انجام می‌دهد و فقط از این جهت عنوان گردید که اگر چندین عملیات را با هم معرفی کردید، استفاده از session.Flush سبب خواهد شد که رفت و برگشت‌ها به دیتابیس حداقل شود و فقط یکبار صورت گیرد.
در پایان این متد، Id ثبت شده در دیتابیس بازگشت داده می‌شود.

چون در متد CreateSessionFactory ، متد ShowSql را نیز ذکر کرده بودیم، هنگام اجرای برنامه، عبارات SQL ایی که در پشت صحنه توسط NHibernate تولید می‌شوند را نیز می‌توان مشاهده نمود:



بررسی متد DeleteCustomer :
ایجاد سشن و آغاز تراکنش آن همانند متد AddNewCustomer است. سپس در این سشن، یک شیء از نوع Customer با Id ایی مشخص load‌ خواهد گردید. برای نمونه، نام این مشتری نیز در کنسول نمایش داده می‌شود. سپس این شیء مشخص و بارگذاری شده را به متد session.Delete ارسال کرده و پس از فراخوانی transaction.Commit ، این مشتری از دیتابیس حذف می‌شود.

برای نمونه خروجی SQL پشت صحنه این عملیات که توسط NHibernate مدیریت می‌شود، به صورت زیر است:

Saving a customer...
NHibernate: select next_hi from hibernate_unique_key with (updlock, rowlock)
NHibernate: update hibernate_unique_key set next_hi = @p0 where next_hi = @p1;@p0 = 17, @p1 = 16
NHibernate: INSERT INTO [Customer] (FirstName, LastName, AddressLine1, AddressLine2, PostalCode, City, CountryCode, Id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7);@p0 = 'Vahid', @p1 = 'Nasiri', @p2 = 'Addr1', @p3 = 'Addr2', @p4 = '1234', @p5 = 'Tehran', @p6 = 'IR', @p7 = 16016
Loading a customer and delete it...
NHibernate: SELECT customer0_.Id as Id2_0_, customer0_.FirstName as FirstName2_0_, customer0_.LastName as LastName2_0_, customer0_.AddressLine1 as AddressL4_2_0_, customer0_.AddressLine2 as AddressL5_2_0_, customer0_.PostalCode as PostalCode2_0_, customer0_.City as City2_0_, customer0_.CountryCode as CountryC8_2_0_ FROM [Customer] customer0_ WHERE customer0_.Id=@p0;@p0 = 16016
Id:16016, Name: Vahid
Deleting a customer...
NHibernate: DELETE FROM [Customer] WHERE Id = @p0;@p0 = 16016
Press a key...
استفاده از دیتابیس SQLite بجای SQL Server در مثال فوق:

فرض کنید از هفته آینده قرار شده است که نسخه سبک و تک کاربره‌ای از برنامه ما تهیه شود. بدیهی است SQL server برای این منظور انتخاب مناسبی نیست (هزینه بالا برای یک مشتری، مشکلات نصب، مشکلات نگهداری و امثال آن برای یک کاربر نهایی و نه یک سازمان بزرگ که حتما ادمینی برای این مسایل در نظر گرفته می‌شود).
اکنون چه باید کرد؟ باید برنامه را از صفر بازنویسی کرد یا قسمت دسترسی به داده‌های آن‌را کلا مورد باز بینی قرار داد؟ اگر برنامه اسپاگتی ما اصلا لایه دسترسی به داده‌ها را نداشت چه؟! همه جای برنامه پر است از SqlCommand و Open و Close ! و عملا استفاده از یک دیتابیس دیگر یعنی باز نویسی کل برنامه.
همانطور که ملاحظه می‌کنید، زمانیکه با NHibernate کار شود، مدیریت لایه دسترسی به داده‌ها به این فریم ورک محول می‌شود و اکنون برای استفاده از دیتابیس SQLite تنها باید تغییرات زیر صورت گیرد:
ابتدا ارجاعی را به اسمبلی System.Data.SQLite.dll اضافه نمائید (تمام این اسمبلی‌های ذکر شده به همراه مجموعه FluentNHibernate ارائه می‌شوند). سپس:
الف) ایجاد یک دیتابیس خام بر اساس کلاس‌های domain و mapping تعریف شده در قسمت‌های قبل به صورت خودکار

CDb.CreateDb(SQLiteConfiguration.Standard.ConnectionString("data source=sample.sqlite").ShowSql());
ب) تغییر آرگومان متد CreateSessionFactory

//todo: Read ConnectionString from app.config or web.config
using (ISessionFactory session = Config.CreateSessionFactory(
SQLiteConfiguration.Standard.ConnectionString("data source=sample.sqlite").ShowSql()
))
{
...

نمایی از دیتابیس SQLite تشکیل شده پس از اجرای متد قسمت الف ، در برنامه Lita :




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

نکته:
در سه قسمت قبل، تمام خواص پابلیک کلاس‌های پوشه domain را به صورت معمولی و متداول معرفی کردیم. اگر نیاز به lazy loading در برنامه وجود داشت، باید تمامی کلاس‌ها را ویرایش کرده و واژه کلیدی virtual را به کلیه خواص پابلیک آن‌ها اضافه کرد. علت هم این است که برای عملیات lazy loading ، فریم ورک NHibernate باید یک سری پروکسی را به صورت خودکار جهت کلاس‌های برنامه ایجاد نماید و برای این امر نیاز است تا بتواند این خواص را تحریف (override) کند. به همین جهت باید آن‌ها را به صورت virtual تعریف کرد. همچنین تمام سطرهای Not.LazyLoad نیز باید حذف شوند.

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


مطالب
استفاده از SQLDom برای آنالیز عبارات T-SQL، قسمت دوم
مدتی قبل مطلبی را در مورد کتابخانه‌ی ویژه SQL Server که یک T-SQL Parser تمام عیار است، در این سایت مطالعه کردید. در این قسمت، همان مطلب را به نحو بهتر و ساده‌تری بازنویسی خواهیم کرد.
مشکلی که در دراز مدت با SQLDom وجود خواهد داشت، مواردی مانند SelectStarExpression و CreateProcedureStatement و امثال آن هستند. این‌ها را از کجا باید تشخیص داد؟ همچنین مراحل بررسی این اجزاء، نسبتا طولانی هستند و نیاز به یک راه حل عمومی‌تر در این زمینه وجود دارد.

راه حلی برای این مشکل در مطلب «XML ‘Visualizer’ for the TransactSql.ScriptDom parse tree» ارائه شده‌است. در اینجا تمام اجزای TSqlFragment توسط Reflection مورد بررسی و استخراج قرار گرفته و نهایتا یک فایل XML از آن حاصل می‌شود.
اگر نکات ذکر شده در این مقاله را تبدیل به یک برنامه با استفاده مجدد کنیم، به چنین شکلی خواهیم رسید:


این برنامه را از اینجا می‌توانید دریافت کنید:
DomToXml.zip

همانطور که در تصویر مشاهده می‌کنید، اینبار به سادگی، SelectStarExpression قابل تشخیص است و تنها کافی است در T-SQL پردازش شده، به دنبال SelectStarExpression‌ها بود. برای اینکار جهت ساده شدن آنالیز می‌توان با ارث بری از کلاس پایه TSqlFragmentVisitor شروع کرد:
using System;
using System.Linq;
using Microsoft.SqlServer.TransactSql.ScriptDom;

namespace DbCop
{
    public class SelectStarExpressionVisitor : TSqlFragmentVisitor
    {
        public override void ExplicitVisit(SelectStarExpression node)
        {
            Console.WriteLine(
                  "`Select *` detected @StartOffset:{0}, Line:{1}, T-SQL: {2}",
                  node.StartOffset,
                  node.StartLine,
                  string.Join(string.Empty, node.ScriptTokenStream.Select(x => x.Text)).Trim());

            base.ExplicitVisit(node);
        }
    }
}
در کلاس پایه TSqlFragmentVisitor به ازای تمام اشیاء شناخته شده‌ی ScriptDom، یک متد ExplicitVisit قابل بازنویسی درنظر گرفته شده‌است. در اینجا برای مثال نمونه‌ی SelectStarExpression آن را بازنویسی کرده‌ایم.
مرحله‌ی بعد، اجرای این کلاس Visitor است:
    public static class GenericVisitor
    {
        public static void Start(string tSql, TSqlFragmentVisitor visitor)
        {
            IList<ParseError> errors;
            TSqlScript sqlFragment;
            using (var reader = new StringReader(tSql))
            {
                var parser = new TSql120Parser(initialQuotedIdentifiers: true);
                sqlFragment = (TSqlScript)parser.Parse(reader, out errors);
            }

            if (errors != null && errors.Any())
            {
                var sb = new StringBuilder();
                foreach (var error in errors)
                    sb.AppendLine(error.Message);

                throw new InvalidOperationException(sb.ToString());
            }
            sqlFragment.Accept(visitor);
        }
    }
در اینجا متد Accept کلاس TSql120Parser، امکان پذیرش یک Visitor را دارد. به این معنا که Parser در حال کار، هر زمانیکه در حال آنالیز قسمتی از T-SQL دریافتی بود، نتیجه را به اطلاع یکی از متدهای کلاس پایه TSqlFragmentVisitor نیز خواهد رساند. بنابراین دیگر نیازی به نوشتن حلقه و بررسی تک تک اجزای خروجی TSql120Parser نیست. اگر نیاز به بررسی SelectStarExpression داریم، فقط کافی است Visitor آن‌را طراحی کنیم.

مثالی از نحوه‌ی استفاده از کلاس GenericVisitor فوق را در اینجا ملاحظه می‌کنید:
 var tsql = @"WITH ctex AS (
SELECT * FROM sys.objects
)
SELECT * FROM ctex";
GenericVisitor.Start(tsql, new SelectStarExpressionVisitor());
مطالب
مقابله با پسوردهایی که ساده حدس زده می‌شوند
هر ساله لیستی از پرکاربردترین کلمات عبور کاربران در دنیا، منتشر می‌شود که یک نمونه از آن‌را در اینجا می‌توان مشاهده کرد:
«Splashdata، توسعه دهنده نرم افزار‌های امنیتی، فهرست سالانه خود را از رایج‌ترین رمزهای عبور منتشر کرده است.»

می‌شود از این لیست برای بهبود پروسه ثبت نام در یک سایت استفاده کرد و همان زمان که کاربر کلمه عبور ضعیفی را وارد کرده است، به او پیغام داد که «کلمه عبور وارد شده را راحت می‌توان حدس زد!»
using System.Linq;

namespace SecurityModule
{
    public static class SafePassword
    {
        public static ISet<string> BadPasswords = new HashSet<string>
        {
            "password",
            "password1",
            "123456",
            "12345678",
            "1234",
            "qwerty",
            "12345",
            "dragon",
            "******",
            "baseball",
            "football",
            "letmein",
            "monkey",
            "696969",
            "abc123",
            "mustang",
            "michael",
            "shadow",
            "master",
            "jennifer",
            "111111",
            "2000",
            "jordan",
            "superman",
            "harley",
            "1234567",
            "iloveyou",
            "trustno1",
            "sunshine",
            "123123",
            "welcome"
        };

        public static bool IsSafePasword(this string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return false;
            if (data.Length < 5) return false;
            if (BadPasswords.Contains(data.ToLowerInvariant())) return false;
            if (data.AreAllCharsEuqal()) return false;

            return true;
        }

        public static bool AreAllCharsEuqal(this string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return false;
            data = data.ToLowerInvariant();
            var firstElement = data.ElementAt(0);
            var euqalCharsLen = data.ToCharArray().Count(x => x == firstElement);
            if (euqalCharsLen == data.Length) return true;
            return false;
        }
    }
}
متد الحاقی IsSafePasword فوق بررسی می‌کند که آیا کلمه عبور انتخابی:
- خالی نیست.
- بیشتر از 5 کاراکتر طول دارد.
- تمام حروف بکارگرفته شده در آن یکسان نیستند.

و در ASP.NET MVC با استفاده از قابلیت Remote validation آن استفاده از این متد به نحو زیر خواهد بود:
public partial class RegisterController : Controller
{
        //...

        [HttpPost]
        [OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
        public virtual ActionResult CheckPassword(string password1)
        {
            return Json(password1.IsSafePasword());
        }
}
ابتدا یک اکشن متد به کنترلر ثبت نام در سایت به نحو فوق اضافه خواهد شد.
سپس قسمتی از ViewModel متناظر با صفحه ثبت نام سایت، به شکل زیر اضافه و تعریف می‌گردد:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace MyBlog.Models
{
    public class RegisterViewModel
    {
        //...

        [Display(Name = "کلمه عبور")]
        [Required(ErrorMessage = "لطفا کلمه عبور خود را وارد نمائید")]
        [DataType(DataType.Password)]
        [StringLength(50, MinimumLength = 5, ErrorMessage = "حداقل طول کلمه عبور 5 حرف است")]
        [Remote(action: "CheckPassword",  controller: "Register",  HttpMethod = "POST",
                     ErrorMessage = "کلمه عبور وارد شده را راحت می‌توان حدس زد!")]
        public string Password1 { get; set; }
    }
}