مطالب دوره‌ها
مروری بر روش ها و رویکردهای مختلف در یادگیری مدل
مقدمه
همان گونه که اشاره شد در روش‌های با ناظر (برای مثال الگوریتم‌های دسته بندی) کل مجموعه داده‌ها به دو بخش مجموعه داده‌های آموزشی و مجموعه داده‌های آزمایشی تقسیم می‌شود. در مرحله یادگیری (آموزش) مدل، الگوریتم براساس مجموعه داده‌های آموزشی یک مدل می‌سازد که شکل مدل ساخته شده به الگوریتم یادگیرنده مورد استفاده بستگی دارد. در مرحله ارزیابی براساس مجموعه داده‌های آزمایشی دقت و کارائی مدل ساخته شده بررسی می‌شود. توجه داشته باشید که مجموعه داده‌های آزمایشی برای مدل ساخته شده پیش از این ناشناخته هستند.
در مرحله یادگیری مدل؛ برای مقابله با مشکل به خاطرسپاری (Memorization) مجموعه داده‌های آموزشی، در برخی موارد بخشی از مجموعه داده‌های آموزشی را از آن مجموعه جدا می‌کنند که با عنوان مجموعه داده ارزیابی (Valid Dataset) شناسائی می‌شود. استفاده از مجموعه داده ارزیابی باعث می‌شود که مدل ساخته شده، مجموعه داده‌های آموزشی را حقیقتاً یاد بگیرد و در پی به خاطرسپاری و حفظ آن نباشد. به بیان دیگر در مرحله یادگیری مدل؛ تا قبل از رسیدن به لحظه ای، مدل در حال یادگیری و کلی سازی (Generalization) است و از آن لحظه به بعد در حال به خاطرسپاری (Over Fitting) مجموعه داده‌های آموزشی است. بدیهی است به خاطرسپاری باعث افزایش دقت مدل برای مجموعه داده‌های آموزشی و بطور مشابه باعث کاهش دقت مدل برای مجموعه داده‌های آزمایشی می‌شود. بدین منظور جهت جلوگیری از مشکل به خاطرسپاری از مجموعه داده ارزیابی استفاده می‌شود که به شکل غیر مستقیم در فرآیند یادگیری مدل، وارد عمل می‌شوند. بدین ترتیب مدلی که مفهومی را از داده‌های آموزشی فرا گرفته، نسبت به مدلی که صرفاً داده‌های آموزشی را به خوبی حفظ کرده است، برای مجموعه داده آزمایشی دقت به مراتب بالاتری دارد. این حقیقت در بیشتر فرآیندهای آموزشی که از مجموعه داده ارزیابی بهره می‌گیرند قابل مشاهده است.
در روش‌های بدون ناظر یا روش‌های توصیفی (برای مثال خوشه بندی) الگوریتم‌ها فاقد مراحل آموزشی و آزمایشی هستند و در پایان عملیات یادگیری مدل، مدل ساخته شده به همراه کارائی آن به عنوان خروجی ارائه می‌شود، برای مثال در الگوریتم‌های خوشه بندی خروجی همان خوشه‌های ایجاد شده هستند و یا خروجی در روش کشف قوانین انجمنی عبارت است از مجموعه ای از قوانین «اگر- آنگاه» که بیانگر ارتباط میان رخداد توامان مجموعه ای از اشیاء با یکدیگر می‌باشد.

