Archive for 2009/10

بررسی وجود پیاده سازی محتویات MasterPage

۱۳۸۸/۰۸/۰۴ ۱:۴۷ Salar Khalilzadeh https://plus.google.com/105397214522932500988 منتشر شده در تاریخ : ۱۳۸۸/۰۸/۰۴ دسته بندی : ، ، ، ، 2

شاید در استفاده از MasterPage ها زمانی پیش آمده باشد که لازم شده وجود پیاده سازی یک ContentPlaceHolder را در صفحه خاص رو بررسی کنید. مثلا زمانی رو در نظر بگیرید که در صفحه خاصی نباید بلوک خلاصه محتویات صفحه یا لینکها نمایش داده شود و این کادر نیز در MasterPage پیاده سازی شده است.

پس روشی باید به کار گیرید که آن کادر در آن صفحه به خصوص نمایش داده نشود. متاسفانه در دات نت روشی برای این کار در نظر گرفته نشده است؛ لااقل هیچ متد عمومی برای این کار وجود ندارد.

خوشبختانه ویژگی داخلی و مخفی در کلاس MasterPage وجود دارد به نام ContentTemplates که لیستی از ContentPlaceHolder هست که توسط صفحه جاری پیاده سازی شده است. کاری که لازم است استفاده از قدرت Reflection دات نت هست تا به آن ویژگی دسترسی پیدا کنیم.

همراه با این باید ContentPlaceHolder برای وجود کنترل در داخلی خودش هم بررسی بشه. تابع زیر برای همین کار هست:
public static bool HasNonEmptyControls(ContentPlaceHolder cph)
{
if (cph.Controls.Count == 0)
{
return false;
}
else if (cph.Controls.Count == 1)
{
LiteralControl c = cph.Controls[0] as LiteralControl;

if (string.IsNullOrEmpty(c.Text) || IsWhiteSpace(c.Text))
return false;
}

return true;
}

static bool IsWhiteSpace(string s)
{
for (int i = 0; i < s.Length; i++)
if (!char.IsWhiteSpace(s[i]))
return false;

return true;
}

در ادامه تابع اصلی منظور ما که وجود پیاده سازی از یک ContentPlaceHolder به خصوص رو بررسی خواهد کرد:
static readonly Type _masterType = typeof(MasterPage);
static readonly PropertyInfo _contentTemplatesProp = _masterType.GetProperty("ContentTemplates", BindingFlags.GetProperty | BindingFlags.NonPublic | BindingFlags.Instance);

public static bool HasContentPageContent(ContentPlaceHolder cph)
{
IDictionary templates = null;
MasterPage master = cph.Page.Master;

while (templates == null && master != null)
{
templates = (IDictionary)_contentTemplatesProp.GetValue(master, null);
master = master.Master;
}

if (templates == null)
return false;

bool isSpecified = false;

foreach (string key in templates.Keys)
{
if (key == cph.ID)
{
isSpecified = true;

break;
}
}

return isSpecified;
}

همانطور که از کد مشخصه با بدست آوردن ویژگی ContentTemplates که یک لسیت دیکشنری است وجود ContentPlaceHolder مورد نظر رو بررسی می کنیم.

و سر انجام ترکیبی از این دو تابع جهت بدست آوردن نتیجه مطلوب:
public static bool HasContentOrControls(ContentPlaceHolder cph)
{
return HasNonEmptyControls(cph) || HasContentPageContent(cph);
}

و تمام. به راحتی می توانید از این تابع استفاده کنید. مانند نمونه زیر:
<%if (HasContentOrControls(plhOptions)){ %>
<div id="options">
<div>
<h2>
<asp:ContentPlaceHolder ID="plhOptionsTitle" runat="server" />
</h2>
<asp:ContentPlaceHolder ID="plhOptions" runat="server" />
</div>
</div>
<%}%>

