۱۳۸۸/۰۷/۰۷

سايت CodePlex و SVN


اگر سورس پروژه Google book downloader را بخواهيد از طريق سايت CodePlex دريافت كنيد چند صد مگابايت خواهد شد. علت هم اين است كه نويسنده پروژه تمام نگارش‌هاي قبلي را نيز در اين مكان نگهداري مي‌كند و وب سايت CodePlex هم اجازه‌ي انتخاب فقط trunk را جهت دريافت نمي‌دهد. يا همه را بايد دريافت كنيد يا هيچ.
راه ميانبري براي دريافت ساده‌تر اين پروژه نيز وجود دارد. مدت‌ها است كه امكان اتصال به اين سايت از طريق كلاينت‌هاي SVN مانند TortoiseSVN نيز فراهم شده است. فقط كافي است براي دريافت سورس كامل يك پروژه در CodePlex ، مسير زير را Checkout كرد:

https://projectname.svn.codeplex.com/svn

براي مثال جهت دريافت فقط trunk اين پروژه، مي‌توان مسير زير را checkout كرد:

https://googlebookdownloader.svn.codeplex.com/svn/trunk



۱۳۸۸/۰۷/۰۶

آشنايي با قابليت FileStream اس كيوال سرور 2008 - قسمت سوم


در انتهاي قسمت قبل، نحوه‌ي ايجاد يك جدول جديد با فيلدي از نوع فايل استريم بررسي شد، حال اگر جدولي از پيش وجود داشت، نحوه‌ي افزودن فيلد ويژه مورد نظر به آن، به صورت زير است:

alter table tbl_files set(filestream_on ='default')

go
alter table tbl_files
add

[systemfile] varbinary(max) filestream null ,
FileId uniqueidentifier not null rowguidcol unique default (newid())
go

در ادامه جدول tblFiles قسمت قبل را در نظر بگيريد:

CREATE TABLE [tblFiles](
[FileId] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[Title] [nvarchar](255) NOT NULL,
[SystemFile] [varbinary](max) FILESTREAM NULL,
UNIQUE NONCLUSTERED
(
[FileId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] FILESTREAM_ON [fsg1]

ALTER TABLE [dbo].[tblFiles] ADD DEFAULT (newid()) FOR [FileId]
GO

نحوه‌ي افزودن ركوردي جديد به جدول tblFiles :

INSERT INTO [tblFiles]
(
[Title],
[SystemFile]
)
VALUES
(
'file-1',
CAST('data data data' AS VARBINARY(MAX))
)
در اينجا سعي كرده‌ايم يك رشته ساده را در فيلدي از نوع فايل استريم ذخيره كنيم كه روش كار به صورت فوق است. از آنجائيكه مقدار پيش فرض FileId را هنگام تعريف جدول به NEWID تنظيم كرده‌ايم، نيازي به ذكر آن نيست و به صورت خودكار محاسبه و ذخيره خواهد شد.
اگر كنجكاو باشيد كه اين فايل اكنون كجا ذخيره شده و نحوه‌ي مديريت آن توسط اس كيوال سرور به چه صورتي است، فقط كافي است به مسيري كه هنگام افزودن گروه فايل‌ها و فايل مربوطه در تنظيمات خواص ديتابيس در قسمت قبل مشخص كرديم، مراجعه كرد (شكل زير).



بديهي است افزودن يك رشته به اين صورت كاربرد عملي ندارد و صرفا جهت يك مثال ارائه شد. در ادامه، نحوه‌ي ثبت محتويات يك فايل را در فيلدي از نوع فايل استريم و سپس خواندن اطلاعات آن‌را از طريق برنامه نويسي بررسي خواهيم كرد:

using System;
using System.IO;
using System.Data.SqlClient;
using System.Data;

namespace FileStreamTest
{
class CFS
{
/// <summary>
/// افزودن ركورد به جدول حاوي ستوني از نوع فايل استريم
/// </summary>
/// <param name="filePath">مسير فايل</param>
/// <param name="title">عنواني دلخواه</param>
public static void AddNewRecord(string filePath, string title)
{
//آيا فايل وجود دارد؟
if (!File.Exists(filePath))
throw new FileNotFoundException(
"لطفا مسير فايل معتبري را مشخص نمائيد", filePath);

//خواندن اطلاعات فايل در آرايه‌اي از بايت‌ها
byte[] buffer = File.ReadAllBytes(filePath);

using (SqlConnection objSqlCon = new SqlConnection())
{
//todo: كانكشن استرينگ بايد از يك فايل كانفيگ خوانده شود
objSqlCon.ConnectionString =
"Data Source=(local);Initial Catalog=testdb2009;Integrated Security = true";
objSqlCon.Open();

//شروع يك تراكنش
using (SqlTransaction objSqlTran = objSqlCon.BeginTransaction())
{
//ساخت عبارت افزودن پارامتري
using (SqlCommand objSqlCmd = new SqlCommand(
"INSERT INTO [tblFiles]([Title],[SystemFile]) VALUES(@title , @file)",
objSqlCon, objSqlTran))
{
objSqlCmd.CommandType = CommandType.Text;

//تعريف وضعيت پارامترها و مقدار دهي آن‌ها
objSqlCmd.Parameters.AddWithValue("@title", title);
objSqlCmd.Parameters.AddWithValue("@file", buffer);

//اجراي فرامين
objSqlCmd.ExecuteNonQuery();
}

//پايان تراكنش
objSqlTran.Commit();
}
}
}

/// <summary>
/// دريافت اطلاعات فايل ذخيره شده به صورت آرايه‌اي از بايت‌ها
/// </summary>
/// <param name="fileId">كليد مورد استفاده</param>
/// <returns></returns>
public static byte[] GetDataFromDb(string fileId)
{
byte[] data = null;

using (SqlConnection objConn = new SqlConnection())
{
//كوئري اس كيوال پارامتري جهت دريافت محتويات فايل
string cmdText = "SELECT SystemFile FROM tblFiles WHERE FileId=@id";
using (SqlCommand objCmd = new SqlCommand(cmdText, objConn))
{
//todo: كانكشن استرينگ بايد از يك فايل كانفيگ خوانده شود
objConn.ConnectionString =
"Data Source=(local);Initial Catalog=testdb2009;Integrated Security = true";
objConn.Open();

//تنظيم كردن وضعيت و مقدار پارامتر تعريف شده در كوئري
objCmd.Parameters.AddWithValue("@id", fileId);

//اجراي فرامين و دريافت فايل
using (SqlDataReader objread = objCmd.ExecuteReader())
{
if (objread != null)
if (objread.Read())
{
if (objread["SystemFile"] != DBNull.Value)
data = (byte[])objread["SystemFile"];
}
}
}
}

return data;
}
}
}

مثالي در مورد روش استفاده از كلاس فوق :

using System.IO;

namespace FileStreamTest
{
class Program
{
static void Main(string[] args)
{
CFS.AddNewRecord(@"C:\filest05.PNG", "test1");

//آي دي ركورد ذخيره شده در ديتابيس براي مثال
byte[] data = CFS.GetDataFromDb("BB848D45-382C-4D95-BF4E-52C3509407D4");
if (data != null)
{
File.WriteAllBytes(@"C:\tst.PNG", data);
}
}
}
}
روش فوق با روش متداول افزودن يك فايل به ديتابيس اس كيوال سرور هيچ تفاوتي ندارد و اين‌جا هم بدون مشكل كار مي‌كند. اطلاعات نهايي به صورت فايل‌هايي بر روي سيستم كه توسط اس كيوال سرور مديريت خواهند شد و با جدول شما يكپارچه‌اند، ذخيره مي‌شوند.

در روش ديگري كه در اكثر مقالات مرتبط مورد استفاده است، از شيء SqlFileStream كمك گرفته شده و نحوه‌ي انجام آن نيز به صورت زير مي‌باشد.
در ابتدا دو رويه ذخيره شده زير را ايجاد مي‌كنيم:

CREATE PROCEDURE [AddFile](@Title NVARCHAR(255), @filepath VARCHAR(MAX) OUTPUT)
AS
BEGIN
SET NOCOUNT ON;

DECLARE @ID UNIQUEIDENTIFIER
SET @ID = NEWID()

INSERT INTO [tblFiles]
(
[FileId],
[title],
[SystemFile]
)
VALUES
(
@ID,
@Title,
CAST('' AS VARBINARY(MAX))
)

SELECT @filepath = SystemFile.PathName()
FROM tblFiles
WHERE FileId = @ID
END
GO

CREATE PROCEDURE [GetFilePath](@Id VARCHAR(50))
AS
BEGIN
SET NOCOUNT ON;

SELECT SystemFile.PathName()
FROM tblFiles
WHERE FileId = @ID
END
در رويه ذخيره شده AddFile ، ابتدا ركوردي بر اساس عنوان دلخواه ورودي با يك فايل خالي ايجاد مي‌شود. سپس مسير سيستمي اين فايل را در آرگومان خروجي filepath قرار مي‌دهيم. SystemFile.PathName از اس كيوال سرور 2008 جهت فيلدهاي فايل استريم به اس كيوال سرور اضافه شده است. از اين مسير در برنامه خود جهت نوشتن بايت‌هاي فايل مورد نظر در آن توسط شيء SqlFileStream استفاده خواهيم كرد.
رويه ذخيره شده GetFilePath نيز تنها مسير سيستمي فايل استريم ذخيره شده را بر مي‌گرداند.
به اين ترتيب كدهاي برنامه به صورت زير تغيير خواهند كرد:

using System.Data.SqlClient;
using System.Data;
using System.Data.SqlTypes;
using System.IO;

namespace FileStreamTest
{
class CFSqlFileStream
{
/// <summary>
/// افزودن ركورد به جدول حاوي ستوني از نوع فايل استريم
/// </summary>
/// <param name="filePath">مسير فايل</param>
/// <param name="title">عنواني دلخواه</param>
public static void AddNewRecord(string filePath, string title)
{
//آيا فايل وجود دارد؟
if (!File.Exists(filePath))
throw new FileNotFoundException(
"لطفا مسير فايل معتبري را مشخص نمائيد", filePath);

//خواندن اطلاعات فايل در آرايه‌اي از بايت‌ها
byte[] buffer = File.ReadAllBytes(filePath);

using (SqlConnection objSqlCon = new SqlConnection())
{
//todo: كانكشن استرينگ بايد از يك فايل كانفيگ خوانده شود
objSqlCon.ConnectionString =
"Data Source=(local);Initial Catalog=testdb2009;Integrated Security = true";
objSqlCon.Open();

//شروع يك تراكنش
using (SqlTransaction objSqlTran = objSqlCon.BeginTransaction())
{
//استفاده از رويه ذخيره شده افزودن فايل
using (SqlCommand objSqlCmd = new SqlCommand(
"AddFile", objSqlCon, objSqlTran))
{
objSqlCmd.CommandType = CommandType.StoredProcedure;

//مشخص ساختن وضعيت و مقدار پارامتر عنوان
SqlParameter objSqlParam1 = new SqlParameter("@Title", SqlDbType.NVarChar, 255);
objSqlParam1.Value = title;

//مشخص ساختن پارامتر خروجي رويه ذخيره شده
SqlParameter objSqlParamOutput = new SqlParameter("@filepath", SqlDbType.VarChar, -1);
objSqlParamOutput.Direction = ParameterDirection.Output;

//افزودن پارامترها به شيء كامند
objSqlCmd.Parameters.Add(objSqlParam1);
objSqlCmd.Parameters.Add(objSqlParamOutput);

//اجراي رويه ذخيره شده
objSqlCmd.ExecuteNonQuery();

//و سپس دريافت خروجي آن
string Path = objSqlCmd.Parameters["@filepath"].Value.ToString();

//زمينه تراكنش فايل استريم موجود را دريافت كرده و از آن براي نوشتن محتويات فايل استفاده خواهيم كرد
//اين مورد نيز يكي از تازه‌هاي اس كيوال سرور 2008 است
using (SqlCommand objCmd = new SqlCommand(
"SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()", objSqlCon, objSqlTran))
{
byte[] objContext = (byte[])objCmd.ExecuteScalar();
using (SqlFileStream objSqlFileStream =
new SqlFileStream(Path, objContext, FileAccess.Write))
{
objSqlFileStream.Write(buffer, 0, buffer.Length);
}
}
}

objSqlTran.Commit();
}
}
}

/// <summary>
/// دريافت اطلاعات فايل ذخيره شده به صورت آرايه‌اي از بايت‌ها
/// </summary>
/// <param name="fileId">كليد مورد استفاده</param>
/// <returns></returns>
public static byte[] GetDataFromDb(string fileId)
{
byte[] buffer = null;

using (SqlConnection objSqlCon = new SqlConnection())
{
//todo: كانكشن استرينگ بايد از يك فايل كانفيگ خوانده شود
objSqlCon.ConnectionString =
"Data Source=(local);Initial Catalog=testdb2009;Integrated Security = true";
objSqlCon.Open();

//شروع يك تراكنش
using (SqlTransaction objSqlTran = objSqlCon.BeginTransaction())
{
//استفاده از رويه ذخيره شده دريافت مسير فايل
using (SqlCommand objSqlCmd =
new SqlCommand("GetFilePath", objSqlCon, objSqlTran))
{
objSqlCmd.CommandType = CommandType.StoredProcedure;

//مشخص ساختن پارامتر ورودي رويه ذخيره شده و مقدار دهي آن
SqlParameter objSqlParam1 = new SqlParameter("@ID", SqlDbType.VarChar, 50);
objSqlParam1.Value = fileId;
objSqlCmd.Parameters.Add(objSqlParam1);

//اجراي رويه ذخيره شده و دريافت مسير سيستمي فايل استريم
string path = string.Empty;
using (SqlDataReader sdr = objSqlCmd.ExecuteReader())
{
sdr.Read();
path = sdr[0].ToString();
}

//زمينه تراكنش فايل استريم موجود را دريافت كرده و از آن براي خواندن محتويات فايل استفاده خواهيم كرد
//اين مورد نيز يكي از تازه‌هاي اس كيوال سرور 2008 است
using (SqlCommand objCmd = new SqlCommand(
"SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()", objSqlCon, objSqlTran))
{
byte[] objContext = (byte[])objCmd.ExecuteScalar();

using (SqlFileStream objSqlFileStream =
new SqlFileStream(path, objContext, FileAccess.Read))
{
buffer = new byte[(int)objSqlFileStream.Length];
objSqlFileStream.Read(buffer, 0, buffer.Length);
}
}
}

objSqlTran.Commit();
}
}

return buffer;
}
}
}
در پايان براي تكميل بحث مي‌توان به مقاله‌ي مرجع زير مراجعه كرد:
FILESTREAM Storage in SQL Server 2008

۱۳۸۸/۰۷/۰۵

آشنايي با قابليت FileStream اس كيوال سرور 2008 - قسمت دوم


در اين قسمت نحوه‌ي فعال سازي قابليت FileStream را بررسي خواهيم كرد و در قسمت بعدي نحوه‌ي دسترسي به آن‌را از طريق برنامه نويسي مرور مي‌نمائيم.

فعال سازي قابليت FileStream
همانند اكثر قابليت‌هاي اس كيوال سرور، فعال سازي FileStream نيز حداقل به دو صورت استفاده از GUI و قابليت‌هاي management studio ميسر است و يا استفاده از دستورات T-SQL (و البته كتابخانه‌ي SMO يا همان محصور كننده‌ي توانايي‌هاي management studio نيز قابل استفاده است).

روش اول) استفاده از management studio
قابليت FileStream به صورت پيش فرض غيرفعال است. براي فعال سازي آن به مسير زير مراجعه نمائيد:

Start > All Programs > Microsoft SqlServer 2008 > Configuration Tools > SQL Server Configuration Manager

سپس در قسمت SQL Server services ، وهله مربوط به SQL Server را يافته، كليك راست و به برگه خواص آن مراجعه كرده (شكل زير) و قابليت FileStream را فعال كنيد:



گزينه‌هاي مختلف آن به شرح زير هستند:
• Enable FileStream for transact-sql access : امكان استفاده از دستورات T-SQL را جهت دسترسي به فايل‌ها فعال مي‌سازد (يا برعكس)
• Enable FileStream for File I/O streaming access : امكان دسترسي به فايل‌ها با استفاده از Win32 streaming access
• All remote clients to have streaming access to file stream data : اجازه‌ي دسترسي به كلاينت‌هاي راه دور جهت استفاده از قابليت FileStream

مرحله بعد، فعال سازي سطح دسترسي به سرور است. به management studio مراجعه نمائيد. سپس بر روي وهله سرور مورد نظر كليك راست نموده و به خواص آن مراجعه كنيد (شكل زير). سپس در قسمت advanced سطح دسترسي را بر روي Full قرار دهيد.



پس از اين تنظيم به شما پيغام داده خواهد شد كه بايد ديتابيس سرور را يكبار راه اندازي مجدد نمائيد تا تنظيمات مورد نظر، اعمال شوند.

در ادامه بايد ديتابيسي را كه نياز است نوع داده FileStream را بپذيرد، تنظيم نمود.
بر روي ديتابيس مورد نظر كليك راست كرده و در برگه خواص آن به گزينه‌ي Filegroups مراجعه كنيد. سپس در اينجا يك گروه جديد را اضافه كرده ، نامي دلخواه را وارد نموده و سپس تيك مربوط به default بودن آن‌را نيز قرار دهيد (شكل زير):



سپس در همين برگه‌ي خواص ديتابيس كه باز است، به گزينه‌ي Files مراجعه كنيد. در اينجا سه كار را بايد انجام دهيد. ابتدا بر روي دكمه Add كليك كرده و در قسمت logical name رديف اضافه شده، نامي دلخواه را وارد كنيد. سپس file type آن را بر روي FileStream قرار دهيد. در ادامه به قسمت path در همين رديف مراجعه نموده و مسير ذخيره سازي را مشخص كنيد. در پايان بر روي دكمه‌ي OK كليك نمائيد تا كار تنظيم ديتابيس به پايان رسد (شكل زير):



روش دوم) استفاده از دستورات T-SQL
منهاي قسمت تنظيمات SQL Server Configuration Manager كه بايد از طريق روش عنوان شده صورت گيرد، ساير موارد فوق را با استفاده از دستورات T-SQL نيز مي‌توان انجام داد:

الف) تنظيم سطح دسترسي بر روي سرور

EXEC sp_configure filestream_access_level, 2 -- 0 : Disable , 1 : Transact Sql Access , 2 : Win IO Access
GO
RECONFIGURE
GO
ب) تنظيمات ديتابيس

اگر نياز باشد ديتابيس جديدي ايجاد شود: (ايجاد گروه فايل مربوطه و سپس تنظيمات مسير آن)

CREATE DATABASE Test_Db
ON
PRIMARY ( NAME = TestDb1,
FILENAME = 'C:\DATA\Test_Db.mdf'),
FILEGROUP FileStreamGroup1 CONTAINS FILESTREAM( NAME = Testfsg1,
FILENAME = 'C:\DATA\Learning_DbStream')
LOG ON ( NAME = TestDbLog1,
FILENAME = 'C:\DATA\Test_Db.ldf')
GO

و يا ايجاد تغييرات بر روي ديتابيسي موجود: (ايجاد گروه فايل مخصوص و سپس افزودن فايل مربوطه و تنظيمات آن)