در این قسمت عملیات ساخت مدل در فرآیند داده کاوی برای سه روش دسته بندی، خوشه بندی و کشف قوانین انجمنی ارائه می‌شود. بدیهی است برای هر کدام از این روش‌ها علاوه بر الگوریتم‌های معرفی شده، الگوریتم‌های متنوعی دیگری نیز وجود دارد. در ادامه سعی می‌شود به صورت کلان به فلسفه یادگیری مدل پرداخته شود. فهرست مطالب به شرح زیر است:
1- دسته بندی:
1-1- دسته بندی مبتنی بر درخت تصمیم (Decision Tree based methods) :  
1-2- دسته بندهای مبتنی بر قانون (Rule based methods) :  
1-3- دسته بندهای مبتنی بر نظریه بیز (Naïve Bayes and Bayesian belief networks) :  
2- خوشه بندی:
2-1- خوشه بندی افرازی (Centroid Based Clustering) :  
2-1-1- الگوریتم خوشه بندی K-Means :  
2-1-2- الگوریتم خوشه بندی K-Medoids :  
2-1-3- الگوریتم خوشه بندی Bisecting K-Means :  
2-1-4- الگوریتم خوشه بندی Fuzzy C-Means :  
2-2- خوشه بندی سلسله مراتبی (Connectivity Based Clustering (Hierarchical Clustering : 
2-2-1- روش‌های خوشه بندی تجمیعی (Agglomerative Clustering) :  
2-2-2- روش‌های خوشه بندی تقسیمی (Divisive Clustering) :  
2-3- خوشه بندی مبتنی بر چگالی (Density Based Clustering) :  
3- کشف قوانین انجمنی :
3-1- الگوریتم های  Apriori ، Brute-Force و FP-Growth: 

1- دسته بندی:
در الگوریتم‌های دسته بندی، برای هر یک از رکوردهای مجموعه داده مورد کاوش، یک برچسب که بیانگر حقیقتی از مساله است تعریف می‌شود و هدف الگوریتم یادگیری؛ یافتن نظم حاکم بر این برچسب هاست. به بیان دیگر در مرحله آموزش؛ مجموعه داده‌های آموزشی به یکی از الگوریتم‌های دسته بندی داده می‌شود تا بر اساس سایر ویژگی‌ها برای مقادیر ویژگی دسته، مدل ساخته شود. سپس در مرحله ارزیابی؛ دقت مدل ساخته شده به کمک مجموعه داده‌های آزمایشی ارزیابی خواهد شد. انواع گوناگون الگوریتم‌های دسته بندی را می‌توان بصورت ذیل برشمرد:

1-1- دسته  بندی مبتنی بر درخت تصمیم (Decision Tree based methods):
از مشهورترین روش‌های ساخت مدل دسته بندی می‌باشد که دانش خروجی را به صورت یک درخت از حالات مختلف مقادیر ویژگی‌ها ارائه می‌کند. بدین ترتیب دسته بندی‌های مبتنی بر درخت تصمیم کاملاً قابل تفسیر می‌باشند. در حالت کلی درخت تصمیم بدست آمده برای یک مجموعه داده آموزشی؛ واحد و یکتا نیست. به بیان دیگر براساس یک مجموعه داده، درخت‌های تصمیم مختلفی می‌توان بدست آورد. عموماً به منظور فراهم نمودن اطلاعات بیشتری از داده ها، از میان ویژگی‌های موجود یک Case ابتدا آنهایی که دارای خاصیت جداکنندگی بیشتری هستند انتخاب می‌شوند. در واقع براساس مجموعه داده‌های آموزشی از میان ویژگی ها، یک ویژگی انتخاب می‌شود و در ادامه مجموعه رکوردها براساس مقدار این ویژگی شکسته می‌شود و این فرآیند ادامه می‌یابد تا درخت کلی ساخته شود. پس از ساخته شدن مدل، می‌توان آن را بر روی مجموعه داده‌های آزمایشی اعمال (Apply) نمود. منظور از اعمال کردن مدل، پیش بینی مقدار ویژگی یک دسته برای یک رکورد آزمایشی براساس مدل ساخته شده است. توجه شود هدف پیش بینی ویژگی دسته این رکورد، براساس درخت تصمیم موجود است.
بطور کلی الگوریتم‌های تولید درخت تصمیم مختلفی از جمله SPRINT، SLIQ، C4.5، ID3، CART و HUNT وجود دارد. این الگوریتم‌ها به لحاظ استفاده از روش‌های مختلف جهت انتخاب ویژگی و شرط توقف در ساخت درخت با یکدیگر تفاوت دارند. عموماً الگوریتم‌های درخت تصمیم برای شناسائی بهترین شکست، از یک مکانیزم حریصانه (Greedy) استفاده می‌کنند که براساس آن شکستی که توزیع دسته‌ها در گره‌های حاصل از آن همگن باشد، نسبت به سایر شکست‌ها بهتر خواهد بود. منظور از همگن بودن گره این است که همه رکوردهای موجود در آن متعلق به یک دسته خاص باشند، بدین ترتیب آن گره به برگ تبدیل خواهد شد. بنابراین گره همگن گره ای است که کمترین میزان ناخالصی (Impurity) را دارد. به بیان دیگر هر چه توزیع دسته‌ها در یک گره همگن‌تر باشد، آن گره ناخالصی کمتری خواهد داشت. سه روش مهم برای محاسبه ناخالصی گره وجود دارد که عبارتند از: ضریب GINI، روش Entropy و Classification Error.
از مزایای درخت تصمیم می‌توان به توانایی کار با داده‌های گسسته و پیوسته، سهولت در توصیف شرایط (با استفاده از منطق بولی) در درخت تصمیم، عدم نیاز به تابع تخمین توزیع، کشف روابط غیرمنتظره یا نامعلوم و ... اشاره نمود.
همچنین از معایب درخت تصمیم نسبت به دیگر روش‌های داده کاوی می‌توان این موارد را برشمرد: تولید درخت تصمیم گیری هزینه بالائی دارد، در صورت همپوشانی گره‌ها تعداد گره‌های پایانی زیاد می‌شود، طراحی درخت تصمیم گیری بهینه دشوار است، احتمال تولید روابط نادرست وجود دارد و ... .
می‌توان موارد استفاده از دسته بند درخت تصمیم نسبت به سایر دسته بندی کننده‌های تک مرحله ای رایج را؛ حذف محاسبات غیر ضروری و انعطاف پذیری در انتخاب زیر مجموعه‌های مختلفی از صفات برشمرد. در نهایت از جمله مسائل مناسب برای یادگیری درخت تصمیم، می‌توان به مسائلی که در آنها نمونه‌ها به شکل جفت‌های «صفت-مقدار» بازنمائی می‌شود و همچنین مسائلی که تابع هدف، مقادیر خروجی گسسته دارد اشاره نمود.

1-2- دسته  بندهای مبتنی بر قانون (Rule based methods):
این دسته بندها دانش خروجی خود را به صورت یک مجموعه از قوانین «اگر-آنگاه» نشان می‌دهند. هر قانون یک بخش شرایط (LHS: Left Hand Side) و یک بخش نتیجه (RHS: Right Hand Side) دارد. بدیهی است اگر تمام شرایط مربوط به بخش مقدم یک قانون درباره یک رکورد خاص درست تعبیر شود، آن قانون آن رکورد را پوشش می‌دهد. دو معیار Accuracy و Coverage برای هر قانون قابل محاسبه است که هر چه میزان این دو معیار برای یک قانون بیشتر باشد، آن قانون؛ قانونی با ارزش‌تر محسوب می‌شود.

Coverage یک قانون، برابر با درصد رکوردهایی است که بخش شرایط قانون مورد نظر در مورد آنها صدق می‌کند و درست تعبیر می‌شود. بنابراین هر چه این مقدار بیشتر باشد آن قانون، قانونی کلی‌تر و عمومی‌تر می‌باشد.
Accuracy یک قانون بیان می‌کند که در میان رکوردهایی که بخش شرایط قانون در مورد آنها صدق می‌کند، چند درصد هر دو قسمت قانون مورد نظر در مورد آنها صحیح است.
چنانچه مجموعه همه رکورد‌ها را در نظر بگیریم؛ مطلوب‌ترین حالت این است که همواره یک رکورد توسط یک و تنها یک قانون پوشش داده شود، به بیان دیگر مجموعه قوانین نهایی به صورت جامع (Exhaustive Rules) و دو به دو ناسازگار (Mutually Exclusive Rules) باشند. جامع بودن به معنای این است که هر رکورد حداقل توسط یک قانون پوشش داده شود و معنای قوانین مستقل یا دو به دو ناسازگار بودن بدین معناست که هر رکورد حداکثر توسط یک قانون پوشش داده شود.
مجموعه قوانین و درخت تصمیم عیناً یک مجموعه دانش را نشان می‌دهند و تنها در شکل نمایش متفاوت از هم هستند. البته روش‌های مبتنی بر قانون انعطاف پذیری و تفسیرپذیری بالاتری نسبت به روش‌های مبتنی بر درخت دارند. همچنین اجباری در تعیین وضعیت هایی که در یک درخت تصمیم برای ترکیب مقادیر مختلف ویژگی‌ها رخ می‌دهد ندارند و از این رو دانش خلاصه‌تری ارائه می‌دهند.


1-3- دسته بند‌های مبتنی بر نظریه بیز (Naïve Bayes and Bayesian belief networks):
دسته بند مبتنی بر رابطه نظریه بیز (Naïve Bayes) از یک چهارچوب احتمالی برای حل مسائل دسته بندی استفاده می‌کند. براساس نظریه بیز رابطه I برقرار است:

هدف محاسبه دسته یک رکورد مفروض با مجموعه ویژگی‌های (A1,A2,A3,…,An) می‌باشد. در واقع از بین دسته‌های موجود به دنبال پیدا کردن دسته ای هستیم که مقدار II را بیشینه کند. برای این منظور این احتمال را برای تمامی دسته‌های مذکور محاسبه نموده و دسته ای که مقدار این احتمال به ازای آن بیشینه شود را به عنوان دسته رکورد جدید در نظر می‌گیریم. ذکر این نکته ضروری است که بدانیم نحوه محاسبه برای ویژگی‌های گسسته و پیوسته متفاوت می‌باشد.


2- خوشه بندی:
خوشه را مجموعه ای از داده‌ها که به هم شباهت دارند تعریف می‌کنند و هدف از انجام عملیات خوشه بندی فهم (Understanding) گروه رکوردهای مشابه در مجموعه داده‌ها و همچنین خلاصه سازی (Summarization) یا کاهش اندازه‌ی مجموعه داده‌های بزرگ می‌باشد. خوشه بندی از جمله روش هایی است که در آن هیچ گونه برچسبی برای رکوردها در نظر گرفته نمی‌شود و رکوردها تنها براساس معیار شباهتی که معرفی شده است، به مجموعه ای از خوشه‌ها گروه بندی می‌شوند. عدم استفاده از برچسب موجب می‌شود الگوریتم‌های خوشه بندی جزء روش‌های بدون ناظر محسوب شوند و همانگونه که پیشتر ذکر آن رفت در خوشه بندی تلاش می‌شود تا داده‌ها به خوشه هایی تقسیم شوند که شباهت بین داده ای درون هر خوشه بیشینه و بطور مشابه شباهت بین داده‌ها در خوشه‌های متفاوت کمینه شود.
چنانچه بخواهیم خوشه بندی و دسته بندی را مقایسه کنیم، می‌توان بیان نمود که در دسته بندی هر داده به یک دسته (طبقه) از پیش مشخص شده تخصیص می‌یابد ولی در خوشه بندی هیچ اطلاعی از خوشه‌ها وجود ندارد و به عبارتی خود خوشه‌ها نیز از داده‌ها استخراج می‌شوند. به بیان دیگر در دسته بندی مفهوم دسته در یک حقیقت خارجی نهفته است حال آنکه مفهوم خوشه در نهان فواصل میان رکورد هاست. مشهورترین تقسیم بندی الگوریتم‌های خوشه بندی به شرح زیر است:

2-1- خوشه بندی افرازی (Centroid Based Clustering) :
تقسیم مجموعه داده‌ها به زیرمجموعه‌های بدون همپوشانی، به طریقی که هر داده دقیقاً در یک زیر مجموعه قرار داشته باشد. این الگوریتم‌ها بهترین عملکرد را برای مسائل با خوشه‌های به خوبی جدا شده از خود نشان می‌دهند. از الگوریتم‌های افرازی می‌توان به موارد زیر اشاره نمود:

2-1-1- الگوریتم خوشه بندی K-Means :
در این الگوریتم عملاً مجموعه داده‌ها به تعداد خوشه‌های از پیش تعیین شده تقسیم می‌شوند. در واقع فرض می‌شود که تعداد خوشه‌ها از ابتدا مشخص می‌باشند. ایده اصلی در این الگوریتم تعریف K مرکز برای هر یک از خوشه‌ها است. بهترین انتخاب برای مراکز خوشه‌ها قرار دادن آنها (مراکز) در فاصله هر چه بیشتر از یکدیگر می‌باشد. پس از آن هر رکورد در مجموعه داده به نزدیکترین مرکز خوشه تخصیص می‌یابد. معیار محاسبه فاصله در این مرحله هر معیاری می‌تواند باشد. این معیار با ماهیت مجموعه داده ارتباط تنگاتنگی دارد. مشهورترین معیارهای محاسبه فاصله رکوردها در روش خوشه بندی معیار فاصله اقلیدسی و فاصله همینگ می‌باشد. لازم به ذکر است در وضعیتی که انتخاب مراکز اولیه خوشه‌ها به درستی انجام نشود، خوشه‌های حاصل در پایان اجرای الگوریتم کیفیت مناسبی نخواهند داشت. بدین ترتیب در این الگوریتم جواب نهائی به انتخاب مراکز اولیه خوشه‌ها وابستگی زیادی دارد که این الگوریتم فاقد روالی مشخص برای محاسبه این مراکز می‌باشد. امکان تولید خوشه‌های خالی توسط این الگوریتم از دیگر معایب آن می‌باشد.

2-1-2- الگوریتم خوشه بندی K-Medoids :

این الگوریتم برای حل برخی مشکلات الگوریتم K-Means پیشنهاد شده است، که در آن بجای کمینه نمودن مجموع مجذور اقلیدسی فاصله بین نقاط (که معمولاً به عنوان تابع هدف در الگوریتم K-Means مورد استفاده قرار می‌گیرد)، مجموع تفاوت‌های فواصل جفت نقاط را کمینه می‌کنند. همچنین بجای میانگین گیری برای یافتن مراکز جدید در هر تکرار حلقه یادگیری مدل، از میانه مجموعه اعضای هر خوشه استفاده می‌کنند.

2-1-3- الگوریتم خوشه بندی Bisecting K-Means :
ایده اصلی در این الگوریتم بدین شرح است که برای بدست آوردن K خوشه، ابتدا کل نقاط را به شکل یک خوشه در نظر می‌گیریم و در ادامه مجموعه نقاط تنها خوشه موجود را به دو خوشه تقسیم می‌کنیم. پس از آن یکی از خوشه‌های بدست آمده را برای شکسته شدن انتخاب می‌کنیم و تا زمانی که K خوشه را بدست آوریم این روال را ادامه می‌دهیم. بدین ترتیب مشکل انتخاب نقاط ابتدایی را که در الگوریتم K-Means با آن مواجه بودیم نداشته و بسیار کاراتر از آن می‌باشد.

2-1-4- الگوریتم خوشه بندی Fuzzy C-Means:
کارائی این الگوریتم نسبت به الگوریتم K-Means کاملاً بالاتر می‌باشد و دلیل آن به نوع نگاهی است که این الگوریتم به مفهوم خوشه و اعضای آن دارد. در واقع نقطه قوت الگوریتم Fuzzy C-Means این است که الگوریتمی همواره همگراست. در این الگوریتم تعداد خوشه‌ها برابر با C بوده (مشابه الگوریتم K-Means) ولی برخلاف الگوریتم K-Means که در آن هر رکورد تنها به یکی از خوشه‌های موجود تعلق دارد، در این الگوریتم هر کدام از رکوردهای مجموعه داده به تمامی خوشه‌ها متعلق است. البته این میزان تعلق با توجه به عددی که درجه عضویت تعلق هر رکورد را نشان می‌دهد، مشخص می‌شود. بدین ترتیب عملاً تعلق فازی هر رکورد به تمامی خوشه‌ها سبب خواهد شد که امکان حرکت ملایم عضویت هر رکورد به خوشه‌های مختلف امکان پذیر شود. بنابراین در این الگوریتم امکان تصحیح خطای تخصیص ناصحیح رکوردها به خوشه‌ها ساده‌تر می‌باشد و مهم‌ترین نقطه ضعف این الگوریتم در قیاس با K-Means زمان محاسبات بیشتر آن می‌باشد. می‌توان پذیرفت که از سرعت در عملیات خوشه بندی در برابر رسیدن به دقت بالاتر می‌توان صرفه نظر نمود.

2-2- خوشه بندی سلسله مراتبی (Connectivity Based Clustering (Hierarchical Clustering:
در پایان این عملیات یک مجموعه از خوشه‌های تودرتو به شکل سلسله مراتبی و در قالب ساختار درختی خوشه بندی بدست می‌آید که با استفاده از نمودار Dendrogram چگونگی شکل گیری خوشه‌های تودرتو را می‌توان نمایش داد. این نمودار درخت مانند، ترتیبی از ادغام و تجزیه را برای خوشه‌های تشکیل شده ثبت می‌کند، یکی از نقاط قوت این روش عدم اجبار برای تعیین تعداد خوشه‌ها می‌باشد (بر خلاف خوشه بندی افرازی). الگوریتم‌های مبتنی بر خوشه بندی سلسله مراتبی به دو دسته مهم تقسیم بندی می‌شوند:

2-2-1- روش‌های خوشه بندی تجمیعی (Agglomerative Clustering) :

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

2-2-2- روش ‌های خوشه بندی تقسیمی (Divisive Clustering) :

با یک خوشه‌ی دربرگیرنده‌ی همه نقاط کار را آغاز نموده و در هر مرحله، خوشه را می‌شکنیم تا زمانی که K خوشه بدست آید و یا در هر خوشه یک نقطه باقی بماند.

2-3- خوشه بندی مبتنی بر چگالی (Density Based Clustering):
تقسیم مجموعه داده به زیرمجموعه هایی که چگالی و چگونگی توزیع رکوردها در آنها لحاظ می‌شود. در این الگوریتم مهمترین فاکتور که جهت تشکیل خوشه‌ها در نظر گرفته می‌شود، تراکم و یا چگالی نقاط می‌باشد. بنابراین برخلاف دیگر روش‌های خوشه بندی که در آنها تراکم نقاط اهمیت نداشت، در این الگوریتم سعی می‌شود تنوع فاصله هایی که نقاط با یکدیگر دارند، در عملیات خوشه بندی مورد توجه قرار گیرد. الگوریتم DBSCAN مشهورترین الگوریتم خوشه بندی مبتنی بر چگالی است.

به طور کلی عملکرد یک الگوریتم خوشه بندی نسبت به الگوریتم‌های دیگر، بستگی کاملی به ماهیت مجموعه داده و معنای آن دارد.

3- کشف قوانین انجمنی :
الگوریتم‌های کاشف قوانین انجمنی نیز همانند الگوریتم‌های خوشه بندی به صورت روش‌های توصیفی یا بدون ناظر طبقه بندی می‌شوند. در این الگوریتم‌ها بدنبال پیدا کردن یک مجموعه از قوانین وابستگی یا انجمنی در میان تراکنش‌ها (برای مثال تراکنشهای خرید در فروشگاه، تراکنشهای خرید و فروش سهام در بورس و ...) هستیم تا براساس قوانین کشف شده بتوان میزان اثرگذاری اشیایی را بر وجود مجموعه اشیاء دیگری بدست آورد. خروجی در این روش کاوش، به صورت مجموعه ای از قوانین «اگر-آنگاه» است، که بیانگر ارتباطات میان رخداد توامان مجموعه ای از اشیاء با یکدیگر می‌باشد. به بیان دیگر این قوانین می‌تواند به پیش بینی وقوع یک مجموعه اشیاء مشخص در یک تراکنش، براساس وقوع اشیاء دیگر موجود در آن تراکنش بپردازد. ذکر این نکته ضروری است که بدانیم قوانین استخراج شده تنها استلزام یک ارتباط میان وقوع توامان مجموعه ای از اشیاء را نشان می‌دهد و در مورد چرایی یا همان علیت این ارتباط سخنی به میان نمی‌آورد. در ادامه به معرفی مجموعه ای از تعاریف اولیه در این مبحث می‌پردازیم (در تمامی تعاریف تراکنش‌های سبد خرید مشتریان در یک فروشگاه را به عنوان مجموعه داده مورد کاوش در نظر بگیرید):
•  مجموعه اشیاء: مجموعه ای از یک یا چند شیء. منظور از مجموعه اشیاء K عضوی، مجموعه ای است که شامل K شیء باشد.
برای مثال:{مسواک، نان، شیر}
•  تعداد پشتیبانی (Support Count) : فراوانی وقوع مجموعه‌ی اشیاء در تراکنش‌های موجود که آنرا با حرف σ نشان می‌دهیم.
برای مثال: 2=({مسواک، نان، شیر})σ
•  مجموعه اشیاء مکرر (Frequent Item Set) : مجموعه ای از اشیاء که تعداد پشتیبانی آنها بزرگتر یا مساوی یک مقدار آستانه (Min Support Threshold) باشد، مجموعه اشیاء مکرر نامیده می‌شود.
•  قوانین انجمنی: بیان کننده ارتباط میان اشیاء در یک مجموعه از اشیاء مکرر. این قوانین معمولاً به شکل X=>Y هستند.
برای مثال:{نوشابه}<={مسواک، شیر}

مهمترین معیارهای ارزیابی قوانین انجمنی عبارتند از:
 Support: کسری از تراکنش‌ها که حاوی همه اشیاء یک مجموعه اشیاء خاص هستند و آنرا با حرف S نشان می‌دهند.
برای مثال: 2.2=({نان، شیر})S
 Confidence: کسری از تراکنش‌های حاوی همه اشیاء بخش شرطی قانون انجمنی که صحت آن قانون را نشان می‌دهد که با آنرا حرف C نشان می‌دهند. برخلاف Support نمی‌توانیم مثالی برای اندازه گیری Confidence یک مجموعه اشیاء بیاوریم زیرا این معیار تنها برای قوانین انجمنی قابل محاسبه است.

با در نظر گرفتن قانون X=>Y می‌توان Support را کسری از تراکنش هایی دانست که شامل هر دو مورد X و Y هستند و Confidence برابر با اینکه چه کسری از تراکنش هایی که Y را شامل می‌شوند در تراکنش هایی که شامل X نیز هستند، ظاهر می‌شوند. هدف از کاوش قوانین انجمنی پیدا کردن تمام قوانین Rx است که از این دستورات تبعیت می‌کند:
 

در این دستورات منظور از SuppMIN و ConfMIN به ترتیب عبارت است از کمترین مقدار برای Support و Confidence که بایست جهت قبول هر پاسخ نهائی به عنوان یک قانون با ارزش مورد توجه قرار گیرد. کلیه قوانینی که از مجموعه اشیاء مکرر یکسان ایجاد می‌شوند دارای مقدار Support مشابه هستند که دقیقاً برابر با تعداد پشتیبانی یا همان σ شیء مکرری است که قوانین انجمنی با توجه به آن تولید شده اند. به همین دلیل فرآیند کشف قوانین انجمنی را می‌توان به دو مرحله مستقل «تولید مجموعه اشیاء مکرر» و «تولید قوانین انجمنی مطمئن» تقسیم نمائیم.
در مرحله نخست، تمام مجموعه اشیاء که دارای مقدار Support  ≥ SuppMIN  می‌باشند را تولید می‌کنیم. رابطه I
در مرحله دوم با توجه به مجموعه اشیاء مکرر تولید شده، قوانین انجمنی با اطمینان بالا بدست می‌آیند که همگی دارای شرط Confidence  ≥ ConfMIN هستند. رابطه II

3-1- الگوریتم های  Apriori ، Brute-Force و FP-Growth:
یک روش تولید اشیاء مکرر روش Brute-Force است که در آن ابتدا تمام قوانین انجمنی ممکن لیست شده، سپس مقادیر Support و Confidence برای هر قانون محاسبه می‌شود. در نهایت قوانینی که از مقادیر آستانه‌ی SuppMIN و ConfMIN تبعیت نکنند، حذف می‌شوند. تولید مجموعه اشیاء مکرر بدین طریق کاری بسیار پرهزینه و پیچیده ای می‌باشد، در واقع روش‌های هوشمندانه دیگری وجود دارد که پیچیدگی بالای روش Brute-Force را ندارند زیرا کل شبکه مجموعه اشیاء را به عنوان کاندید در نظر نمی‌گیرند. همانند تولید مجموعه اشیاء مکرر، تولید مجموعه قوانین انجمنی نیز بسیار پرهزینه و گران است.
چنانچه یک مجموعه اشیاء مکرر مشخص با d شیء را در نظر بگیریم، تعداد کل قوانین انجمنی قابل استخراج از رابطه III محاسبه می‌شود. (برای مثال تعداد قوانین انجمنی قابل استخراج از یک مجموعه شیء 6 عضوی برابر با 602 قانون می‌باشد، که با توجه به رشد d؛ سرعت رشد تعداد قوانین انجمنی بسیار بالا می‌باشد.)
الگوریتم‌های متعددی برای تولید مجموعه اشیاء مکرر وجود دارد برای نمونه الگوریتم‌های Apriori و FP-Growth که در هر دوی این الگوریتم ها، ورودی الگوریتم لیست تراکنش‌ها و پارامتر SuppMIN می‌باشد. الگوریتم Apriori روشی هوشمندانه برای یافتن مجموعه اشیاء تکرار شونده با استفاده از روش تولید کاندید است که از یک روش بازگشتی برای یافتن مجموعه اشیاء مکرر استفاده می‌کند. مهمترین هدف این الگوریتم تعیین مجموعه اشیاء مکرری است که تعداد تکرار آنها حداقل برابر با SuppMIN باشد. ایده اصلی در الگوریتم Apriori این است که اگر مجموعه اشیایی مکرر باشد، آنگاه تمام زیر مجموعه‌های آن مجموعه اشیاء نیز باید مکرر باشند. در واقع این اصل همواره برقرار است زیرا Support یک مجموعه شیء هرگز بیشتر از Support زیرمجموعه‌های آن مجموعه شیء نخواهد بود. مطابق با این ایده تمام ابرمجموعه‌های مربوط به مجموعه شیء نامکرر از شبکه مجموعه اشیاء حذف خواهند شد (هرس می‌شوند). هرس کردن مبتنی بر این ایده را هرس کردن بر پایه Support نیز عنوان می‌کنند که باعث کاهش قابل ملاحظه ای از تعداد مجموعه‌های کاندید جهت بررسی (تعیین مکرر بودن یا نبودن مجموعه اشیاء) می‌شود.
الگوریتم FP-Growth در مقایسه با Apriori روش کارآمدتری برای تولید مجموعه اشیاء مکرر ارائه می‌دهد. این الگوریتم با ساخت یک درخت با نام FP-Tree سرعت فرآیند تولید اشیاء مکرر را به طور چشمگیری افزایش می‌دهد، در واقع با یکبار مراجعه به مجموعه تراکنش‌های مساله این درخت ساخته می‌شود. پس از ساخته شدن درخت با توجه به ترتیب نزولی Support مجموعه اشیاء تک عضوی (یعنی مجموعه اشیاء) مساله تولید مجموعه اشیاء مکرر به چندین زیر مسئله تجزیه می‌شود، که هدف در هر کدام از این زیر مساله ها، یافتن مجموعه اشیاء مکرری است که به یکی از آن اشیاء ختم خواهند شد.
الگوریتم Aprior علاوه بر تولید مجموعه اشیاء مکرر، اقدام به تولید مجموعه قوانین انجمنی نیز می‌نماید. در واقع این الگوریتم با استفاده از مجموعه اشیاء مکرر بدست آمده از مرحله قبل و نیز پارامتر ConfMIN قوانین انجمنی مرتبط را که دارای درجه اطمینان بالائی هستند نیز تولید می‌کند. به طور کلی Confidence دارای خصوصیت هماهنگی (Monotone) نیست ولیکن Confidence قوانینی که از مجموعه اشیاء یکسانی بوجود می‌آیند دارای خصوصیت ناهماهنگی هستند. بنابراین با هرس نمودن کلیه ابرقوانین انجمنی یک قانون انجمنی یا Confidence (Rx) ≥ ConfMIN در شبکه قوانین انجمنی (مشابه با شبکه مجموعه اشیاء) اقدام به تولید قوانین انجمنی می‌نمائیم. پس از آنکه الگوریتم با استفاده از روش ذکر شده، کلیه قوانین انجمنی با اطمینان بالا را در شبکه قوانین انجمنی یافت، اقدام به الحاق نمودن آن دسته از قوانین انجمنی می‌نماید که پیشوند یکسانی را در توالی قانون به اشتراک می‌گذارند و بدین ترتیب قوانین کاندید تولید می‌شوند.
 
جهت آشنائی بیشتر به List of machine learning concepts مراجعه نمائید.
مطالب
یکپارچه کردن ELMAH با WCF RIA Services

پیشتر در مورد ELMAH مطلبی را منتشر کرده بودم و اگر برنامه نویس ASP.NET هستید و با ELMAH آشنایی ندارید،‌ جدا نیمی از عمر کاری شما بر فنا است!
هاست پیش فرض یک WCF RIA Service هم یک برنامه‌ی ASP.NET است. بنابراین کلیه‌ی خطاهای رخ داده در سمت سرور را باید بتوان به نحوی لاگ کرد تا بعدا با مطالعه‌ی آن‌ها اطلاعات ارزشمندی را از نقایص برنامه در عمل و پیش از گوشزد شدن آن‌ها توسط کاربران، دریافت، بررسی و رفع کرد.
کلیه خطاها را لاگ می‌کنم تا:
- بدانم معنای جمله‌ی "برنامه کار نمی‌کنه" چی هست.
- بدون روبرو شدن با کاربران یا حتی سؤال و جوابی از آن‌ها بدانم دقیقا مشکل از کجا ناشی شده.
- بدانم رفتارهای عمومی کاربران که منجر به بروز خطا می‌شوند کدام‌ها هستند.
- بدانم در کدامیک از قسمت‌های برنامه تعیین اعتبار ورودی کاربران یا انجام نشده یا ضعیف و ناکافی است.
- بدانم زمانیکه دوستی (!) قصد پایین آوردن برنامه را با تزریق SQL داشته، دقیقا چه چیزی را وارد کرده، در کجا و چه زمانی؟
- بتوانم Remote worker خوبی باشم.

ELMAH هم برای لاگ کردن خطاهای مدیریت نشده‌ی یک برنامه‌ی ASP.NET ایجاد شده است. بنابراین باید بتوان این دو (WCF RIA Services و ELMAH) را به نحوی با هم سازگار کرد. برای اینکار نیاز است تا یک مدیریت کننده‌ی خطای سفارشی را با پیاده سازی اینترفیس IErrorHandler تهیه کنیم (تا خطاهای مدیریت نشده‌ی حاصل را به سمت ELMAH هدایت کند) و سپس آن‌را به کمک یک ویژگی یا Attribute به DomainService خود جهت لاگ کردن خطاها اعمال نمائیم. روش تعریف این Attribute را در کدهای بعد ملاحظه خواهید نمود (در اینجا نیاز است تا دو ارجاع را به اسمبلی‌های Elmah.dll که دریافت کرده‌اید و اسمبلی استاندارد System.ServiceModel نیز به پروژه اضافه نمائید):

//add a reference to "Elmah.dll"
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Web;

namespace ElmahWcf
{
public class HttpErrorHandler : IErrorHandler
{
#region IErrorHandler Members
public bool HandleError(Exception error)
{
return false;
}

public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (error == null)
return;

if (HttpContext.Current == null) //In case we run outside of IIS
return;

Elmah.ErrorSignal.FromCurrentContext().Raise(error);
}
#endregion
}
}

//add a ref to "System.ServiceModel" assembly
using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace ElmahWcf
{
public class ServiceErrorBehaviorAttribute : Attribute, IServiceBehavior
{
Type errorHandlerType;
public ServiceErrorBehaviorAttribute(Type errorHandlerType)
{
this.errorHandlerType = errorHandlerType;
}

#region IServiceBehavior Members

public void AddBindingParameters(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{ }

public void ApplyDispatchBehavior(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandler;
errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;
cd.ErrorHandlers.Add(errorHandler);
}
}

public void Validate(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{ }
#endregion
}
}
اکنون پس از تعریف ویژگی ServiceErrorBehavior، نوبت به اعمال آن می‌رسد. به فایل DomainService خود مراجعه کرده و یک سطر زیر را به آن اضافه نمائید:
    [ServiceErrorBehavior(typeof(HttpErrorHandler))] //Integrating with ELMAH
[EnableClientAccess()]
public partial class MyDomainService : LinqToEntitiesDomainService<myEntities>

در ادامه نحوه‌ی افزودن تعاریف متناظر با ELMAH به Web.Config برنامه ذکر شده است. این تعاریف برای IIS6 و 7 به بعد هم تکمیل گردیده است. خطاها هم به صورت فایل‌های XML در پوشه‌ای به نام Errors که به ریشه‌ی سایت اضافه خواهید نمود (یا هر پوشه‌ی دلخواه دیگری)، لاگ می‌شوند.
به نظر من این روش، از ذخیره سازی اطلاعات لاگ‌ها در دیتابیس بهتر است. چون اساسا زمانیکه خطایی رخ می‌دهد شاید مشکل اصلی همان ارتباط با دیتابیس باشد.
قسمت ارسال خطاها به صورت ایمیل نیز comment شده است که در صورت نیاز می‌توان آن‌را فعال نمود:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="elmah">
<section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah"/>
<section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
<section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
<section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah"/>
<section name="errorTweet" requirePermission="false" type="Elmah.ErrorTweetSectionHandler, Elmah"/>
</sectionGroup>
</configSections>

<elmah>
<security allowRemoteAccess="1" />
<errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/Errors" />
<!-- <errorMail
from="errors@site.net"
to="nasiri@site.net"
subject="prj-error"
async="true"
smtpPort="25"
smtpServer="mail.site.net"
noYsod="true" /> -->
</elmah>

<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
<add name="DomainServiceModule"
preCondition="managedHandler"
type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</modules>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add name="Elmah" verb="POST,GET,HEAD" path="myelmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
</handlers>
</system.webServer>
<system.web>
<globalization
requestEncoding="utf-8"
responseEncoding="utf-8"
/>
<authentication mode="Forms">
<!--one month ticket-->
<forms name=".403AuthV"
cookieless="UseCookies"
slidingExpiration="true"
protection="All"
path="/"
timeout="43200" />
</authentication>
<httpHandlers>
<add verb="POST,GET,HEAD" path="myelmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
</httpHandlers>
<httpModules>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
<add name="DomainServiceModule"
type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</httpModules>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</assemblies>
</compilation>
</system.web>
<connectionStrings>
</connectionStrings>
<system.serviceModel>
<serviceHostingEnvironment
aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
</system.serviceModel>
</configuration>
اکنون برای مثال به یکی از متدهای DomainService خود سطر زیر را اضافه کرده و برنامه را آزمایش کنید:
throw new Exception("This is an ELMAH test");

سپس به آدرس http://localhost/myelmah.axd مراجعه نموده و اطلاعات لاگ شده حاصل را بررسی کنید:


این روش با WCF Services های متداول هم کار می‌کند. فقط در این سرویس‌ها باید aspNetCompatibilityEnabled مطابق تگ‌های ذکر شده‌ی system.serviceModel فوق در web.config لحاظ شوند (این مورد به صورت پیش فرض در WCF RIA Services وجود دارد). همچنین ویژگی زیر نیز باید به سرویس شما اضافه گردد:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

منابع مورد استفاده:
Integrating ELMAH for a WCF Service
Making WCF and ELMAH play nice together
Getting ELMAH to work with WCF services



پ.ن.
اگر به خطاهای ASP.NET دقت کرده باشید که به yellow screen of death هم مشهور هستند (در مقابل صفحات آبی ویندوز!)، ابتدای آن خیلی بزرگ نوشته شده Server Error و سپس ادامه‌ی خطا. همین مورد دقیقا یادم هست که هر بار سبب بازخواست مدیران شبکه بجای برنامه نویس‌ها می‌شد! (احتمالا این هم یک نوع بدجنسی تیم ASP.NET برای گرفتن حال ادمین‌های شبکه است! و گرنه مثلا می‌توانستند همان ابتدا بنویسند program/application error بجای server error)

مطالب
تهیه XML امضاء شده جهت تولید مجوز استفاده از برنامه
اگر به فایل مجوز استفاده از برنامه‌‌ای مانند EF Profiler دقت کنید، یک فایل XML به ظاهر ساده بیشتر نیست:
<?xml version="1.0" encoding="utf-8"?>
<license id="17d46246-a6cb-4196-98a0-ff6fc08cb67f" expiration="2012-06-12T00:00:00.0000000" type="Trial" prof="EFProf">
  <name>MyName</name>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
      <Reference URI="">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue>b8N0bDE4gTakfdGKtzDflmmyyXI=</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>IPELgc9BbkD8smXSe0sGqp5vS57CtZo9ME2ZfXSq/thVu...=</SignatureValue>
  </Signature>
</license>
در این فایل ساده متنی، نوع مجوز استفاده از برنامه، Trial ذکر شده است. شاید عنوان کنید که خوب ... نوع مجوز را به Standard تغییر می‌دهیم و فایل را ذخیره کرده و نهایتا استفاده می‌کنیم. اما ... نتیجه حاصل کار نخواهد کرد! حتی اگر یک نقطه به اطلاعات این فایل اضافه شود، دیگر قابل استفاده نخواهد بود. علت آن هم به قسمت Signature فایل XML فوق بر می‌گردد.
در ادامه به نحوه تولید و استفاده از یک چنین مجوزهای امضاء شده‌ای در برنامه‌های دات نتی خواهیم پرداخت.


تولید کلیدهای RSA

برای تهیه امضای دیجیتال یک فایل XML نیاز است از الگوریتم RSA استفاده شود.
برای تولید فایل XML امضاء شده، از کلید خصوصی استفاده خواهد شد. برای خواندن اطلاعات مجوز (فایل XML امضاء شده)، از کلیدهای عمومی که در برنامه قرار می‌گیرند کمک خواهیم گرفت (برای نمونه برنامه EF Prof این کلیدها را در قسمت Resourceهای خود قرار داده است).
استفاده کننده تنها زمانی می‌تواند مجوز معتبری را تولید کند که دسترسی به کلیدهای خصوصی تولید شده را داشته باشد.
        public static string CreateRSAKeyPair(int dwKeySize = 1024)
        {
            using (var provider = new RSACryptoServiceProvider(dwKeySize))
            {
                return provider.ToXmlString(includePrivateParameters: true);
            }
        }
امکان تولید کلیدهای اتفاقی مورد استفاده در الگوریتم RSA، در دات نت پیش بینی شده است. خروجی متد فوق یک فایل XML است که به همین نحو در صورت نیاز توسط متد provider.FromXmlString مورد استفاده قرار خواهد گرفت.


تهیه ساختار مجوز

در ادامه یک enum که بیانگر انواع مجوزهای برنامه ما است را مشاهده می‌کنید:
namespace SignedXmlSample
{
    public enum LicenseType
    {
        None,
        Trial,
        Standard,
        Personal
    }
}
به همراه کلاسی که اطلاعات مجوز تولیدی را دربر خواهد گرفت:
using System;
using System.Xml.Serialization;

namespace SignedXmlSample
{
    public class License
    {
        [XmlAttribute]
        public Guid Id { set; get; }
        
        public string Domain { set; get; }

        [XmlAttribute]
        public string IssuedTo { set; get; }

        [XmlAttribute]
        public DateTime Expiration { set; get; }

        [XmlAttribute]
        public LicenseType Type { set; get; }
    }
}
خواص این کلاس یا عناصر enum یاد شده کاملا دلخواه هستند و نقشی را در ادامه بحث نخواهند داشت؛ از این جهت که از مباحث XmlSerializer برای تبدیل وهله‌ای از شیء مجوز به معادل XML آن استفاده می‌شود. بنابراین المان‌های آن‌را مطابق نیاز خود می‌توانید تغییر دهید. همچنین ذکر ویژگی XmlAttribute نیز اختیاری است. در اینجا صرفا جهت شبیه سازی معادل مثالی که در ابتدای بحث مطرح شد، از آن استفاده شده است. این ویژگی راهنمایی است برای کلاس XmlSerializer تا خواص مزین شده با آن‌را به شکل یک Attribute در فایل نهایی ثبت کند.


تولید و خواندن مجوز دارای امضای دیجیتال

کدهای کامل کلاس تولید و خواندن یک مجوز دارای امضای دیجیتال را در اینجا مشاهده می‌کنید:
using System;
using System.IO;
using System.Security.Cryptography; // needs a ref. to `System.Security.dll` asm.
using System.Security.Cryptography.Xml;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace SignedXmlSample
{
    public static class LicenseGenerator
    {
        public static string CreateLicense(string licensePrivateKey, License licenseData)
        {
            using (var provider = new RSACryptoServiceProvider())
            {
                provider.FromXmlString(licensePrivateKey);
                var xmlDocument = createXmlDocument(licenseData);
                var xmlDigitalSignature = getXmlDigitalSignature(xmlDocument, provider);
                appendDigitalSignature(xmlDocument, xmlDigitalSignature);
                return xmlDocumentToString(xmlDocument);
            }
        }

        public static string CreateRSAKeyPair(int dwKeySize = 1024)
        {
            using (var provider = new RSACryptoServiceProvider(dwKeySize))
            {
                return provider.ToXmlString(includePrivateParameters: true);
            }
        }

        public static License ReadLicense(string licensePublicKey, string xmlFileContent)
        {
            var doc = new XmlDocument();
            doc.LoadXml(xmlFileContent);

            using (var provider = new RSACryptoServiceProvider())
            {
                provider.FromXmlString(licensePublicKey);

                var nsmgr = new XmlNamespaceManager(doc.NameTable);
                nsmgr.AddNamespace("sig", "http://www.w3.org/2000/09/xmldsig#");

                var xml = new SignedXml(doc);
                var signatureNode = (XmlElement)doc.SelectSingleNode("//sig:Signature", nsmgr);
                if (signatureNode == null)
                    throw new InvalidOperationException("This license file is not signed.");

                xml.LoadXml(signatureNode);
                if (!xml.CheckSignature(provider))
                    throw new InvalidOperationException("This license file is not valid.");

                var ourXml = xml.GetXml();
                if (ourXml.OwnerDocument == null || ourXml.OwnerDocument.DocumentElement == null)
                    throw new InvalidOperationException("This license file is coruppted.");

                using (var reader = new XmlNodeReader(ourXml.OwnerDocument.DocumentElement))
                {
                    var xmlSerializer = new XmlSerializer(typeof(License));
                    return (License)xmlSerializer.Deserialize(reader);
                }
            }
        }

        private static void appendDigitalSignature(XmlDocument xmlDocument, XmlNode xmlDigitalSignature)
        {
            xmlDocument.DocumentElement.AppendChild(xmlDocument.ImportNode(xmlDigitalSignature, true));
        }

        private static XmlDocument createXmlDocument(License licenseData)
        {
            var serializer = new XmlSerializer(licenseData.GetType());
            var sb = new StringBuilder();
            using (var writer = new StringWriter(sb))
            {
                var ns = new XmlSerializerNamespaces(); ns.Add("", "");
                serializer.Serialize(writer, licenseData, ns);
                var doc = new XmlDocument();
                doc.LoadXml(sb.ToString());
                return doc;
            }
        }

        private static XmlElement getXmlDigitalSignature(XmlDocument xmlDocument, AsymmetricAlgorithm key)
        {
            var xml = new SignedXml(xmlDocument) { SigningKey = key };
            var reference = new Reference { Uri = "" };
            reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            xml.AddReference(reference);
            xml.ComputeSignature();
            return xml.GetXml();
        }

        private static string xmlDocumentToString(XmlDocument xmlDocument)
        {
            using (var ms = new MemoryStream())
            {
                var settings = new XmlWriterSettings { Indent = true, Encoding = Encoding.UTF8 };
                var xmlWriter = XmlWriter.Create(ms, settings);
                xmlDocument.Save(xmlWriter);
                ms.Position = 0;
                return new StreamReader(ms).ReadToEnd();
            }
        }
    }
}
توضیحات:
در حین کار با متد CreateLicense، پارامتر licensePrivateKey همان اطلاعاتی است که به کمک متد CreateRSAKeyPair قابل تولید است. توسط پارامتر licenseData، اطلاعات مجوز در حال تولید اخذ می‌شود. در این متد به کمک provider.FromXmlString، اطلاعات کلیدهای RSA دریافت خواهند شد. سپس توسط متد createXmlDocument، محتوای licenseData دریافتی به یک فایل XML نگاشت می‌گردد (بنابراین اهمیتی ندارد که حتما از ساختار کلاس مجوز یاد شده استفاده کنید). در ادامه متد getXmlDigitalSignature با در اختیار داشتن معادل XML شیء مجوز و کلیدهای لازم، امضای دیجیتال متناظری را تولید می‌کند. با استفاده از متد appendDigitalSignature، این امضاء را به فایل XML اولیه اضافه می‌کنیم. از این امضاء جهت بررسی اعتبار مجوز برنامه در متد ReadLicense استفاده خواهد شد.
برای خواندن یک فایل مجوز امضاء شده در برنامه خود می‌توان از متد ReadLicense استفاده کرد. توسط آرگومان licensePublicKey، اطلاعات کلید عمومی دریافت می‌شود. این کلید دربرنامه، ذخیره و توزیع می‌گردد. پارامتر xmlFileContent معادل محتوای فایل XML مجوزی است که قرار است مورد ارزیابی قرار گیرد.


مثالی در مورد نحوه استفاده از کلاس تولید مجوز

در ادامه نحوه استفاده از متدهای CreateLicense و  ReadLicense را ملاحظه می‌کنید؛ به همراه آشنایی با نمونه کلیدهایی که باید به همراه برنامه منتشر شوند:
using System;
using System.IO;

namespace SignedXmlSample
{
    class Program
    {
        static void Main(string[] args)
        {
            //Console.WriteLine(LicenseGenerator.CreateRSAKeyPair());
            writeLicense();            
            readLicense();

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

        private static void readLicense()
        {
            var xml = File.ReadAllText("License.xml");
            const string publicKey = @"<RSAKeyValue>
                                    <Modulus>
                                        mBNKFIc/LkMfaXvLlB/+6EujPkx3vBOvLu8jdESDSQLisT8K96RaDMD1ORmdw2XNdMw/6ZBuJjLhoY13qCU9t7biuL3SIxr858oJ1RLM4PKhA/wVDcYnJXmAUuOyxP/vfvb798o6zAC1R2QWuzG+yJQR7bFmbKH0tXF/NOcSgbc=
                                    </Modulus>
                                    <Exponent>
                                        AQAB
                                    </Exponent>
                                 </RSAKeyValue>";

            var result = LicenseGenerator.ReadLicense(publicKey, xml);

            Console.WriteLine(result.Domain);
            Console.WriteLine(result.IssuedTo);
        }

        private static void writeLicense()
        {
            const string rsaData = @"<RSAKeyValue>
                    <Modulus>
                        mBNKFIc/LkMfaXvLlB/+6EujPkx3vBOvLu8jdESDSQLisT8K96RaDMD1ORmdw2XNdMw/6ZBuJjLhoY13qCU9t7biuL3SIxr858oJ1RLM4PKhA/wVDcYnJXmAUuOyxP/vfvb798o6zAC1R2QWuzG+yJQR7bFmbKH0tXF/NOcSgbc=
                    </Modulus>
                    <Exponent>
                        AQAB
                    </Exponent>
                    <P>
                        xwPKN77EcolMTD2O2Csv6k9Y4aen8UBVYjeQ4PtrNGz0Zx6I1MxLEFzRpiKC/Ney3xKg0Icwj0ebAQ04d5+HAQ==
                    </P>
                    <Q>
                        w568t0Xe6OBUfCyAuo7tTv4eLgczHntVLpjjcxdUksdVw7NJtlnOLApJVJ+U6/85Z7Ji+eVhuN91yn04pQkAtw==
                    </Q>
                    <DP>
                        svkEjRdA4WP5uoKNiHdmMshCvUQh8wKRBq/D2aAgq9fj/yxlj0FdrAxc+ZQFyk5MbPH6ry00jVWu3sY95s4PAQ==
                    </DP>
                    <DQ>
                        WcRsIUYk5oSbAGiDohiYeZlPTBvtr101V669IUFhhAGJL8cEWnOXksodoIGimzGBrD5GARrr3yRcL1GLPuCEvQ==
                    </DQ>
                    <InverseQ>
                        wIbuKBZSCioG6MHdT1jxlv6U1+Y3TX9sHED9PqGzWWpVGA+xFJmQUxoFf/SvHzwbBlXnG0DLqUvxEv+BkEid2w==
                    </InverseQ>
                    <D>                        Yk21yWdT1BfXqlw30NyN7qNWNuM/Uvh2eaRkCrhvFTckSucxs7st6qig2/RPIwwfr6yIc/bE/TRO3huQicTpC2W3aXsBI9822OOX4BdWCec2txXpSkbZW24moXu+OSHfAdYoOEN6ocR7tAGykIqENshRO7HvONJsOE5+1kF2GAE=
                    </D>
                  </RSAKeyValue>";

            string data = LicenseGenerator.CreateLicense(
                                                rsaData,
                                                new License
                                                {
                                                     Id = Guid.NewGuid(),
                                                     Domain = "dotnettips.info",
                                                     Expiration = DateTime.Now.AddYears(2),
                                                     IssuedTo = "VahidN",
                                                     Type = LicenseType.Standard
                                                });
            File.WriteAllText("License.xml", data);
        }
    }
}
ابتدا توسط متد CreateRSAKeyPair کلیدهای لازم را تهیه و ذخیره کنید. این کار یکبار باید صورت گیرد.
همانطور که مشاهده می‌کنید، اطلاعات کامل یک نمونه از آن، در متد writeLicense مورد نیاز است. اما در متد readLicense تنها به قسمت عمومی آن یعنی Modulus و Exponent نیاز خواهد بود (موارد قابل انتشار به همراه برنامه).

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

دریافت نسخه جنریک این مثال:
SignedXmlSample.zip
نظرات مطالب
افزودن و اعتبارسنجی خودکار Anti-Forgery Tokens در برنامه‌های Angular مبتنی بر ASP.NET Core
یک چنین پیاده سازی خواهد داشت:
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Helpers;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Web.Mvc;
using ActionFilterAttribute = System.Web.Http.Filters.ActionFilterAttribute;

namespace NgxAntiforgeryWebApi.Providers
{
    public class XsrfCookieGeneratorAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            var xsrfTokenCookie = new HttpCookie("XSRF-TOKEN")
            {
                Value = ComputeXsrfTokenValue(),
                HttpOnly = false // Now JavaScript is able to read the cookie
            };
            HttpContext.Current.Response.AppendCookie(xsrfTokenCookie);
        }

        private string ComputeXsrfTokenValue()
        {
            string cookieToken, formToken;
            AntiForgery.GetTokens(null, out cookieToken, out formToken);
            return $"{cookieToken}:{formToken}";
        }
    }

    public class XsrfTokensValidationAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            IEnumerable<string> headerValues;
            if (!actionContext.Request.Headers.TryGetValues("X-XSRF-TOKEN", out headerValues))
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = "X-XSRF-TOKEN header is missing." };
                return;
            }

            if (headerValues == null)
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = "X-XSRF-TOKEN header value is empty." };
                return;
            }

            var xsrfTokensValue = headerValues.FirstOrDefault();
            if (string.IsNullOrEmpty(xsrfTokensValue) || !xsrfTokensValue.Contains(":"))
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = "X-XSRF-TOKEN header value is null." };
                return;
            }

            var values = xsrfTokensValue.Split(':');
            if (values.Length != 2)
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = "X-XSRF-TOKEN header value is malformed." };
                return;
            }

            var cookieToken = values[0];
            var formToken = values[1];

            try
            {
                AntiForgery.Validate(cookieToken, formToken);
            }
            catch (HttpAntiForgeryException ex)
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest) {  ReasonPhrase = ex.Message };
            }
        }
    }
}
XsrfCookieGeneratorAttribute کار تولید کوکی مخصوص Angular را انجام می‌دهد (می‌تواند به عنوان فیلتر سراسری معرفی شود و یا فقط یکبار پس از لاگین، کوکی آن‌را به روشی که عنوان شده، تولید کنید؛ بدون نیاز به تولید هرباره‌ی آن با هر درخواستی) و XsrfTokensValidationAttribute بجای ValidateAntiForgeryToken اصلی بکار خواهد رفت.
نظرات مطالب
طراحی گردش کاری با استفاده از State machines - قسمت اول
با توجه به اینکه اطلاعات مربوط به State ها، همینطور Transition‌ها و ... در بانک اطلاعاتی ذخیره می‌شود  برای به گردش درآوردن باید با fetch کردن داده‌ها از بانک اطلاعات یک Object را به گردش درآوریم. حال برای ساختن و کانفیگ کلاس مربوط به State Machine به نظر می‌رسد یک راه این باشد: 
 