همانطور که در این مثال مشاهده می کنید، فقط در صورتی که plhOptions در صفحه پیاده سازی شده باشد کادر مربوط به آن همراه با محتویات بخش plhOptionsTitle نمایش داده خواهند شد.

منبع

خوش باشید.

 

رفع باگ موجود در CookieContainer

۱۳۸۸/۰۷/۲۴ ۲۳:۵۱ Salar Khalilzadeh https://plus.google.com/105397214522932500988 منتشر شده در تاریخ : ۱۳۸۸/۰۷/۲۴ دسته بندی : ، ، 0

قبلا نوشته بودم که ماکروسافت قراره باگی رو که در کلاس CookieContainer هست رو رفع کنه، که اون باگ باعث 7 ماه سرگردانی من شده است. خبر خوبی بود، اما اگر به سایت ماکروسافت که لینکش رو گذاشته بودم مراجعه کنید می بینید که هیچ وصله ای قرار نیست برای دات نت 2 منتشر بشه. و این یعنی اینکه همچنان با مشکل باید دست و پنجه نرم کنیم!

به هر حال در ادامه می خوام علت بروز مشکل و راه حل اون رو با زحمت بسیار و همکاری یه نفر دیگه بدست اومده رو بررسی کنم.

  • مقدمه ای در مورد Domain در کوکی ها


تمام مشکل حول محور نحوه مدیریت domain در کلاس Cookiecontainer بر میگرده. پس میخوام مختصر توضیحی در مورد دامین در کوکی ها بدم.

در مبحث کار با کوکی ها با تنظیم مقدار domain می توانید تعیین کنید که کوکی برای کدام دامنه یا زیر دامنه ها قابل دسترسی باشد.

یک مثال ساده مانند زیر هست؛ در این مثال  کوکی با نام Hello رو برای دامین site.org ثبت می کنیم:

Hello=cookieValue value; domain=site.org; path=/

توجه کنید که این کوکی فقط به دامین site.org ثبت شده و فقط برای آن قابل مشاهده خواهد بود. هیچ زیر دامنه ای مانند www.site.org نمی تواند این کوکی رو مشاهده کند. برای اینکه کوکی به زیر دامنه های سایت site.org هم قابل دسترسی باشه باید یک نقطه (.) به اول دامنه اضافه کنیم. مانند این نمونه:

Hello=cookieValue value; domain=.site.org; path=/

با این کار این کوکی به تمامی زیر دامنه ها قابل مشاهده خواهد بود.

همچنین توجه کنید که این قانون به کوکی های خود زیر دامنه ها هم اعمال میشه. برای مثال کوکی زیر فقط برای sub.site.org قابل مشاهده خواهد بود:

Hello=cookieValue value; domain=sub.site.org; path=/

و کوکی زیر، هم برای دامنه اصلی و هم برای تمامی زیر دامنه های آن قابل مشاهده خواهد بود:

Hello=cookieValue value; domain=.sub.site.org; path=/

  • بررسی مشکل


قبل از اینکه به مشکل بپردازم لازمه بدونید که کلاس CookieContainer برای مدیریت و نگهداری کوکی های برای ارسال درخواست های وب توسط HttpWebRequest مورد استفاده قرار میگیره. و به علت مشکلی که داره بعضا باعث اشکالات عجیب و نامشخص می شه.

احتمالا تا حالا متوجه شدید که مشکل کلاس CookieContainer در نحوه مدیریت نقطه (.) هایی هست که در ابتدای دامین قرار می گیره و به شیوه اعمال اونها تاثیر میذاره.

برای نمونه مشکل زمانی اتفاق می افته که یک کوکی رو به آدرس site.org ثبت کنید و درخواست خودتون رو آدرس www.site.org ارسال کنید.
طبق مقدمه انتظار میره که هیچ کوکی ارسال نشه ولی برخلاف انتطار کوکی مزبور ارسال خواهد شد. متن کوکی این مثال:

Hello=CookieValue value; domain=site.org; path=/

همچینین مشکل دیگر و البته بدتر زمانی هست که کوکی رو به آدرس www.site.org ثبت کنید و درخواست خودتون رو به آدرس www.site.org ارسال کنید. انتظار می ره که کوکی به سلامتی ارسال بشه ولی در کمال ناباوری هیچ کوکی ارسال نخواهد شد. متن کوکی این مثال:

Hello=CookieValue value; domain=www.site.org; path=/

علت بروز مشکل به یک سری پیچیدگیهای اضافی که ماکروسافت در پیاده سازی این کلاس اعمال کرده بر می گرده.

  • نحوه رفع باگ


مشکل اصلی به نحوه پیاده سازی یکی از متد ها داخلی و پنهان کلاس CookieContainer بر می گرده. در نتییجه توابعی که از اون تابع استفاده می کنند این مشکل زو خواهند داشت. متاسفانه همه سه تابعی که برای افزودن کوکی قابل دسترسی هستند از مشکل رنج می برند! این سه تابع و توضیح مختصر در مورد اونها:

  • Add(Uri,Cookie) این تابع کوکی را اضافه کرده و به مسیر تعیین شده ارتباط میدهد

  • Add(Cookie) این تابع کوکی رو اضافه کرده و دامینهای قابل اعمال رو از دامین کوکی تعیین می کنه

  • SetCookies(CookieHeader) این تابع کوکی که به صورت هدر کوکی ارسال شده رو تولید کرده و اضافه می کنه.


راه حل قطعی که تا کنون با همکاری دوستم تونستیم پیدا کنیم تغییر کلید های دسترسی به کوکی و اضافه کردن کلید های مفقود شده به صورت دستی با استفاده از امکانات Reflection دات نت.