--add filegroup
alter database TestDb
Add FileGroup FileStreamFileGroup1 contains FileStream
go

--Add FileGroup To DB
alter database TestDB
add file
(
name = 'UserDocuments' ,
filename = 'C:\FileStream\UserDocuments'
) to filegroup FileStreamFileGroup1


تعريف جدولي آزمايشي به همراه فيلدي از نوع FileStream :
تا اينجا سرور و همچنين ديتابيس جهت پذيرش اين نوع داده آماده شدند. اكنون نوبت به استفاده از آن است:

CREATE TABLE [tblFiles]
(
FileId UNIQUEIDENTIFIER NOT NULL ROWGUIDCOL UNIQUE DEFAULT(NEWID()),
Title NVARCHAR(255) NOT NULL,
SystemFile VARBINARY(MAX) FILESTREAM NULL
)
ON [PRIMARY] FILESTREAM_ON [fsg1]

توسط دستور T-SQL فوق جدولي كه از نوع داده FileStream استفاده مي‌كند، ايجاد خواهد شد. اين جدول همانطور كه مشخص است حتما بايد داراي يك فيلد منحصربفرد باشد (ر.ك. مقاله قبل) و همچنين برچسب فايل استريم به فيلدي از نوع VARBINARY(MAX) نيز الصاق شده است. به علاوه گروه فايل آن نيز بايد به صورت صريح مشخص گردد؛ كه در مثال ما مطابق تصاوير به fsg1 تنظيم شده بود.

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

۱۳۸۸/۰۷/۰۴

آشنايي با قابليت FileStream اس كيوال سرور 2008 - قسمت اول


مطلبي چندي قبل در مورد "ذخيره سازي فايل‌ها در ديتابيس يا استفاده از فايل سيستم متداول؟" منتشر گرديد، جهت برشمردن فوايد ذخيره سازي فايل‌ها در ديتابيس (+). اما معايب اين نوع ذخيره سازي بررسي نشدند:

الف) اختصاص يافتن قسمتي از بافر SQL Server به اين امر.
ب) با توجه به قرار گرفتن داده‌هاي BLOB‌ در ديتابيس ، transaction log قابل توجهي توليد خواهد شد. (+)
ج) بيش از 2GB را نمي‌توان در فيلدهايي از نوع varbinary(max) ذخيره كرد.
د) به روز رساني BLOB ها سبب ايجاد fragmentation مي‌شود.

مايكروسافت براي رفع اين مشكلات در SQL Server 2008 قابليت جديدي را ارائه داده است به نام FileStream كه در طي مقالاتي به بررسي آن خواهيم پرداخت.

FILESTREAM موتور ديتابيس اس كيوال سرور را با سيستم فايل NTFS يكپارچه مي‌كند؛ به اين صورت كه داده‌هاي BLOB از نوع varbinary(max) را به صورت فايل بر روي سيستم ذخيره خواهد كرد. سپس با استفاده از دستورات T-SQL مي‌توان اين فايل‌ها را ثبت، حذف، به روز رساني، جستجو و بك آپ گيري كرد. اين قابليت نيز از فيلدهاي varbinary(max) استفاده مي‌كند؛ اما اكنون ويژگي و برچسب FILESTREAM به اين نوع فيلدها الصاق خواهد شد. FILESTREAM data بايد در FILESTREAM filegroups ذخيره شوند. FILESTREAM filegroups در حقيقت همان پوشه‌هاي فايل سيستم مي‌باشند. به آن‌ها data containers نيز گفته مي‌شوند كه مرزي هستند بين ذخيره سازي داده‌ها در فايل سيستم و در ديتابيس.

مزاياي سيستم FileStream چيست؟
الف) سيستم transaction مختص به خود را داشته، به همين جهت سبب رشد غير منطقي حجم فايل transaction log ديتابيس اصلي نمي‌شوند.
ب) هنگام به روز رساني فيلدهايي از اين دست، صرفا ايجاد يا حذف يك فايل مد نظر است؛ بنابراين fragmentation ايجاد شده در اين حالت بسيار كمتر از روش استفاده از فيلدهايي از نوع varbinary(max) مي‌باشد.
ج) استفاده از NT system cache جهت كش كردن اطلاعات كه سبب بالا بردن بازدهي بانك اطلاعاتي خواهد شد.
د) از buffer pool اس كيوال سرور در اين حالت استفاده نشده (مطابق قسمت ج) و اين حافظه جهت امور روزمره‌ي اس كيوال سرور كاملا مهيا خواهد بود.
ه) محدوديت 2GB فيلدهايي از نوع varbinary(max) با توجه به ذخيره سازي اين نوع BLOBs در فايل سيستم، ديگر وجود نخواهد داشت.

چه زماني بهتر است از FileStream استفاده شود؟
الف) فايل‌هايي كه ذخيره مي‌شوند به طور متوسط بيش از يك مگابايت حجم داشته باشند. (براي كمتر از اين مقدار varbinary(max) BLOBs كارآيي بهتري را ارائه مي‌دهند). هر چند اين مرز يك مگابايت مطابق اطلاعات books online است اما تجربيات كاري نشان مي‌دهند كه اين سقف را بايد 256 كيلوبايت درنظر گرفت.
ب) قابليت خواندن سريع اطلاعات فايل‌ها مد نظر باشد (بررسي كارآيي مطابق تصوير زير از MSDN). سيستم NTFS نسبت به SQL Server‌ در خواندن فايل‌هاي حجيم سريعتر عمل مي‌كند.
ج) اگر از يك معماري middle tier در برنامه‌هاي خود در حال استفاده‌ايد.
د) زمانيكه نياز باشد تا اطلاعات relational و non-relational در يك تراكنش مورد استفاده قرار گيرند.



نكاتي را كه بايد هنگام ذخيره سازي اطلاعات در FileStream در نظر داشت
الف) هنگامي كه يك جدول حاوي فيلدي از نوع FileStream مي‌باشد، بايد داراي فيلد ID منحصربفرد نيز باشد.
ب) data containers ايي كه پيش از اين در مورد آن‌ها صحبت شد، نبايد تو در تو باشند.
ج) FILESTREAM filegroups بر روي درايوهاي فشرده شده نيز مي‌توانند قرار داشته باشند.

FileStream از ديدگاه امنيت
امنيت داده‌هاي FileStream در اس كيوال سرور دقيقا همانند امنيت ساير اطلاعات ذخيره شده در ديتابيس است (دسترسي در حد جدول و يا فيلد). اگر كاربري دسترسي به فيلد FileStream در يك جدول داشته باشد، مي‌تواند آن‌ فايل را گشوده و استفاده كند. رمزنگاري بر روي اين ستون‌ها پشتيباني نمي‌شود. تنها اكانتي كه اس كيوال سرور تحت آن در حال اجرا است دسترسي به FILESTREAM container دارد. همچنين توصيه شده است كه به هيچ اكانت ديگري اين دسترسي داده نشود. زمانيكه يك ديتابيس آغاز و مشغول به كار مي‌شود، اس كيوال سرور دسترسي به FILESTREAM data container را محدود خواهد كرد و دسترسي به اين اطلاعات تنها از طريق دستورات T-SQL و يا OpenSqlFilestream API ميسر خواهد بود. بديهي است زمانيكه اس كيوال سرور متوقف شود، اين اطلاعات بدون هيچگونه محدوديتي قابل دسترسي بوده و تنها محدوديت‌هاي سيستمي به آن‌ها اعمال خواهند شد (كه اين مورد بايد مد نظر باشد).

نگهداري FileStream
FileStream به صورت فيلدهاي varbinary(max) يكپارچه با ديتابيس ذخيره مي‌شود؛ بنابراين نحوه‌ي تهيه پشتيبان از آن‌ها همانند روش‌هاي متداول است بدون هيچگونه تغييري (و اين اطلاعات در بك آپ ديتابيس لحاظ مي‌شوند). اگر نياز بود هنگام تهيه پشتيبان از اين نوع داده‌ها بك آپ گرفته نشود، مي‌توان از partial backup با پارامترهاي مربوطه استفاده كرد.


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

۱۳۸۸/۰۷/۰۳

تغيير رويه در مورد لينك‌هاي ارائه شده


از اين پس بجاي ارسال مطالب خواندني‌ها كه در اصل لينك به يك سري سايت با مطالب روز بودند، اين لينك‌ها در سايت iDevCenter ارائه خواهند شد.
براي پيگيري مستمر اين سايت هم فقط كافي است مشترك فيد RSS آن شويد:


براي راي دادن به لينك‌هاي جديد هم لطفا به آدرس زير مراجعه كنيد (مكان آن زياد واضح نيست به همين علت تعداد زيادي لينك در صف انتظار هستند):




دريافت كتاب از Google books


پيرو مطلب "آيا نمودارهاي UML هنوز هم استفاده‌ي صنعتي گسترده‌اي دارند يا خير؟" در كامنت‌هاي اين مطلب، اكثرا عنوان مي‌كردند كه از CRC cards استفاده مي‌كنند. كتاب معروف CRC cards هم به نام The CRC card book مربوط به سال 1997 است و در كتاب فروشي‌هاي اين دور و اطراف يافت نشد (يا حداقل من نيافتم). اما اين كتاب در Google books موجود است.

برنامه رايگان و سورس بازي براي اين منظور در CodePlex موجود است كه پس از دريافت آدرس كتاب، كل آن‌را از Google books دريافت كرده و يك خروجي pdf و يا تصويري ارائه مي‌دهد.



آنچنان سرعت بالايي براي دريافت يك كتاب ندارد اما كار مي‌كند (براي دريافت يك كتاب شايد نصف روز معطل شويد).

۱۳۸۸/۰۶/۳۰

اعمال متداول با select (يا همان DropDownList / ComboBox) توسط jQuery