private StateMachine<string, string> stateMachine;
        private StateMachineCOM source;
        private string startState;
        public delegate void UnhandledTriggerDelegate(State state,  StateConfig trigger);
        public delegate void EntryExitDelegate();
        public delegate bool GuardClauseDelegate();
        public string Id;
        public EntryExitDelegate OnEntry = null;
        public EntryExitDelegate OnExit = null;
        public GuardClauseDelegate GuardClauseFromToTrigger = null;
        public UnhandledTriggerDelegate OnUnhandledTrigger = null;

        public StateMachineRequest(StateMachineCOM source, string startStateId)
        {
            this.source = source;   
            this.startState = startStateId;
        }

        public void Configure() 
        {
            this.stateMachine = new StateMachine<string, string>(startState);
            var states = source.States;
            states.ForEach(state =>
            {
                var triggers = source.StateConfigs.AsQueryable()
                                   .Where(config => config.FromStateId == state.StateId)
                                   .Select(config => new {Id=config.TransitionId.ToString(), From= config.FromStateId.ToString(), To= config.ToStateId.ToString(), Permit=config.PermiteAction })
                                   .ToList();

                triggers.ForEach(trig =>
                {
                    this.stateMachine.Configure(state.StateId.ToString())
                });
            });
        }

        public bool TryFireTrigger(string TrigerId)
        {
            if (!stateMachine.CanFire(TrigerId))
            {
                return false;
            }
            stateMachine.Fire(TrigerId);
            return true;
        }
        
        public string GetCurrentState()
        {
            return this.stateMachine.State;
        }

