نگاهی به آینده #C و Visual Basic
Learn how you can save time by creating your own reusable .NET Core templates in just a few steps.
Do you ever develop prototypes, or starter projects/accelerators, that you’d like to use again in the future? A good way to do that is by creating custom templates for dotnet. Once completed, anytime you want to create a new project of that type in the future, you can key in “dotnet new ” and you’re off, complete with correct namespaces. You can even do conditional checks, or variable replacements.
مباحث eager fetching/loading (واکشی حریصانه) و lazy loading/fetching (واکشی در صورت نیاز، با تاخیر، تنبل) جزو نکات کلیدی کار با ORM های پیشرفته بوده و در صورت عدم اطلاع از آنها و یا استفادهی ناصحیح از هر کدام، باید منتظر از کار افتادن زود هنگام سیستم در زیر بار چند کاربر همزمان بود. به همین جهت تصور اینکه "با استفاده از ORMs دیگر از فراگیری SQL راحت شدیم!" یا اینکه "به من چه که پشت صحنه چه اتفاقی میافته!" بسی مهلک و نادرست است!
در ادامه به تفصیل به این موضوع پرداخته خواهد شد.
ابزار مورد نیاز
در این مطلب از برنامهی NHProf استفاده خواهد شد.
اگر مطالب NHibernate این سایت را دنبال کرده باشید، در مورد لاگ کردن SQL تولیدی به اندازهی کافی توضیح داده شده یا حتی یک ماژول جمع و جور هم برای مصارف دم دستی نوشته شده است. این موارد شاید این ایده را به همراه داشته باشند که چقدر خوب میشد یک برنامهی جامعتر برای این نوع بررسیها تهیه میشد. حداقل SQL نهایی فرمت میشد (یعنی برنامه باید مجهز به یک SQL Parser تمام عیار باشد که کار چند ماهی هست ...؛ با توجه به اینکه مثلا NHibernate از افزونههای SQL ویژه بانکهای اطلاعاتی مختلف هم پشتیبانی میکند، مثلا T-SQL مایکروسافت با یک سری ریزه کاریهای منحصر به MySQL متفاوت است)، یا پس از فرمت شدن، syntax highlighting به آن اضافه میشد، در ادامه مشخص میکرد کدام کوئریها سنگینتر هستند، کدامیک نشانهی عدم استفادهی صحیح از ORM مورد استفاده است، چه مشکلی دارد و از این موارد.
خوشبختانه این ایدهها یا آرزوها با برنامهی NHProf محقق شده است. این برنامه برای استفادهی یک ماه اول آن رایگان است (آدرس ایمیل خود را وارد کنید تا یک فایل مجوز رایگان یک ماهه برای شما ارسال گردد) و پس از یک ماه، باید حداقل 300 دلار هزینه کنید.
واکشی حریصانه و غیرحریصانه چیست؟
رفتار یک ORM جهت تعیین اینکه آیا نیاز است برای دریافت اطلاعات بین جداول Join صورت گیرد یا خیر، واکشی حریصانه و غیرحریصانه را مشخص میسازد.
در حالت واکشی حریصانه به ORM خواهیم گفت که لطفا جهت دریافت اطلاعات فیلدهای جداول مختلف، از همان ابتدای کار در پشت صحنه، Join های لازم را تدارک ببین. در حالت واکشی غیرحریصانه به ORM خواهیم گفت به هیچ عنوان حق نداری Join ایی را تشکیل دهی. هر زمانی که نیاز به اطلاعات فیلدی از جدولی دیگر بود باید به صورت مستقیم به آن مراجعه کرده و آن مقدار را دریافت کنی.
به صورت خلاصه برنامه نویس در حین کار با ORM های پیشرفته نیازی نیست Join بنویسد. تنها باید ORM را طوری تنظیم کند که آیا اینکار را حتما خودش در پشت صحنه انجام دهد (واکشی حریصانه)، یا اینکه خیر، به هیچ عنوان SQL های تولیدی در پشت صحنه نباید حاوی Join باشند (lazy loading).
چگونه واکشی حریصانه و غیرحریصانه را در NHibernate 3.0 تنظیم کنیم؟
در NHibernate اگر تنظیم خاصی را تدارک ندیده و خواص جداول خود را به صورت virtual معرفی کرده باشید، تنظیم پیش فرض دریافت اطلاعات همان lazy loading است. به مثالی در این زمینه توجه بفرمائید:
مدل برنامه:
مدل برنامه همان مثال کلاسیک مشتری و سفارشات او میباشد. هر مشتری چندین سفارش میتواند داشته باشد. هر سفارش به یک مشتری وابسته است. هر سفارش نیز از چندین قلم جنس تشکیل شده است. در این خرید، هر جنس نیز به یک سفارش وابسته است.
using System.Collections.Generic;
namespace CustomerOrdersSample.Domain
{
public class Customer
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Order> Orders { get; set; }
}
}
using System;
using System.Collections.Generic;
namespace CustomerOrdersSample.Domain
{
public class Order
{
public virtual int Id { get; set; }
public virtual DateTime OrderDate { set; get; }
public virtual Customer Customer { get; set; }
public virtual IList<OrderItem> OrderItems { set; get; }
}
}
namespace CustomerOrdersSample.Domain
{
public class OrderItem
{
public virtual int Id { get; set; }
public virtual Product Product { get; set; }
public virtual int Quntity { get; set; }
public virtual Order Order { set; get; }
}
}
namespace CustomerOrdersSample.Domain
{
public class Product
{
public virtual int Id { set; get; }
public virtual string Name { get; set; }
public virtual decimal UnitPrice { get; set; }
}
}
که جداول متناظر با آن به صورت زیر خواهند بود:
create table Customers (
CustomerId INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
primary key (CustomerId)
)
create table Orders (
OrderId INT IDENTITY NOT NULL,
OrderDate DATETIME null,
CustomerId INT null,
primary key (OrderId)
)
create table OrderItems (
OrderItemId INT IDENTITY NOT NULL,
Quntity INT null,
ProductId INT null,
OrderId INT null,
primary key (OrderItemId)
)
create table Products (
ProductId INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
UnitPrice NUMERIC(19,5) null,
primary key (ProductId)
)
alter table Orders
add constraint fk_Customer_Order
foreign key (CustomerId)
references Customers
alter table OrderItems
add constraint fk_Product_OrderItem
foreign key (ProductId)
references Products
alter table OrderItems
add constraint fk_Order_OrderItem
foreign key (OrderId)
references Orders
همچنین یک سری اطلاعات آزمایشی زیر را هم در نظر بگیرید: (بانک اطلاعاتی انتخاب شده SQL CE است)
SET IDENTITY_INSERT [Customers] ON;
GO
INSERT INTO [Customers] ([CustomerId],[Name]) VALUES (1,N'Customer1');
GO
SET IDENTITY_INSERT [Customers] OFF;
GO
SET IDENTITY_INSERT [Products] ON;
GO
INSERT INTO [Products] ([ProductId],[Name],[UnitPrice]) VALUES (1,N'Product1',1000.00000);
GO
INSERT INTO [Products] ([ProductId],[Name],[UnitPrice]) VALUES (2,N'Product2',2000.00000);
GO
INSERT INTO [Products] ([ProductId],[Name],[UnitPrice]) VALUES (3,N'Product3',3000.00000);
GO
SET IDENTITY_INSERT [Products] OFF;
GO
SET IDENTITY_INSERT [Orders] ON;
GO
INSERT INTO [Orders] ([OrderId],[OrderDate],[CustomerId]) VALUES (1,{ts '2011-01-07 11:25:20.000'},1);
GO
SET IDENTITY_INSERT [Orders] OFF;
GO
SET IDENTITY_INSERT [OrderItems] ON;
GO
INSERT INTO [OrderItems] ([OrderItemId],[Quntity],[ProductId],[OrderId]) VALUES (1,10,1,1);
GO
INSERT INTO [OrderItems] ([OrderItemId],[Quntity],[ProductId],[OrderId]) VALUES (2,5,2,1);
GO
INSERT INTO [OrderItems] ([OrderItemId],[Quntity],[ProductId],[OrderId]) VALUES (3,20,3,1);
GO
SET IDENTITY_INSERT [OrderItems] OFF;
GO
دریافت اطلاعات :
میخواهیم نام کلیه محصولات خریداری شده توسط مشتریها را به همراه نام مشتری و زمان خرید مربوطه، نمایش دهیم (دریافت اطلاعات از 4 جدول بدون join نویسی):
var list = session.QueryOver<Customer>().List();
foreach (var customer in list)
{
foreach (var order in customer.Orders)
{
foreach (var orderItem in order.OrderItems)
{
Console.WriteLine("{0}:{1}:{2}", customer.Name, order.OrderDate, orderItem.Product.Name);
}
}
}
خروجی به صورت زیر خواهد بود:
Customer1:2011/01/07 11:25:20 :Product1
Customer1:2011/01/07 11:25:20 :Product2
Customer1:2011/01/07 11:25:20 :Product3
همانطور که مشاهده میکنید در اینجا اطلاعات از 4 جدول مختلف دریافت میشوند اما ما Join ایی را ننوشتهایم. ORM هرجایی که به اطلاعات فیلدهای جداول دیگر نیاز داشته، به صورت مستقیم به آن جدول مراجعه کرده و یک کوئری، حاصل این عملیات خواهد بود (مطابق تصویر جمعا 6 کوئری در پشت صحنه برای نمایش سه سطر خروجی فوق اجرا شده است).
این حالت فقط و فقط با تعداد رکورد کم بهینه است (و به همین دلیل هم تدارک دیده شده است). بنابراین اگر برای مثال قصد نمایش اطلاعات حاصل از 4 جدول فوق را در یک گرید داشته باشیم، بسته به تعداد رکوردها و تعداد کاربران همزمان برنامه (خصوصا در برنامههای تحت وب)، بانک اطلاعاتی باید بتواند هزاران هزار کوئری رسیده حاصل از lazy loading را پردازش کند و این یعنی مصرف بیش از حد منابع (IO بالا، مصرف حافظه بالا) به همراه بالا رفتن CPU usage و از کار افتادن زود هنگام سیستم.
کسانی که پیش از این با SQL نویسی خو گرفتهاند احتمالا الان منابع موجود را در مورد نحوهی نوشتن Join در NHibernate زیر و رو خواهند کرد؛ زیرا پیش از این آموختهاند که برای دریافت اطلاعات از دو یا چند جدول مرتبط باید Join نوشت. اما همانطور که پیشتر نیز عنوان شد، اگر با جزئیات کار با NHibernate آشنا شویم، نیازی به Join نویسی نخواهیم داشت. اینکار را خود ORM در پشت صحنه باید و میتواند مدیریت کند. اما چگونه؟
در NHibernate 3.0 با معرفی QueryOver که جایگزینی از نوع strongly typed همان ICriteria API قدیمی است، یا با معرفی Query که همان LINQ to NHibernate میباشد، متدی به نام Fetch نیز تدارک دیده شده است که استراتژیهای lazy loading و eager loading را به سادگی توسط آن میتوان مشخص نمود.
مثال: دریافت اطلاعات با استفاده از QueryOver
var list = session
.QueryOver<Customer>()
.Fetch(c => c.Orders).Eager
.Fetch(c => c.Orders.First().OrderItems).Eager
.Fetch(c => c.Orders.First().OrderItems.First().Product).Eager
.List();
foreach (var customer in list)
{
foreach (var order in customer.Orders)
{
foreach (var orderItem in order.OrderItems)
{
Console.WriteLine("{0}:{1}:{2}", customer.Name, order.OrderDate, orderItem.Product.Name);
}
}
}
پشت صحنه:
اینبار فقط یک کوئری حاصل عملیات بوده و join ها به صورت خودکار با توجه به متدهای Fetch ذکر شده که حالت eager loading آنها صریحا مشخص شده است، تشکیل شدهاند (6 بار رفت و برگشت به بانک اطلاعاتی به یکبار تقلیل یافت).
نکته 1: نتایج تکراری
اگر حاصل join آخر را نمایش دهیم، نتایجی تکراری خواهیم داشت که مربوط است به مقدار دهی customer با سه وهله از شیء مربوطه تا بتواند واکشی حریصانهی مجموعه اشیاء فرزند آنرا نیز پوشش دهد. برای رفع این مشکل یک سطر TransformUsing باید اضافه شود:
...
.TransformUsing(NHibernate.Transform.Transformers.DistinctRootEntity)
.List();
دریافت اطلاعات با استفاده از LINQ to NHibernate3.0
برای اینکه بتوان متدهای Fetch ذکر شده را به LINQ to NHibernate 3.0 اعمال نمود، ذکر فضای نام NHibernate.Linq ضروری است. پس از آن خواهیم داشت:
var list = session
.Query()
.FetchMany(c => c.Orders)
.ThenFetchMany(o => o.OrderItems)
.ThenFetch(p => p.Product)
.ToList();
اینبار از FetchMany، سپس ThenFetchMany (برای واکشی حریصانه مجموعههای فرزند) و در آخر از ThenFetch استفاده خواهد شد.
همانطور که ملاحظه میکنید حاصل این کوئری، با کوئری قبلی ذکر شده یکسان است. هر دو، اطلاعات مورد نیاز از دو جدول مختلف را نمایش میدهند. اما یکی در پشت صحنه شامل چندین و چند کوئری برای دریافت اطلاعات است، اما دیگری تنها از یک کوئری Join دار تشکیل شده است.
نکته 2: خطاهای ممکن
ممکن است حین تعریف متدهای Fetch در زمان اجرا به خطاهای Antlr.Runtime.MismatchedTreeNodeException و یا Specified method is not supported و یا موارد مشابهی برخورد نمائید. تنها کاری که باید انجام داد جابجا کردن مکان بکارگیری extension methods است. برای مثال متد Fetch باید پس از Where در حالت استفاده از LINQ ذکر شود و نه قبل از آن.
- فعال سازی فشرده سازی
- انتقال لاگها به یک سیستم راه دور
- حذف لاگ فایلهای قدیمی از طریق اسکریپت نویسی
- حذف لاگ فایلهای قدیمی توسط IIS Log File Cleaner
فشرده سازی دایرکتوری لاگ فایل ها
%SystemDrive%\inetpub\logs\LogFiles
این روش سادهترین روش موجود برای مدیریت لاگ هاست ولی روش نهایی نیست و باز به مرور زمان این روش هم کارایی خودش را از دست خواهد داد. این روش بیشتر شبیه خرید زمان میباشد تا اینکه یک راه حل نهایی برای حل مشکل باشد. البته این را هم باید مدنظر داشت که موقع تیک زدن گزینه بالا عملیات فشرده سازی باعث کند شدن سرعت کامپیوتر در حین آغاز عمل ذخیره سازی لاگ فایلها هم خواهد شد. پس اگر قصد چنین کاری ذا دارید در ساعاتی که سرور کمترین فشار از طرف کاربران را دارد یا اصطلاحا پیک کاری آن پایین است انجامش دهید.
انتقال لاگ فایلها به یک سیستم راه دور
همانطور که در بالا اشاره کردیم محل پیش فرض ذخیره سازی لاگها درمسیر
%SystemDrive%\inetpub\logs\LogFiles
قرار دارد و این محل ذخیره سازی برای هر سرور یا حتی یک وب سایت خاص در صفحه تنظیمات Logging مشخص شده است و شما در میتوانید این لاگها را حتی برای کل سرور یا مربوط به یک سایت خاص، به سروری دیگر انتقال دهید. این امکان میتواند به امنیت سیستم هم کمک فراوانی کند تا اگر دیسک محلی Local Disk هم دچار مشکل شد، باز خواندن لاگ فایلها میسر باشد و با استفاده از ابزارهای تحلیل لاگ فایل ها، آنها را مورد بررسی قرار دهیم. برای تغییر محل ذخیره سازی لاگها به یک سیستم راه دور، راه حل زیر را طی کنید.
در IIS وب سایتی را که میخواهید لاگ آن انتقال یابد، انتخاب کنید؛ یا اگر لاگ کل سیستم IIS را میخواهید انتقال بدهید نام سرور را در لیست درختی انتخاب کنید و از ماژولهای سمت راست، ماژول Logging را انتخاب کنید و در قسمت Directory که محل ذخیره سازی فعلی لاگها را نوشته شده است، به صورت UNC آدرس دهی کنید. در آدرس زیر اولی نام سرور است Contoso-server1\\ و دومی هم Logs نام پوشهای که به اشتراک گذاشته شده است.
حذف لاگ فایلهای قدیمی با استفاده از اسکریپت
با این روش میتوانید لاگ فایل هایی را که بعد از مدتی معین که دلخواه شما هست، از سیستم حذف نمایید و اگر این اسکریپت را زمان بندی خودکار نمایید، میتوانید از مراقبت مداوم و ثابت این کار نیز رها شوید.
با ستفاده از VBScript بررسی میکنیم که اگر مثلا عمر لاگ فایل به 30 روز رسیده است، باید حذف شوند. خط دوم کد زیر نهایت عمر یک لاگ فایل را مشخص میکند:
sLogFolder = "c:\inetpub\logs\LogFiles" iMaxAge = 30 'in days Set objFSO = CreateObject("Scripting.FileSystemObject") set colFolder = objFSO.GetFolder(sLogFolder) For Each colSubfolder in colFolder.SubFolders Set objFolder = objFSO.GetFolder(colSubfolder.Path) Set colFiles = objFolder.Files For Each objFile in colFiles iFileAge = now-objFile.DateCreated if iFileAge > (iMaxAge+1) then objFSO.deletefile objFile, True end if Next Next
اسکریپت بالا تمامی subfolderها را برای همه سایتها بررسی کرده و لاگهای آنان را حذف میکند. ولی اگر دوست دارید این عملیات را تنها به یک وب سایت محدود کنید، باید مسیر را در خط اول دقیقتر مشخص کنید.
برای اجرای دستی اسکریپت در cmd تایپ کنید:
cscript.exe c:\scripts\retentionscript.vbs
ولی اگر میخواهید این اسکریپت در هر دورهی زمانی خاص اجرا شود، یا زمان بندی Scheduling گردد، دیگر مجبور نیستید هر بار به فکر نگهداری از لاگها باشید.
زمان بندی اجرای اسکریپت
server manager (قابل تست در ویندوزهای سرور) را باز کرده و از منوی Tools گزینه Task Scheduler را انتخاب کنید و در قسمت Actions گزینه Create Task را انتخاب نمایید. در کادر باز شده نام "Delete Log Files " را برای مثال برگزینید و در قسمت Security هم کاربری که اجازه اجرای اسکریپت را دارد مشخص کنید.
برگه Triggers را انتخاب کرده و گزینه New را انتخاب کنید و عملیات زمان بندی را تنظیم کنید و حتما بعد از زمان بندی مطمئن باشید که تیک Enabled فعال است.
در برگه Actions هم گزینه New را انتخاب کنید؛ در کادر باز شده از لیست Start a program را انتخاب کرده و در قسمت Program\script، دستور cscript را ذکر نمایید و به عنوان آرگومان ورودی Add arguments هم مسیر اسکریپت خود را ذکر نمایید و کادر را تایید کنید.
برای آغاز زمان بندی در لیست وظیفههای فعال active task pane، وظیفه ای که الان ساخته اید را اجرا کرده و به مسیر ذخیره لاگها رفته و میبینید که لاگهای مورد نظر حذف شدهاند؛ پس از صحت اجرای اسکریپت مطمئن میشویم. دوباره به لیست وظایف رفته و گزینه End را بزنید تا وظیفه، در حالت Ready قرار گیرد تا از همین الان فرایند زمان بندی اجرای اسکریپت آغاز شود.
حذف لاگ فایلها با استفاده از IIS Log Cleaner Tools
سادهترین ابزار برای مدیریت حذف لاگ فایل هاست که هر یک ساعت یکبار اجرا شده و لاگ فایلهای تاریخ گذشته را که زمانش را شما تعیین میکنید، به سمت سطل زباله که البته درستش بازیافت است Recycle Bin انتقال میدهد تا از ضرر از دست دادن لاگها جلوگیری کند که بعدا شما میتوانید آنها را به صورت دستی حذف کنید. همچنین عملیات خودکار حذف را نیز میتوان متوقف نمود.
ابتدا برنامه را از اینجا دانلود کنید. موقعیکه برنامه را اجرا کنید، در نوتیفیکیشن taskbar مینشیند و برنامه با یک پیغام به شما اعلام میکند، این اولین بار است که برنامه را باز کردهاید. پس یک سر به setting آن بزنید؛ با انتخاب گزینهی settings برنامه بسته شده و فایل Settings.txt برای شما باز میشود که مدت زمان عمر لاگ فایل و مسیر ذخیره آنها، از شما پرسیده میشود که مقدار عمر هر لاگ فایل به طور پیش فرض 30 روز و مسیر ذخیرهی لاگها همان مسیر پیش فرض IIS است که اگر شما دستی آن را تغییر داده اید، با پرسیدن آن، از محل لاگها اطمینان کسب میکند. در صورتی که قصد تغییری را در فایل، دارید آن را تغییر داده و ذخیره کنید و برنامه را مجددا اجرا کنید.
نکات نهایی در مورد این برنامه :
- اگر از ابزار IIS Cleaner Tool استفاده میکنید باید دستی سطل بازیافت را هم پاک کنید و هم اینکه میتوانید یک محدودیت حجمی برای Recycle Bin قرار دهید که اگر به یک حدی رسید، خودکار پاک کند تا مشکلی برای سیستم عامل ایجاد نشود که البته به طور پیش فرض چنین است.
- برنامه بالا به طور پیش فرض ریشهی لاگها را حذف میکند. پس اگر میخواهید فقط سایت خاصی را مد نظر داشته باشد، آدرس دایرکتوری آن را اضافه کنید. البته چون این برنامه فقط روی یک دایرکتوری کار میکند و شما چند وب سایت دارید و مثلا میخواهید سه تای آنها را پاکسازی کنید، چارهی جز استفاده از اسکریپتهای با زمان بندی ندارید.
- برنامهی بالا فقط فایل هایی با پسوند log را به سطل بازیافت انتقال میدهد.
- برنامهی بالا یک سرویس نیست و باید به طور دستی توسط کاربر اجرا گردد. پس اگر ریست هم شد باید دستی اجرا شود یا آن را به داخل پوشه startup بکشید.
- برنامه برای اجرایش نیاز به لاگین کاربر و مجوز نوشتن در آن پوشه را دارد تا به درستی کار کند.
Work in progress to add support for covariant return types to the .NET runtime. Soon we'll be able to override a virtual method returning `object` with a method returning `string`. Because of how array variance works, weird things might be possible in IL.
class Base { public virtual IntPtr[] Fun() => null; } // This is obvious pseude-code because C# won't let us introduce methods differing // in return type. C# also requires to be explicit about "virtual" and "override". // But IL... not so much. class Derived : Base { // overrides Base.Fun on 32bit platforms. public override uint[] Fun() => null; // overrides Base.Fun on 64bit platforms. public override ulong[] Fun() => null; }
- اصل Interface Segregation چیست؟ | www.dotnetdev.info
- چند نکته در مورد فیلد Identity در SQL | vsblogger.blogfa.com
- گوگل سالانه 300 میلیون دلار به موزیلا پرداخت می کند | www.winbeta.net
- وزیر ارتباطات : دسترسی به اینترنت ممکن است حذف شود | www.winbeta.net
- FIXED – 64-bit Adobe PDF Previewer | www.sharepointjohn.com
- LINQ Extensions Library | linqlib.codeplex.com
- Reactive Extensions (Rx) v1.0.10621 SP1 | www.microsoft.com
- Return-Oriented Programming | cseweb.ucsd.edu
- 11 کاری که هر برنامه نویس بهتر است در سال 2012 انجام دهد | michaelcrump.net
- Max & Min in LINQ to XML | rmanimaran.wordpress.com
- سه پروژه آغازین با Roslyn | www.codeproject.com
- مروری بر مباحث امنیتی سیلورلایت 5 | blogs.msdn.com
- تفاوت no-store با no-cache در Cache-Control | tostring.blogfa.com
- آموزش jQuery بخش اول | www.pasargad-graphic.com
- نمونه فرم قرارداد برای توسعه دهندگان وب – شماره 2 | silvercover.me
- Android-x86 - Porting Android to x86 | www.android-x86.org
- Learn Regex The Hard Way
Scanning And Parsing Text Without Going Insane | regex.learncodethehardway.org
- Learning Modern 3D Graphics Programming | www.arcsynthesis.org
- MSDN Magazine: December 2011 | msdn.microsoft.com
- Scott Hanselman's 2011 Ultimate Developer and Power Users Tool List for Windows - Scott Hanselman | www.hanselman.com
- Telerik Visual Style Builder | stylebuilder.telerik.com
- Themes - BlogEngine.NET gallery | www.dnbegallery.org
- SilverlightPersianDatePicker 1.2 منتشر شد | slpdatepicker.codeplex.com
- TortoiseSVN-1.7.2.22327 منتشر شد | sourceforge.net
- مقالاتی در مورد برنامه نویسی موازی در دات نت 4.5 | www.microsoft.com