در اين مقاله مروري خواهيم داشت بر نحوه‌ي انجام يك سري از اعمال متداول با استفاده از jQuery بر روي select استاندارد HTML. قبل از شروع به عنوان قالب كار، صفحه ساده‌ي زير را در نظر بگيريد كه از يك DropDownList استاندارد ASP.Net تشكيل شده است:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="select.aspx.cs" Inherits="TestJQueryAjax.select" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="js/jquery.js" type="text/javascript"></script>

<script type="text/javascript">
//...
</script>

</head>
<body>
<form id="form1" runat="server">
<asp:DropDownList ID="ddlTest" runat="server">
<asp:ListItem Value="1">آيتم يك</asp:ListItem>
<asp:ListItem Value="2">آيتم دو</asp:ListItem>
<asp:ListItem Value="3">آيتم سه</asp:ListItem>
<asp:ListItem Value="4">آيتم چهار</asp:ListItem>
<asp:ListItem Value="5">آيتم پنج</asp:ListItem>
</asp:DropDownList>
</form>
</body>
</html>
1) بدست آوردن value آيتم انتخاب شده

$("#<%=ddlTest.ClientID %>").val()
2) بدست آوردن text آيتم انتخاب شده

$("#<%=ddlTest.ClientID %> option:selected").text()
3) عكس العمل نشان دادن به انتخاب آيتم‌هاي مختلف

<script type="text/javascript">
$(document).ready(function() {
$("#<%=ddlTest.ClientID %>").change(function() {
alert($("#<%=ddlTest.ClientID %>").val());
});
});
</script>
4) انتخاب يك آيتم از طريق برنامه نويسي

$("#<%=ddlTest.ClientID %>").val(2);
5) افزودن يك آيتم به ليست از طريق برنامه نويسي

$("<option value='6'>آيتم شش</option>").appendTo("#<%=ddlTest.ClientID %>");

6) خالي و حذف كردن تمامي آيتم‌ها

$("#<%=ddlTest.ClientID %>").html("");

و براي تكميل بحث مي‌توان به اين برگه مرجع مراجعه كرد:
jQuery – Select element cheat sheet


۱۳۸۸/۰۶/۲۹

معرفي افزونه‌ي WhySharper


ReSharper جهت بهبود كيفيت كد‌هاي نوشته شده راهنمايي‌هاي مختلفي را ارائه مي‌دهد. اما اكثر اين‌ها مختصر و مفيد و خلاصه هستند. براي مثال اين متد بهتر است استاتيك شود يا اين متغير بهتر است readonly شود و الي آخر. اما چرا؟
براي رفع اين نقيصه، افزونه‌اي براي ReSharper تهيه شده است به نام WhySharper كه دلايل راهنمايي‌هاي ارائه شده از طرف افزونه‌ي اصلي را نيز بيان مي‌كند (لينكي را به سايت stackoverflow جهت مشاهده بحث مربوطه ارائه مي‌دهد).



اين افزونه از گوگل كد و يا رپيدشير قابل دريافت است.

متاسفانه امكان استفاده از گوگل كد وجود ندارد و اين افزونه، فايل بررسي نگارش و همچنين پيشنهادات خود را از گوگل‌كد مي‌خواند كه سبب عدم بارگذاري آن خواهد شد. براي رفع اين مشكل، مسير زير را پس از نصب پيدا كنيد:

C:\Documents and Settings\UserName\Application Data\JetBrains\WhySharper

سپس دو فايل زير را در آن مسير كپي نمائيد:
http://whysharper.googlecode.com/svn/trunk/WhySharper/SuggestionsVersion.txt
http://whysharper.googlecode.com/svn/trunk/WhySharper/Suggestions.xml

دريافت اين دو فايل

۱۳۸۸/۰۶/۲۸

بررسي واژه كليدي static


تفاوت بين يك كلاس استاتيك، متدي استاتيك و يا متغير عضو استاتيك چيست؟ چه زماني بايد از آن‌ها‌ استفاده كرد و لزوم بودن آن‌ها‌ چيست؟
براي پاسخ دادن به اين سؤالات بايد از نحوه‌ي تقسيم بندي حافظه شروع كرد.
RAM براي هر نوع پروسه‌اي كه در آن بارگذاري مي‌شود به سه قسمت تقسيم مي‌گردد: Stack ، Heap و Static (استاتيك در دات نت در حقيقت قسمتي از Heap است كه به آن High Frequency Heap نيز گفته مي‌شود).
اين قسمت استاتيك حافظه، محل نگهداري متدها و متغيرهاي استاتيك است. آن متدها و يا متغيرهايي كه نياز به وهله‌اي از كلاس براي ايجاد ندارند، به صورت استاتيك ايجاد مي‌گردند. در سي شارپ از واژه كليدي static براي معرفي آن‌ها كمك گرفته مي‌شود. براي مثال:

class MyClass
{
public static int a;
public static void DoSomething();
}
در اين مثال براي فراخواني متد DoSomething نيازي به ايجاد يك وهله جديد از كلاس MyClass نمي‌باشد و تنها كافي است بنويسيم:

MyClass.DoSomething(); // and not -> new MyClass().DoSomething();
نكته‌ي مهمي كه در اينجا وجود دارد اين است كه متدهاي استاتيك تنها قادر به استفاده از متغيرهاي استاتيك تعريف شده در سطح كلاس هستند. علت چيست؟
به مثال زير دقت نمائيد:

class MyClass
{
// non-static instance member variable
private int a;
//static member variable
private static int b;
//static method
public static void DoSomething()
{
//this will result in compilation error as “a” has no memory
a = a + 1;
//this works fine since “b” is static
b = b + 1;
}
}
در اين مثال اگر متد DoSomething را فراخواني كنيم، تنها متغير b تعريف شده، در حافظه حضور داشته (به دليل استاتيك معرفي شدن) و چون با روش فراخواني MyClass.DoSomething هنوز وهله‌اي از كلاس مذكور ايجاد نشده، به متغير a نيز حافظه‌اي اختصاص داده نشده است و نامعين مي‌باشد.
بر اين اساس كامپايلر نيز از كامپايل شدن اين كد جلوگيري كرده و خطاي لازم را گوشزد خواهد كرد.

اكنون تعريف يك كلاس به صورت استاتيك چه اثري را خواهد داشت؟
با تعريف يك كلاس به صورت استاتيك مشخص خواهيم كرد كه اين كلاس تنها حاوي متدها و متغيرهاي استاتيك مي‌باشد. امكان ايجاد يك وهله از آن‌ها وجود نداشته و نيازي نيز به اين امر ندارند. اين كلاس‌ها امكان داشتن instance variables را نداشته و به صورت پيش فرض از نوع sealed به حساب خواهند آمد و امكان ارث بري از آن‌ها نيز وجود ندارد. علت اين امر هم اين است كه يك كلاس static هيچ نوع رفتاري را تعريف نمي‌كند.

پس با اين تفاسير چرا نياز به يك كلاس static ممكن است وجود داشته باشد؟
همانطور كه عنوان شد يك كلاس استاتيك هيچ نوع رفتاري را تعريف نمي‌كند بنابراين بهترين مكان است براي تعريف متدهاي كمكي كه به ساير اعضاي كلاس‌هاي ما وابستگي نداشته، عمومي بوده، مستقل و متكي به خود هستند. عموما متدهاي كمكي در يك برنامه به صورت مكرر فراخواني شده و نياز است تا به سرعت در دسترس قرار داشته باشند و حداقل يك مرحله ايجاد وهله كلاس در اينجا براي راندمان بيشتر حذف گردد.
براي مثال متدي را در نظر بگيريد كه بجز اعداد، ساير حروف يك رشته را حذف مي‌كند. اين متد عمومي است، وابستگي به ساير اعضاي يك كلاس يا كلاس‌هاي ديگر ندارد. بنابراين در گروه متدهاي كمكي قرار مي‌گيرد. اگر از افزونه‌ي ReSharper‌ استفاده نمائيد، اين نوع متدها را به صورت خودكار تشخيص داده و راهنمايي لازم را جهت تبديل آ‌ن‌ها به متد‌هاي استاتيك ارائه خواهد داد.

با كلاس‌هاي استاتيك نيز همانند ساير كلاس‌هاي يك برنامه توسط JIT compiler رفتار مي‌شود، اما با يك تفاوت. كلاس‌هاي استاتيك فقط يكبار هنگام اولين دسترسي به آن‌ها ساخته شده و در قسمت High Frequency Heap حافظه قرار مي‌گيرند. اين قسمت از حافظه تا پايان كار برنامه از دست garbage collector‌ در امان است (بر خلاف garbage-collected heap‌ يا object heap كه جهت instance classes مورد استفاده قرار مي‌گيرد)


نكته:
در برنامه‌هاي ASP.Net از بكارگيري متغيرهاي عمومي استاتيك برحذر باشيد (از static fields و نه static methods). اين متغيرها بين تمامي كاربران همزمان يك برنامه به اشتراك گذاشته شده و همچنين بايد مباحث قفل‌گذاري و امثال آن‌را در محيط‌هاي چند ريسماني هنگام كار با آن‌ها رعايت كرد (thread safe نيستند).

۱۳۸۸/۰۶/۲۶

آموزش رايگان XAML از مايكروسافت


يك دوره آموزشي رايگان XAML اخيرا از طرف مايكروسافت ارائه شده است كه از طريق آدرس زير قابل دسترسي است:


اين كلينيك آموزشي شامل موارد زير است:
  • Navigation Overview
  • Clinic Information
  • Introduction to XAML
  • Overview of XAML
  • Why XAML?
  • XAML Layouts
  • Module Summary
  • XAML and WPF In Action
  • XAML in a Browser
  • Using XAML and code-behind in Desktop Applications
  • Module Summary
  • Unique Features of XAML
  • Resources
  • Styles and ControlTemplates
  • Module Summary
  • Glossary