باشد یعنی State‌ها Transition‌ها و ... را بعد از Fetch کردن از بانک اطلاعاتی به State Machine ارسال کنیم. حالا برای در نظر گرفتن شروط مربوط به OnEntry و OnExit یا GuardClauseFromToTrigger پیشنهاد شما توجه به اینکه براساس State می‌بایست این متدها ساخته شوند چیست؟
- آیا بهتر است delegate پارامتر دریافت کند؟
اگر بله پیاده سازی آن در هنگام کانفیگ به چه صورت است؟ به این صورت ؟
this.stateMachine.Configure(state.StateId.ToString())
                        .OnEntry(() => { if (state.OnEnter) OnEntry(trig.Id);})
                        .OnExit(() => { if (state.OnExit) OnExit(trig.Id); })
                        .PermitIf(trig.From, trig.To, () => { if (trig.Permit) return GuardClauseFromToTrigger(); return true; });
- اگر خیر چگونه می‌توان این متدها را بصورت دینامیک ایجاد کرد و به هنگام کانفیگ ماشین حالت به آن انتساب داد و بعد در هنگام گردش آبجکت به آن دسترسی داشت؟
مطالب
کامپایل پویای کد در دات نت

در دات نت فریم ورک امکان کامپایل پویای یک قطعه کد دریافت شده از یک رشته، توسط فضای نام 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- دادن اجازه‌ی کد نویسی به کاربران برنامه‌ی خود (شبیه به سیستم‌های ماکرو و اسکریپت نویسی موجود)
و ...