به صورت خلاصه تابع زیر برای رفع این نقیصه نوشته شده. این تابع رو همراه با یک سری تمهیدات باید به کار ببرد که در ادامه توضیح خواهد داد:
private static Type _cookieContainerType = Type.GetType("System.Net.CookieContainer, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
private static Type _pathListType = Type.GetType("System.Net.PathList, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
///<summary>
/// This method is aimed to fix a goddamn CookieContainer issue,
/// It adds missed path for cookies which are not started with dot.
/// This is a dirty hack
///</summary>
///<remarks>
/// This method is only for .NET 2.0 which is used by .NET 3.0 and 3.5 too.
/// The issue will be fixed in .NET 4, I hope!
///</remarks>
/// Many thanks to CallMeLaNN "dot-net-expertise.blogspot.com" to complete this method
private void BugFix_AddDotCookieDomain(CookieContainer cookieContainer)
{
Hashtable table = (Hashtable)_cookieContainerType.InvokeMember("m_domainTable",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance,
null,
cookieContainer,
new object[] { });

ArrayList keys = new ArrayList(table.Keys);

object pathList1;
object pathList2;

SortedList sortedList1;
SortedList sortedList2;
ArrayList pathKeys;

CookieCollection cookieColl1;
CookieCollection cookieColl2;

foreach (string key in keys)
{
if (key[0] == '.')
{
string nonDotKey = key.Remove(0, 1);
// Dont simply code like this:
// table[nonDotKey] = table[key];
// instead code like below:

// This codes will copy all cookies in dot domain key into nondot domain key.

pathList1 = table[key];
pathList2 = table[nonDotKey];
if (pathList2 == null)
{
pathList2 = Activator.CreateInstance(_pathListType); // Same as PathList pathList = new PathList();
lock (cookieContainer)
{
table[nonDotKey] = pathList2;
}
}

// merge the PathList, take cookies from table[keyObj] copy into table[nonDotKey]
sortedList1 = (SortedList)_pathListType.InvokeMember("m_list", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance, null, pathList1, new object[] { });
sortedList2 = (SortedList)_pathListType.InvokeMember("m_list", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance, null, pathList2, new object[] { });

pathKeys = new ArrayList(sortedList1.Keys);

foreach (string pathKey in pathKeys)
{

cookieColl1 = (CookieCollection)sortedList1[pathKey];
cookieColl2 = (CookieCollection)sortedList2[pathKey];
if (cookieColl2 == null)
{
cookieColl2 = new CookieCollection();
sortedList2[pathKey] = cookieColl2;
}

foreach (Cookie c in cookieColl1)
{
lock (cookieColl2)
{
cookieColl2.Add(c);
}
}
}
}
}
}

جزئیات نحوه کار این متد زیاد مهم نخواهد بود پس از ذکر آنها خودداری می کنم.

شیوه به کار گیری:

نکته خیلی خیلی مهم این هست که از هیچ کدام از توابع Add(Uri,Cookie)  و SetCookies(CookieHeader) نباید استفاده کنید. مشکلاتی این دو تابع ایجاد می کنند غیر قابل اصلاح است.

تنها تابع مجاز برای استفاده Add(Cookie) جها اضافه کردن کوکی های به کلاس CookieContainer است.

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

این تابع تمام مشکلات ذکر شده در بالا را رفع خواهد کرد.

نکته پایانی

تنها موردی که باقی ماند که نیاز اساسی به یه راه حل داره تابع SetCookies هست. چون این تابع با هدر ها کار می کنه ممکنه که به اون نیاز پیدا کنید. متاسفانه همونطور که گفتم مشکل مربوط به SetCookies هنوز رفع نشده و من و دوستم به دنبال راه حلی برای این مورد هستیم.

  • لینکهای مرتبط


بحث طولانی من و دوستم که منجر به رسیدن به این راه حل شد

وصله ماکروسافت برای رفع مشکل 7 ماهه من

پاسخ ماکروسافت به این مشکل

 

FeedDemon V3

۱۳۸۸/۰۷/۱۵ ۲۲:۵۶ Salar Khalilzadeh https://plus.google.com/105397214522932500988 منتشر شده در تاریخ : ۱۳۸۸/۰۷/۱۵ دسته بندی : ، ، ، 1

Feed Demon

FeedDemon ابزاری جهت خواندن و مدیریت فید یا خوراک ها هست. نسخه جدید این نرم افزار مدتی هست که منتشر شده. این نسخه نسبت به نسخه قبلی پیشرفت های قابل ملاحظه ای پیدا کرده. ویژگی قابل توجه این نسخه امکان هماهنگ کردن نرم افزار با سرویس Google Reader هست. یعنی می تونید به حساب خودتون از طریق این نرم افزار دسترسی پیدا کنید، بدون نیاز به یازکردن مرورگر و رفتن به صفخه google reader و از خواندن فید ها لذت ببرید. در مقابل حجم و سربار کمی که داره امکانات قابل توجهی رو ارائه می کنه.

Feed Demon

سایر امکانات این نرم افزار به طور خلاصه شامل :

  • به اشتراک گذاری فیدها، در این نسخه بهبود زیادی کرده.

  • نمایش مسیر اصلی آدرسهای کوتاه شده با TinyUrl

  • برچسب گذاری لینک ها و فیدها

  • Watch ها که امکان جمع آوری مطالبی مرتبط با کلمات کلیدی تعیین شده رو فراهم می کنه. در این نسخه بهبود زیادی کرده.

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

  • پادکست: امکان دانلود خودکار پادکستها و انتقال آنها به وسایل قابل حملتان

به نظر من این ابزار بسیار کارایی برایتان برای خواندن فیدها خواهد بود، حتی بهتر از بقیه ، پس حتما اون رو امتحان کنید.

دریافت FeedDemon

وبسایت

فید چیست؟

* پ.ن: جالبه بدونید که این نرم افزار با Delphi و SQLite توسعه داده شده.