اين ماژول به صورت آفلاين نيز قابل دريافت است (به حجم 44 مگابايت) اما پيش از آن بايد برنامه offline player آن‌را نصب نمود و طبق روال معمول سايت مايكروسافت، بهتر است از IE جهت مرور اين صفحات استفاده كرد.

۱۳۸۸/۰۶/۲۵

كامپايل پوياي كد در دات نت


در دات نت فريم ورك امكان كامپايل پوياي يك قطعه كد دريافت شده از يك رشته، توسط فضاي نام CodeDom مهيا است كه قدرت قابل توجهي را در اختيار برنامه نويس قرار مي‌دهد.

مثال يك:
رشته زير را كامپايل كرده و تبديل به يك فايل exe كنيد:

string source =
@"
namespace Foo
{
public class Bar
{
static void Main(string[] args)
{
Bar.SayHello();
}

public static void SayHello()
{
System.Console.WriteLine(""Hello World"");
}
}
}
";
روش انجام كار به همراه توضيحات مربوطه به صورت كامنت:

using System;
using System.Collections.Generic;
//دو فضاي نامي كه براي اين منظور اضافه شده‌اند
using Microsoft.CSharp;
using System.CodeDom.Compiler;

namespace compilerTest
{
class Program
{
static void compileIt1()
{
//سورس كد ما جهت كامپايل
string source =
@"
namespace Foo
{
public class Bar
{
static void Main(string[] args)
{
Bar.SayHello();
}

public static void SayHello()
{
System.Console.WriteLine(""Hello World"");
}
}
}
";

//تعيين نگارش كامپايلر مورد استفاده
Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
{"CompilerVersion", "v3.5"}
};
//تعيين اينكه كد ما سي شارپ است
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);

//تعيين اينكه خروجي يك فايل اجرايي است بعلاوه مشخص سازي محل ذخيره سازي فايل نهايي
CompilerParameters compilerParams = new CompilerParameters
{
OutputAssembly = "D:\\Foo.EXE",
GenerateExecutable = true
};

//عمليات كامپايل در اينجا صورت مي‌گيرد
CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);

//اگر خطايي وجود داشته باشد نمايش داده خواهد شد
Console.WriteLine("Number of Errors: {0}", results.Errors.Count);
foreach (CompilerError err in results.Errors)
{
Console.WriteLine("ERROR {0}", err.ErrorText);
}
}

static void Main(string[] args)
{
compileIt1();

Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}
مثال 2:
كد مورد نظر را به صورت يك فايل dll كامپايل كنيد.
براي اين منظور تمامي مراحل مانند قبل است فقط GenerateExecutable ذكر شده به false تنظيم شده و نام خروجي نيز به foo.dll بايد تنظيم شود.


مثال 3:
كد مورد نظر را در حافظه كامپايل كرده (خروجي dll يا exe نمي‌خواهيم)، سپس متد SayHello آن را به صورت پويا فراخواني نموده و خروجي را نمايش دهيد.
در اين حالت روش كار همانند مثال 1 است با اين تفاوت كه GenerateInMemory = true و GenerateExecutable = false تنظيم مي‌شوند. همچنين جهت دسترسي به متد كلاس ذكر شده،‌ از قابليت‌هاي ريفلكشن موجود در دات نت فريم ورك استفاده خواهد شد.

using System;
using System.Collections.Generic;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;

namespace compilerTest
{
class Program
{
static void compileIt2()
{
//سورس كد ما جهت كامپايل
string source =
@"
namespace Foo
{
public class Bar
{
static void Main(string[] args)
{
Bar.SayHello();
}

public static void SayHello()
{
System.Console.WriteLine(""Hello World"");
}
}
}
";

//تعيين نگارش كامپايلر مورد استفاده
Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
{"CompilerVersion", "v3.5"}
};
//تعيين اينكه كد ما سي شارپ است
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);

//نحوه تعيين مشخص سازي كامپايل در حافظه
CompilerParameters compilerParams = new CompilerParameters
{
GenerateInMemory = true,
GenerateExecutable = false
};

//عمليات كامپايل در اينجا صورت مي‌گيرد
CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);

// اگر خطايي در كامپايل وجود نداشت متد دلخواه را فراخواني مي‌كنيم
if (results.Errors.Count == 0)
{
//استفاده از ريفلكشن براي دسترسي به متد و فراخواني آن
Type type = results.CompiledAssembly.GetType("Foo.Bar");
MethodInfo method = type.GetMethod("SayHello");
method.Invoke(null, null);
}
}


static void Main(string[] args)
{
compileIt2();

Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}
نكته: نحوه‌ي استفاده از اسمبلي‌هاي ديگر در رشته سورس كد خود
مثال:
اگر رشته سورس ما به صورت زير بوده و از اسمبلي System.Drawing.Dll نيز كمك گرفته باشد،‌

string source =
@"
namespace Foo
{

public class Bar
{
static void Main(string[] args)
{
Bar.SayHello();
}

public static void SayHello()
{
System.Console.WriteLine(""Hello World"");
var r = new System.Drawing.Rectangle(0,0,100,100);
System.Console.WriteLine(r);
}
}
}
";
هنگام كامپايل آن توسط روش مثال يك، با خطاي زير مواجه خواهيم شد.

Number of Errors: 1
ERROR The type or namespace name 'Drawing' does not exist in the namespace 'System' (are you missing an assembly reference?)

براي رفع اين مشكل و معرفي اين اسمبلي،‌ سطر زير بايد پس از تعريف compilerParams اضافه شود.

compilerParams.ReferencedAssemblies.Add("System.Drawing.Dll");
اكنون كد كامپايل شده و مشكلي نخواهد داشت.
نمونه‌اي ديگر از اين دست، استفاده از LINQ مي‌باشد. در اين حالت اسمبلي System.Core.Dll نيز به روش ذكر شده بايد معرفي گردد تا مشكلي در كامپايل كد رخ ندهد.


كاربردها:
1- استفاده در ابزارهاي توليد كد (براي مثال در برنامه Linqer از اين قابليت استفاده مي‌شود)
2- استفاده‌هاي امنيتي (ايجاد روش‌هاي توليد يك سريال به صورت پويا و كامپايل پوياي كد مربوطه در حافظه‌اي محافظت شده)
3- استفاده جهت مقاصد محاسباتي پيشرفته
4- دادن اجازه‌ي كد نويسي به كاربران برنامه‌ي خود (شبيه به سيستم‌هاي ماكرو و اسكريپت نويسي موجود)
و ...

۱۳۸۸/۰۶/۲۴

FxCop براي SQL Server


حتما با FxCop كه براي آناليز اسمبلي‌هاي برنامه‌هاي دات نتي بكار مي‌رود آشنايي داريد. شبيه به اين مورد به صورت افزونه‌اي براي Visual studio 2008 team system نيز موجود است. فقط كافي است Microsoft® Visual Studio Team System 2008 Database Edition GDR R2 را نصب كرده و يك پروژه ديتابيس جديد را شروع كنيد (نوع database wizard كه يك ديتابيس كامل را import مي‌كند). سپس در برگه build تيك مربوط به code analysis را قرار دهيد (شكل 1) و يكبار پروژه را build كنيد. به اين صورت در پنجره خروجي، اشكالات كدهاي T-SQL شما گوشزد مي‌شود (شكل 3). اين‌كار را با استفاده از منوي Data نيز مي‌توان انجام داد (شكل 2).

شكل 1- فعال سازي تحليل و بررسي كد

شكل 2- اجراي تحليل و بررسي كد


شكل 3- يك نمونه خروجي حاصل از تحليل و بررسي كد


۱۳۸۸/۰۶/۲۳

شبيه ساز ميل سرور براي برنامه نويس‌ها


مطلبي را در مورد شبيه سازي ارسال ايميل جهت بررسي خروجي واقعي يك برنامه قبلا نوشته بودم. در تكميل اين مبحث، برنامه رايگان و سورس بازي به نام Antix SMTP Server for Developers نيز وجود دارد كه از آدرس زير قابل دريافت است:


اين برنامه به صورت يك پروسه پس زمينه اجرا شده و توانايي‌هاي يك SMTP Server واقعي را شبيه سازي مي‌كند؛ بدون اينكه ايميلي را ارسال نمايد. پس از اجرا، منتظر دريافت ايميل‌هاي ارسالي از طريق SMTP Client برنامه‌ي شما شده و پس از دريافت ايميل‌ها، آن‌ها را در پوشه‌اي مشخص ذخيره مي‌كند. همچنين توسط اين برنامه مي‌توان عنوان ايميل‌هاي ارسالي را نيز مشاهده نمود (مزيت اصلي نسبت به روش قبلي معرفي شده). با دوبار كليك بر روي ايميل‌هاي ليست شده، مي‌توان آن‌ها را در mail client نصب شده مانند آوت لوك، مشاهده نمود. به اين صورت يك برنامه نويس مي‌تواند متن و فرمت ايميل‌هاي ارسالي توسط برنامه خود را پيش از بكارگيري آن در يك محيط واقعي كاري، كاملا بررسي و آزمايش نمايد. بديهي است كه اين برنامه حتي مي‌تواند بر روي كامپيوتري ديگر در شبكه نيز قرار داشته باشد. همچنين با توجه به نحوه‌ي توزيع ClickOnce اين برنامه، هر بار كه بسته شود، بررسي خواهد كرد كه آيا نگارش جديدتري از آن آماده شده است يا خير (اگر نصاب ClickOnce آن را دريافت و نصب كنيد).


اگر از دات نت فريم ورك استفاده مي‌كنيد، جهت استفاده از اين شبيه ساز كافي است app.config و يا web.config برنامه شما به صورت زير تنظيم شده باشد:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.net>
<mailSettings>
<smtp>
<network port="25" host="127.0.0.1"/>
</smtp>
</mailSettings>
</system.net>
</configuration>