اشتراک‌ها
نکات افزایش پرفرمنس برای وبسایت

In this post, I'll talk about what the different things I've made on this website to improve the performance. Some of the optimizations are just some configuration flags to turn on, others require more changes in your code.

  • Enable HTTP/2
  • Enable TLS 1.3
  • Compress responses using Brotli or gzip
  • Add caching information
  • Optimize JavaScript / CSS files
  • Reduce the number of redirections
  • Optimize images
  • Move your servers near your visitors (GeoDNS, CDN)
  • Resource Hints: Prefetch resources
  • Remove unused resources / features
  • Minify HTML
  • Optimize JavaScript code
  • Automate almost everything! 
نکات افزایش پرفرمنس برای وبسایت
اشتراک‌ها
SQL Server® 2014 Service Pack 1 منتشر شد

A few of the customer-requested updates in Microsoft SQL Server 2014 SP1 are:

  • Column store performance is improved when batch mode operators spill over to disk. A new XEvent provides insights into column store inserts.
  • Several issues in buffer pool extension SSD configuration are addressed, including the ability to do instant initialization of the buffer pool extension file.
  • Certain queries compile faster when using new cardinality estimator, and other cardinality estimation query plans are more efficient when using TF-4199. 
  • The scalability benefits of two trace flags (TF-1236, TF-9024) are applied automatically.
  • Backup header information retrieval works better with encrypted backups.