پ.ن.
همانطور كه در تصوير مشخص است اين برنامه قادر به تفسير عنوان ايميل فارسي نيست (اولين عنوان بررسي شده فارسي است). اگر وقت كرديد در اين پروژه سورس باز شركت كنيد و نكته زير را به آن اعمال نمائيد (زيبايي يك كار سورس باز ...):
رمزگشايي عنوان يك ايميل فارسي دريافت شده

۱۳۸۸/۰۶/۲۱

Optimize for unknown


مفهومي در SQL Server وجود دارد به نام parameter sniffing كه شرح آن به صورت زير است.
ابتدا رويه ذخيره شده زير را در نظر بگيريد:

create procedure test (@pid int)
as
select * from Sales.SalesOrderDetail
where ProductID = @pid
استفاده از كوئري‌هاي پارامتري يكي از بهترين تمرين‌هاي كاري با SQL server است؛ از آنجائيكه در اين حالت plan تهيه شده مجددا مورد استفاده قرار گرفته، همچنين از SQL injection نيز جلوگيري خواهد كرد، زيرا براي نمونه در مثال فوق تنها pid از نوع int پذيرفته مي‌شود و نه هر ورودي خطرناك ديگري.
اما اين نوع كوئري‌ها يك مشكل را نيز به همراه خود دارند. اين plan تهيه شده به ازاي اولين ورودي رويه ذخيره شده تهيه مي‌شود (parameter sniffing) و الزامي ندارد كه براي دومين ورودي و فراخواني‌هاي بعدي، بهترين plan باشد.

براي حل اين مشكل راه‌هاي زيادي هست:
الف) انتساب پارامترهاي يك رويه ذخيره شده به متغيري محلي

create procedure test (@pid int)
as
Declare @mpid int
Set @mpid = @pid
select * from Sales.SalesOrderDetail
where ProductID = @mpid
در اينجا پارامتر ورودي مستقيما در كوئري استفاده نشده است و SQL Server اين متغير محلي را sniff نخواهد كرد.

ب) استفاده از گزينه RECOMPILE كه سبب خواهد شد به ازاي هر ورودي يك plan بهينه تهيه شود. اين مورد مصرف CPU بالايي را به همراه خواهد داشت.

ج) راه حل ارائه شده در SQL Server 2005
استفاده از روش الف به علاوه اضافه كردن گزينه كمكي زير به انتهاي اسكريپت فوق

OPTION (OPTIMIZE FOR(@pid = 544))

در اينجا فرض بر اين است كه مي‌دانيم pid=544 بسيار مورد استفاده قرار خواهد گرفت، بنابراين اين معرفي را به موتور بهينه ساز SQL Server ارائه خواهيم كرد.

د) راه حل ارائه شده در SQL Server 2008
با استفاده از Optimize for unknown كه در اس كيوال سرور 2008 معرفي شده است، مزيت استفاده از كوئري‌هاي پارامتري همانند استفاده مجدد از plan تهيه شده، حفظ گشته اما اين plan‌ تهيه شده اوليه بر اساس اولين مقدار پاس شده، تهيه نگرديده و حالت عمومي‌تر و بهينه‌تري را براي اكثر مقادير پاس شده خواهد داشت.

create procedure test (@pid int)
as
select * from Sales.SalesOrderDetail
where ProductID = @pid

OPTION(OPTIMIZE FOR (@pid UNKNOWN))

جهت مطالعه بيشتر (+ و + و +)

۱۳۸۸/۰۶/۲۰

استفاده‌ي همزمان از آپديت پنل ASP.Net و پلاگين‌هاي جي‌كوئري


مشكل: زمانيكه يك AsyncPostback در آپديت پنلASP.Net Ajax رخ دهد، پس از پايان كار، پلاگين جي‌كوئري كه در حال استفاده از آن بوديد و در هنگام بارگذاري اوليه صفحه بسيار خوب كار مي‌كرد، اكنون از كار افتاده است و ديگر جواب نمي‌دهد.

قبل از شروع، نياز به يك سري پيش زمينه هست (شايد بر اساس روش استفاده شما از آن پلاگين جي‌كوئري، مشكل را حل كنند):
الف) رفع تداخل جي‌كوئري با ساير كتابخانه‌هاي مشابه.
ب) آشنايي با jQuery Live جهت بايند رخ‌داد‌ها به عناصري كه بعدا به صفحه اضافه خواهند شد.
ج) تزريق اسكريپت به صفحه در حين يك AsyncPostback رخ داده در آپديت پنل

علت بروز مشكل:
علت رخ‌دادن اين مشكل (علاوه بر قسمت الف ذكر شده)، عدم فراخواني document.ready تعريف شده، جهت بايند مجدد پلاگين jQuery مورد استفاده شما پس از هر AsyncPostback رخ داده در آپديت پنل ASP.Net Ajax است. راه حل استاندارد جي‌كوئري هم همان مورد (ب) فوق مي‌باشد، اما ممكن است جهت استفاده از آن نياز به بازنويسي يك پلاگين موجود خاص وجود داشته باشد، كه آنچنان مقرون به صرفه نيست.

مثالي جهت مشاهده‌ي اين مشكل در عمل:
مي‌خواهيم افزونه‌ي Colorize - jQuery Table را به يك گريد ويوو ASP.Net قرار گرفته درون يك آپديت پنل اعمال كنيم.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UpdatePanelTest.aspx.cs"
Inherits="TestJQueryAjax.UpdatePanelTest" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="sm" runat="server">
<Scripts>
<asp:ScriptReference Path="~/js/jquery.js" />
<asp:ScriptReference Path="~/js/jquery.colorize-1.6.0.js" />
</Scripts>
</asp:ScriptManager>
<asp:UpdatePanel ID="uppnl" runat="server">
<ContentTemplate>
<asp:GridView ID="GridView1" runat="server" AllowPaging="True"
OnPageIndexChanging="GridView1_PageIndexChanging">
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
</form>

<script type="text/javascript">
$(document).ready(function() {
$('#<%=GridView1.ClientID %>').colorize();
});
</script>
</body>
</html>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace TestJQueryAjax
{
public partial class UpdatePanelTest : System.Web.UI.Page
{
void BindTo()
{
List<string> rows = new List<string>();
for (int i = 0; i < 1000; i++)
rows.Add(string.Format("row{0}", i));

GridView1.DataSource = rows;
GridView1.DataBind();
}

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
BindTo();
}
}

protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
GridView1.PageIndex = e.NewPageIndex;
BindTo();
}
}
}
مثال بسيار ساده‌اي است جهت اعمال اين افرونه به يك گريدويو و مشاهده كار كردن اين افزونه در هنگام بارگذاري اوليه صفحه و سپس از كار افتادن آن پس از مشاهده صفحه دوم گريد. در اين مثال از نكته "اسكريپت‌هاي خود را يكي كنيد" استفاده شده است.

راه حل:
از ويژگي‌هاي ذاتي ASP.Net Ajax بايد كمك گرفت براي مثال:

<script type="text/javascript">
$(document).ready(function() {
$('#<%=GridView1.ClientID %>').colorize();
});

function pageLoad(sender, args) {
if (args.get_isPartialLoad()) {
$('#<%=GridView1.ClientID %>').colorize();
}
}
</script>

متد استاندارد pageLoad به صورت خودكار پس از هر AsyncPostback رخ داده در آپديت پنل ASP.Net Ajax فراخواني مي‌شود (و همچنين پس از پايان پردازش و بارگذاري اوليه DOM صفحه). در اين متد بررسي مي‌كنيم كه آيا يك partial postback رخ داده است؟ اگر بله، مجددا عمليات بايند افزونه به گريد را انجام مي‌دهيم و مشكل برطرف خواهد شد.

براي مطالعه بيشتر

۱۳۸۸/۰۶/۱۸

Eazfuscator 2.6 منتشر شد


Eazfuscator يكي از برنامه‌هاي با كيفيت code obfuscation مخصوص دات نت فريم ورك است. اين برنامه رايگان بوده و استفاده از آن به سادگي drag & drop فايل dll يا exe برنامه خود بر روي پنجره آن مي‌باشد (يا استفاده از آن از طريق خط فرمان جهت اتوماسيون اين‌كار)


ويژگي‌هاي آن:
Easy to use as 1-2-3
Automatic code protection with variety of supported obfuscation techniques:
  • Symbol renaming
  • String encryption
  • Constant literals pruning
  • Method signatures overload induction
  • Class hierarchy linerization
  • Code control flow obfuscation
  • Assemblies merging
Automatic code optimization
Supports .NET Framework versions 2.0, 3.0 and 3.5
Supports .NET Compact Framework versions 2.0 and 3.5
Supports Silverlight assemblies and XAP packages
Supports XNA applications for Windows, Xbox 360 and Zune platforms
Can obfuscate any 100% managed .NET assembly
Provides revolutionally innovative and easy to use GUI interface as well as classical command line interface
Microsoft Visual Studio integration. Supported versions are Microsoft Visual Studio 2005 and 2008 including Express editions
Supports automatic builds




پ.ن.
بنابر تجربه شخصي با اين ابزارها (تجاري و غيرتجاري)، اين تنها برنامه‌اي است كه جهت code obfuscation اسمبلي‌هاي ASP.Net در محيط كاري مشكل ساز نشده و سايت پس از مدتي با پيغام‌هاي عجيب و غريب از كار نمي‌افتد.

۱۳۸۸/۰۶/۱۷

گزينه "مرا به خاطر بسپار" درست كار نمي‌كند