SQL Server® 2014 Service Pack 1 منتشر شد
نظرات مطالب
نحوه ایجاد یک تصویر امنیتی (Captcha) با حروف فارسی در ASP.Net MVC
سلام، از نظر شما متشکرم

همون طور که آقای محسن خان گفت، احتمالا هاست شما medium trust هست. اما رو کامپیوتر خودتون full trust برنامه نویسی می‌کنید.

چون هویت کاربر هنوز مشخص نشده پیغامی مبنی بر این لاگ میشه که اسمبلی mscorlib وجود نداره. در واقع وجود داره ولی نه برای کاربر anonymous ! همچنین این به دلیل medium trust بودن هم میتونه باشه. برای حل این مشکل کارهای زیر رو انجام بدین:

1) به فایل web.config برین و کد زیر رو اضافه کنید:
<trust level="Full" originUrl=".*" />

2) باید تغییری رو در متدهای الحاقی Encrypt و Decrypt بدین، که از این متدها برای رمزنگاری و رمزگشایی محتوای کوکی تصویر امنیتی استفاده میشه. قبل از هر کدوم از این متدها flag زیر رو اضافه کنید:

[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Assert, Unrestricted = true)]

همچنین، یک خط داخل بدنه هر کدوم از متدهای الحاقی Encrypt و Decrypt هست، منظورم این خط کد هست:
var cspp = new CspParameters { KeyContainerName = key };
که باید به خط کد زیر تبدیل بشه:
var cspp = new CspParameters { KeyContainerName = key, Flags = CspProviderFlags.UseMachineKeyStore };

اشتراک‌ها
آموزش رایگان برنامه نویسی Full-Stack به زبان فارسی

C#.NET for non-engineers.

The first course of "A Sr. Developer Course" courses. which contains:
1- C# Fundamentals for non-engineers.
2- DataBase for non-engineers.
3- Asp.NET WebForm for Non-engineers.
4- Application Architecture for no-engineers.
5- ASP.NET MVC for non-engineers.
6- Angular for non-engineers.

This is a course for who knows noting about C# and development if you know nothing about Array, variable, loop, and conditions you are in the right place.
at the end of this course, we will create one small university registration console application together.

You will learn in this course:
C#.NET
.NET Framework
Methods
Recursive methods
C# Primitive Types/Complex Types
conditions
switch case
Arrays
if statement
switch
loops
Creating a method
ref, out
enums
OOP/Object-oriented programing
Generics
Error handling
problem-solving
working with files


level: beginners to upper intermediate 

آموزش رایگان برنامه نویسی Full-Stack به زبان فارسی