حالت forms authentication در ASP.Net ، امكان تعريف كوكي‌هايي ماندگار را نيز جهت ورود خودكار كاربران در دفعات بعدي بازديد آن‌ها فراهم مي‌كند. اما زمان منقضي شدن اين كوكي‌هاي ماندگار در ASP.Net 1.1 و ASP.Net 2.0 به بعد كاملا با هم متفاوت بوده و اگر برنامه نويس از اين تغيير حاصل شده مطلع نباشد ممكن است بارها و بارها برنامه را آزمايش كند اما نتيجه‌اي نگيرد.
مدت زمان منقضي شدن كوكي‌هاي ماندگار forms authentication در ASP.Net 1.1 به صورت زير است (بود):
زمان منقضي شدن كوكي سشن = زمان جاري + زمان منقضي شدن سشن كاربر
زمان منقضي شدن كوكي ماندگار (persistent) = زمان جاري + 50 سال

اين عدد 50 سال بسيار غيرمنطقي بوده و در ASP.Net 2.0 به بعد به صورت زير اصلاح شده است:
زمان منقضي شدن كوكي سشن = زمان جاري + زمان منقضي شدن سشن كاربر
زمان منقضي شدن كوكي ماندگار (persistent) = زمان جاري + زمان منقضي شدن سشن كاربر (پيش فرض آن 30 دقيقه است)

و جهت بيشتر كردن طول عمر اين ماندگاري بايد زمان مورد نظر را در وب كانفيگ به صورت زير اعمال كرد:

<authentication mode="Forms">
<forms

name="MyCookieName"
slidingExpiration="true"
timeout="43200"
/>
</authentication>
مطابق زمان تنظيم شده فوق كه بر حسب دقيقه است، اين كوكي به مدت يك ماه ماندگاري پيدا مي‌كند در غير اينصورت اعمال گزينه "مرا به خاطر بسپار" مطابق نظر شما كار نخواهد كرد و همانند ASP.Net 1.1 به صورت پيش فرض به 50 سال تنظيم نمي‌شود.

همچنين نكته‌ي مهم ديگري را كه بايد رعايت كرد، name ايي است كه در اين فايل config عنوان مي‌‌كنيد (در قسمت تنظيمات form authentication). اگر بر روي يك وب سرور، چندين برنامه وب ASP.Net را در حال اجرا داريد، بايد براي هر كدام از اين‌ها نامي جداگانه و منحصربفرد انتخاب كنيد، در غيراينصورت تداخل رخ داده و باز هم گزينه مرا به خاطر بسپار شما كار نخواهد كرد.

كار slidingExpiration كه در اينجا تنظيم شده است نيز به صورت زير مي‌باشد:
اگر لاگين موفقيت آميزي ساعت 5 عصر صورت گيرد و timeout شما به عدد 10 تنظيم شده باشد، اين لاگين به صورت خودكار در 5:10‌ منقضي خواهد شد. اما اگر در اين حين در ساعت 5:05 ، كاربر يكي از صفحات سايت شما را مرور كند، زمان منقضي شدن كوكي ذكر شده به 5:15 تنظيم خواهد شد. (مفهوم تنظيم slidingExpiration)
لازم به ذكر است كه اگر كاربر پيش از نصف زمان منقضي شدن كوكي (مثلا در 5:04)، يكي از صفحات را مرور كند، تغييري در اين زمان نهايي منقضي شدن رخ نخواهد داد.

۱۳۸۸/۰۶/۱۵

طول و عرض WPF


شايد بد نباشد اين فناوري را از ديدگاه مدت زماني كه بايد به آن تسلط پيدا كرد، بررسي نمود:



بله، مشكل در طول و عرض WPF بوده و مدت زمان يادگيري و تسلط كامل به آن، از فناوري‌هاي قبلي مطرح در دات نت فريم ورك بسيار بيشتر مي‌باشد. (تعداد كلاس‌هاي آن بيشتر از مجموع تعداد كلاس‌هاي نگارش 2 WinForms و ASP.Net است!)

در مقايسه با WinForms و ASP.Net هم موارد زير قابل تامل است:
ASP.NET 2.0 شامل 1098 public types و 1551 classes است.
WinForms 2.0 شامل 777 public types و 1500 classes مي‌باشد.
سيلورلايت 2 را هم كه در تصوير مشاهده مي‌كنيد. شامل 376 public types و 335 classes است.

ماخذ

۱۳۸۸/۰۶/۱۴

Dependency Injection


در ادامه مباحث بهتر كد بنويسيم و الگوهايي كه در اين رابطه معرفي شدند، اخيرا كتابي از انتشارات manning منتشر شده تحت عنوان Dependency Injection . هر چند به ظاهر اين كتاب براي جاوا كارها تهيه شده اما قسمت عمده‌اي از آن براي ساير زبان‌هاي برنامه نويسي ديگر نيز قابل استفاده است.




DESCRIPTION
In object-oriented programming, a central program normally controls other objects in a module, library, or framework. With dependency injection, this pattern is inverted—a reference to a service is placed directly into the object which eases testing and modularity. Spring or Google Guice use dependency injection so you can focus on your core application and let the framework handle infrastructural concerns.
Dependency Injection explores the DI idiom in fine detail, with numerous practical examples that show you the payoffs. You'll apply key techniques in Spring and Guice and learn important pitfalls, corner-cases, and design patterns. Readers need a working knowledge of Java but no prior experience with DI is assumed.

WHAT'S INSIDE:
◊ How to apply it (Understand it first!)
◊ Design patterns and nuances
◊ Spring, Google Guice, PicoContainer, and more
◊ How to integrate DI with Java frameworks


راستي، اين كتاب تر و تازه رو مي‌تونيد از همين كتاب فروشي‌هاي دور و اطراف نيز تهيه كنيد! در سايت booktraining دات ارگ در قسمت graphics-and-design به تاريخ 4 آگوست.



۱۳۸۸/۰۶/۱۳

نحوه اعلام وجود وبلاگي!


گاهي از اوقات بعضي از وبلاگ‌ نويس‌ها را مي‌بينم كه مدتي تلاش مي‌كنند و بعد دست از كار مي‌كشند و خداحافظ. مهم‌ترين علت آن احساس عدم توجه از طرف خوانندگان است. اين عدم توجه هم از اين‌جا ناشي مي‌شود كه كسي از وجود آن‌ها خبري ندارد.
يك وبلاگ با موضوع علمي راه انداخته‌ايد؟ يك سايت در زمينه IT فراهم كرده‌ايد؟ لطفا به يكي از سايت‌هاي زير مراجعه كرده و اعلام وجود كنيد!


من اكثر سايت‌هاي مرتبط جديد را از همين طريق كشف مي‌كنم!


به روز رساني فيلدهاي XML در SQL Server


از SQL server 2005 به بعد، پشتيباني كاملي از XML توسط اين محصول صورت مي‌گيرد. در ادامه مروري خواهيم داشت بر نحوه‌ي به روز رساني مقادير فيلدهايي از نوع XML در SQL Server .
در ابتدا جدول موقتي زير را كه شامل يك ركورد از نوع XML است، در نظر بگيريد:

DECLARE @tblTest AS TABLE (xmlField XML)

INSERT INTO @tblTest
(
xmlField
)
VALUES
(
'<Sample>
<Node1>Value1</Node1>
<Node2>Value2</Node2>
<Node3>OldValue</Node3>
</Sample>'
)
مي‌خواهيم OldValue را به مقداري ديگر تغيير دهيم.

سعي اول:

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'
UPDATE @tblTest
SET xmlField.modify('replace value of (/Sample/Node3)[1] with ' + @newValue)
اين سعي با خطاي زير متوقف مي‌شود:

The argument 1 of the XML data type method "modify" must be a string literal.
بنابراين از روش String concatenation براي معرفي مقدار متغير مورد نظر در اينجا نمي‌شود استفاده كرد.

سعي دوم:

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3)[1] with sql:variable("@newValue")'
)
روش معرفي صحيح يك متغير را در اينجا مي‌توان مشاهده كرد. اما اين سعي نيز با خطاي زير متوقف مي‌شود:

XQuery [@tblTest.xmlField.modify()]: The target of 'replace value of' must be a non-metadata attribute or an element with simple typed content, found 'element(NodeThree,xdt:untyped) ?'

سعي سوم:

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3/text())[1]
with sql:variable("@newValue")'
)

SELECT xmlField.value('(/Sample/Node3)[1]','varchar(50)') FROM @tblTest

و بله. كار مي‌كنه!
XML ايي را كه در ابتدا استفاده كرديم از نوع un-typed XML محسوب شده و هيچ schema ايي را براي آن در نظر نگرفته‌ايم، به همين جهت بايد دقيقا مشخص كنيم كه قصد داريم text اين node را ويرايش نمائيم.

مشكل بعدي!
در ابتدا مثال زير را در نظر بگيريد:

DECLARE @tblTest AS TABLE (xmlField XML)

INSERT INTO @tblTest
(
xmlField
)
VALUES
(
'<Sample>
<Node1>Value1</Node1>
<Node2>Value2</Node2>
<Node3></Node3>
</Sample>'
)

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3/text())[1]
with sql:variable("@newValue")'
)

SELECT xmlField.value('(/Sample/Node3)[1]','varchar(50)') FROM @tblTest

اين عبارات T-SQL ، خلاصه بحث ما تا به اينجا هستند اما با يك تفاوت. نود 3 در اينجا خالي است.
اگر اسكريپت را اجرا كنيد، هيچ تغييري را مشاهده نخواهيد كرد. به عبارت ديگر به روز رساني صورت نمي‌گيرد. در اينجا چون text اين نود خالي است ، فرض SQL Server بر اين خواهد بود كه وجود ندارد، بنابراين اين نود را به روز رساني نخواهد كرد. به همين منظور بايد براي به روز رساني اين نود، عبارت جديد را در جايي كه text ندارد insert‌ كرد (و نه replace).

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3/text())[1]
with sql:variable("@newValue")'
)

UPDATE @tblTest
SET xmlField.modify(
'insert text{sql:variable("@newValue")} into
(/Sample/Node3)[1] [not(text())]'
)

SELECT xmlField.value('(/Sample/Node3)[1]','varchar(50)') FROM @tblTest

۱۳۸۸/۰۶/۱۱

استفاده از jQuery يا‌ MS Ajax control toolkit


به نظر من jQuery به چندين دليل از كتابخانه MS Ajax و ملحقات آن مهم‌تر است و بايد به آن پرداخته شود:

1- دانش شما قابل انتقال است. اگر روزي به PHP يا JSP يا موارد مشابه ديگري مهاجرت كرديد، دانش jQuery شما باز هم قابل استفاده خواهد بود.
2- اين كتابخانه بسيار سبك‌تر و كم حجم تر از MS Ajax است. (حجم آن كمتر از نصف است(+))
3- تقريبا از اكثر فريم ورك‌هاي جاوا اسكريپتي موجود سريع‌تر است. (+)
4- پشتيباني آن از مرورگرهاي مختلف بي‌نظير است. لازم نيست عمر گرانمايه را صرف اين كنيد كه چرا اين اسكريپت در فايرفاكس كار مي‌كنه اما در IE خير.
5- كوهي از افزونه‌هاي مختلف براي آن موجود است. (jQuery plugin را در گوگل جستجو كنيد)
6- استفاده از آن، ميزان كد جاوا اسكريپتي را كه بايد نوشت تا حد بسيار قابل ملاحظه‌اي كاهش مي‌دهد، كه در نهايت سبب ايجاد كدي خواناتر در مدت زماني كمتر مي‌گردد.
7- كدنويسي با jQuery از كد نويسي JavaScript ايي خالص بسيار ساده‌تر است. افرادي كه تا ديروز حتي طرف جاوا اسكريپت هم نمي‌رفتند، امروزه پلاگين‌هاي jQuery مي‌نويسند.
8- مستندات كاملي داشته به همراه مثال‌هايي بسيار كاربردي.
9- به دليل استفاده شدن از آن در پلتفرم‌هاي مختلف، مثال‌هاي كاربردي بيشتري داشته، همچنين منابع آموزشي پر محتواتري را نيز مي‌توان در اين رابطه يافت. (مي‌توان tutorial هاي مربوط به php را مشاهده و آن‌ها را به ASP.Net تبديل كرد و صرفا منحصر به يك عده و يك پلتفرم نيست)
10- خلاقيت شما را نكشته و از شما فردي كه هيچگونه دركي نسبت به عمليات اجكسي رخ داده ندارد، نخواهد ساخت. (MS Ajax براي ASP.Net يك شاهكار است. چند عدد محصور كننده را استفاده كنيد، برنامه نويسي شما با روش سابق "هيچ" تفاوتي نخواهد داشت. مسلما اين توانمندي تيم مايكروسافت را نمايش مي‌دهد، نه توانمندي من و شما را! اما اگر نياز به كمي ابتكار وجود داشت اينجا است كه گير خواهيد افتاد و موارد 6 و 7 و 9 ذكر شده، jQuery را بر اين كتابخانه مقدم خواهد ساخت)
11- با آمدن ASP.Net MVC تمايل به استفاده از jQuery به شدت افزايش يافته كه نمود آن‌را در وبلاگ‌هاي مطرح در اين زمينه مي‌توان مشاهده كرد (اكثر مطالب ارسالي اين روزهاي بلاگ‌هاي ASP.Net حول و حوش ASP.Net MVC و jQuery است).

۱۳۸۸/۰۶/۱۰

ذخيره سازي فايل‌ها در ديتابيس يا استفاده از فايل سيستم متداول؟


اگر به ساز و كار شيرپوينت مايكروسافت دقت كنيد، همه چيز را داخل ديتابيس ذخيره مي‌كند (از اطلاعات ركوردها گرفته تا فايل‌ها و غيره). حال شايد اين سؤال مطرح شود كه براي ذخيره سازي فايل‌هايي با تعداد بيش از يك ميليون عدد، استفاده از ديتابيس مناسب است يا فايل سيستم متداول. براي پاسخ به اين سؤال بايد به نكات ذيل توجه داشت:

- هر نوع عملياتي كه بر روي فايل‌ها صورت گيرد، بستن، بازكردن و غيره، نيازمند اعمالي در سطح سيستم عامل است (براي مثال بررسي سطح دسترسي لازم براي انجام اين‌كارها).
- هر گونه عملياتي بر روي فايل‌ها نيازمند يك حداقل قفل گذاري بر روي آن‌ها است كه اين نيز مصرف CPU قابل توجهي را سبب خواهد شد.
- تمامي اعمال ذكر شده كل سرور و تمامي سرويس‌هاي در حال اجرا را تحت تاثير قرار داده و بازدهي آن‌ها‌را كاهش مي‌دهند.
- حتي سيستم عامل‌ها نيز از يك file system database جهت مديريت اعمال خود استفاده مي‌كنند اما اين روش براي مديريت ميليون‌ها و ميلياردها فايل بهينه سازي نشده است.
- ذخيره سازي ميليون‌ها و ميلياردها فايل به تدريج سبب ايجاد fragmentation قابل توجهي شده و اين مورد نيز بر روي كارآيي تاثير منفي خواهد گذاشت (همچنين اين مورد بر روي طول عمر تجهيزات ذخيره سازي داده‌ها تاثير منفي دارند).
- تهيه پشتيبان و بازگرداندن ميليون‌ها فايل بسيار زمانگير است (براي مثال جابجايي يك فايل يك مگابايتي بسيار سريعتر است از جابجايي 100 فايل 10 كيلوبايتي).
- مديريت تغييرات و همچنين بررسي اينكه چه شخصي چه فايلي را قرار داده، حذف كرده يا تغيير داده است در حالت استفاده از file system مشكل است.
- به صورت پيش فرض عموما مباحث replication و امثال آن‌ توسط روش استفاده از file system خصوصا با تعداد بالاي فايل، پشتيباني نمي‌شود.
- در حالت استفاده از file system ، برنامه‌هاي وب بايد دسترسي write بر روي يك سري پوشه داشته باشند كه اين مورد هميشه از ديدگاه امنيتي مساله ساز بوده و مشكل آفرين.
- كرش file system مساوي است با كرش سيستم عامل و بازگشت اين‌ها زمان‌بر خواهد بود.

با توجه به اين نكات استفاده از ديتابيس براي ذخيره سازي تعداد زيادي فايل، مزاياي زير را به همراه خواهد داشت:

- اكثر سيستم‌هاي ديتابيسي امروزي براي كار با حجم عظيمي از داده‌ها به حد بلوغ خود رسيده‌اند.
- هنگام استفاده از ديتابيس براي ذخيره سازي فايل‌ها ديگر سر و كار ما با ميليون‌ها فايل نخواهد بود و حداكثر چند فايل ديتابيس و ملحقات آن مانند لاگ فايل، كل سيستم را تشكيل مي‌دهند.
- فايل‌هاي ديتابيس براي مثال SQL Server ، هميشه توسط SQL Server در حالت باز قرار داشته و مباحث قفل‌گذاري بر روي فايل‌هاي ديتابيس و بررسي سطح دسترسي و غيره توسط سيستم عامل در اين‌جا به حداقل خود مي‌رسد.
- در اين حالت بار سيستم عامل شما تنها سيستمي است كه مشغول سرويس دهي اطلاعات ديتابيس‌هاي شما است.
- جستجوي فايل‌ها، حتي جستجو در محتواي اين فايل‌هاي ذخيره شده در يك ديتابيس بسيار سريعتر از روش file system مي‌باشد. امكان استفاده از كوئري‌هاي SQL انعطاف پذيري خاصي را به اين سيستم‌ها خواهند داد (براي مثال قابليت full text search مربوط به SQL server امكان جستجو بر روي ركوردهايي با محتواي pdf را نيز پس از انجام اندكي تنظيمات، دارا مي‌باشد).
- هنگام كار با ديتابيس مباحث تراكنشي نقش بسيار حائز اهميتي را بازي مي‌كنند اما عموما سيستم عامل‌ها در اين زمينه نيازمند كار و برنامه نويسي قابل توجهي هستند (اين قابليت به ويندوز ويستا اضافه شده است).
- كرش يك ديتابيس عموما سبب كرش سيستم عامل يا حتي كرش ساير ديتابيس‌هاي موجود نخواهد شد.
- امكان تهيه پشتيبان از ديتابيس‌ها و بازيابي آن‌ها ساده است. (حداقل از بازيابي ميليون‌ها فايل ساده‌تر است)
- امكانات replication به صورت پيش فرض در اكثر سيستم‌هاي ديتابيسي امروزي مهيا است.
- امكان ثبت وقايع و مديريت اطلاعات افزوده شده به ديتابيس، از طريق نرم افزارهايي كه براي اين كار نوشته خواهند شد (يا حتي امكانات توكار اين برنامه‌ها) از هر لحاظ نسبت به روش file system برتري دارد.
- امكانات سوئيچ كردن به ديتابيسي ديگر در شبكه در صورت كرش يك نود، مهيا است و پيش بيني شده است.
- براي استفاده از يك ديتابيس توسط يك برنامه وب، نيازي به داشتن دسترسي write بر روي هيچ فولدري وجود ندارد كه اين خود يك مزيت امنيتي مهم است و همچنين امكان محدود كردن سطوح دسترسي به فايل‌هاي ذخيره شده در ديتابيس با برنامه‌هاي نوشته شده نيز ساده‌تر است. (البته در اين‌جا مسلما منظور از ديتابيس، ديتابيس Access نيست و SQL Server يا MySQL مد نظر هستند)