اشتراک‌ها
بررسی CSRF و SOP

Same-Origin Policy یک سازوکار امنیتی در مرورگرها هستش که ارتباط مستندات (Documents) و Script­ها رو از یک Origin با یک Origin دیگه، محدود میکنه و برای اون­‌ها قانون وضع می­کنه. این سازوکار کمک می­کنه که مستندات آلوده ایزوله باشن و از حملات احتمالی جلوگیری بشه. به این صورت که مرورگر اجازه نمیده که یک Origin محتوایی از Origin دیگه­‌ای بخونه و یا به اون دسترسی داشته باشه. 

بررسی CSRF و SOP
نظرات مطالب
طراحی یک ماژول IpBlocker در ASP.NET MVC
درستش همین حالت فعلی هست. چون درخواست تصاویر و یا اسکریپت‌ها و CSSها هم درخواست واقعی به سرور هستند و باید در محاسبات AntiDos لحاظ شوند. عدد 20 هم کم است. عدد را روی 500 یا 1000 قرار دهید. کسانیکه شروع می‌کنند به حمله، خیلی سریع این 1000 تا را رد می‌کنند؛ در چند ثانیه فقط. هدف ماژول AntiDos این نوع حملات است و نه تداخل با کار عادی کاربران و یا حتی موتورهای جستجو را هم باید مدنظر داشته باشید.
نظرات مطالب
مدیریت دانلود‌های همزمان از یک سایت و بحث تایم آوت
برای دور زدن دانلوداز سرورهای مختلف که مشکل محدودیت کانکشن فعال دارند هم می شود از اینها استفاده کرد ؟ البته من این کار رو برای دانلود نمی خوام ، بلکه قصد نهایی باز کردن تعداد بسیار زیاد کانکشن به یک سرور خاص و نهایتا از کار انداختن آن است . چیزی مثل حملات DDOS .
البته من فکر نمی کنم این حرفی که شما زدید در این مورد که من گفتم کار کند چون معمولا Firewallهایی که روی سرور هست این اجازه رو به شما نمیده . و نهایتا همه رو می بنده .
در این مورد می تونید راهنمایی کنه ؟
نظرات مطالب
روشی برای DeSerialize کردن QueryString به یک کلاس
جهت افزودن پشتیبانی از حالت POST کد متد را به شکل زیر تغییر دهید:
 public T GetFromQueryString<T>(RequestType type=RequestType.GET) where T : new()
        {
            var obj = new T();
            var properties = typeof(T).GetProperties();

            var queries =new NameValueCollection();
            if(type==RequestType.GET)
                queries= HttpContext.Current.Request.QueryString;
            else
            {
                queries = HttpContext.Current.Request.Form;
            }

            foreach (var property in properties)
            {
                foreach (Attribute attribute in property.GetCustomAttributes(true))
                {
                    var requestBodyField = attribute as RequestBodyField; 
                    if (requestBodyField == null) continue;

                    //get value of query string
                    var valueAsString = queries[requestBodyField.Field];

                    if (valueAsString == null)
                    {
                        var keys = from key in queries.AllKeys where key.StartsWith(requestBodyField.Field) select key;

                        if(!keys.Any())
                            continue;

                        var collection = new NameValueCollection();

                        foreach (var key in keys)
                        {
                            var openBraketIndex = key.IndexOf("[", StringComparison.Ordinal);
                            var closeBraketIndex = key.IndexOf("]", StringComparison.Ordinal);

                            if (openBraketIndex < 0 || closeBraketIndex < 0)
                                throw new Exception("query string is crupted.");

                            openBraketIndex++;
                            //get key in [...]
                            var fieldName = key.Substring(openBraketIndex, closeBraketIndex - openBraketIndex);
                            collection.Add(fieldName, queries[key]);
                        }
                        property.SetValue(obj, collection, null);
                        continue;
                    }

                    var converter = TypeDescriptor.GetConverter(property.PropertyType);
                    var value = converter.ConvertFrom(valueAsString);

                    if (value == null)
                        continue;

                    property.SetValue(obj, value, null);
                }
            }
            return obj;
        }
مطالب
مقابله با XSS ؛ یکبار برای همیشه!

ASP.NET به صورت پیش فرض در مقابل ارسال هر نوع تگی عکس العمل نشان می‌دهد و پیغام خطای یافتن خطری بالقوه را گوشزد می‌کند. اما بین خودمان باشد، همه این قابلیت را خاموش می‌کنند! چون در یک برنامه واقعی نیاز است تا مثلا کاربران تگ html هم ارسال کنند. برای نمونه یک ادیتور متنی پیشرفته را درنظر بگیرید. خاموش کردن این قابلیت هم مساوی است با فراهم کردن امکان ارسال تگ‌های مجاز و در کنار آن بی دفاع گذاشتن برنامه در مقابل حملات XSS.
توصیه هم این است که همه جا از توابع مثلا HtmlEncode و موارد مشابه حتما استفاده کنید. ولی باز هم خودمونیم ... چند نفر از شماها اینکار را می‌کنید؟!
بهترین کار در این موارد وارد شدن به pipe line پردازشی ASP.NET و دستکاری آن است! اینکار هم توسط HttpModules میسر است. به عبارتی در ادامه می‌خواهیم ماژولی را بنویسیم که کلیه تگ‌های ارسالی کوئری استرینگ‌ها را پاک کرده و همچنین تگ‌های خطرناک موجود در مقادیر ارسالی فرم‌های برنامه را هم به صورت خودکار حذف کند. اما هنوز اجازه بدهد تا کاربران بتوانند تگ HTML هم ارسال کنند.
مشکل! در ASP.NET مقادیر ارسالی کوئری استرینگ‌ها و همچنین فرم‌ها به صورت NameValueCollection در اختیار برنامه قرار می‌گیرند و ... خاصیت IsReadOnly این مجموعه‌ها در حین ارسال، به صورت پیش فرض true است و همچنین غیرعمومی! یعنی به همین سادگی نمی‌توان عملیات تمیزکاری را روی مقادیر ارسالی، پیش از مهیا شدن آن جهت استفاده در برنامه اعمال کرد. بنابراین در ابتدای کار نیاز است با استفاده از قابلیت Reflection ، اندکی در سازوکار داخلی ASP.NET دست برد، این خاصیت فقط خواندنی غیرعمومی را برای مدت کوتاهی false کرد و سپس مقصود نهایی را اعمال نمود. پیاده سازی آن را در ادامه مشاهده می‌کنید:
using System;
using System.Collections.Specialized;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using Microsoft.Security.Application;
namespace AntiXssMdl { public class AntiXssModule : IHttpModule { private static readonly Regex _cleanAllTags = new Regex("<[^>]+>", RegexOptions.Compiled); public void Init(HttpApplication context) { context.BeginRequest += CleanUpInput; }
public void Dispose() { }
private static void CleanUpInput(object sender, EventArgs e) { HttpRequest request = ((HttpApplication)sender).Request; if (request.QueryString.Count > 0) { //تمیزکاری مقادیر کلیه کوئری استرینگ‌ها پیش از استفاده در برنامه CleanUpAndEncode(request.QueryString, allowHtmltags: false); }
if (request.HttpMethod == "POST") { //تمیزکاری کلیه مقادیر ارسالی به سرور if (request.Form.Count > 0) { CleanUpAndEncode(request.Form, allowHtmltags: true); } } }
private static void CleanUpAndEncode(NameValueCollection collection, bool allowHtmltags) { //اندکی دستکاری در سیستم داخلی دات نت PropertyInfo readonlyProperty = collection .GetType() .GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic); readonlyProperty.SetValue(collection, false, null);//IsReadOnly=false
for (int i = 0; i < collection.Count; i++) { if (string.IsNullOrWhiteSpace(collection[i])) continue;
if (!allowHtmltags) { //در حالت کوئری استرینگ دلیلی برای ارسال هیچ نوع تگی وجود ندارد collection[collection.Keys[i]] = AntiXss.HtmlEncode(_cleanAllTags.Replace(collection[i], string.Empty)); } else { //قصد تمیز سازی ویوو استیت را نداریم چون در این حالت وب فرم‌ها از کار می‌افتند if (collection.Keys[i].StartsWith("__VIEWSTATE")) continue; //در سایر موارد کاربران مجازند فقط تگ‌های سالم را ارسال کنند و مابقی حذف می‌شود collection[collection.Keys[i]] = Sanitizer.GetSafeHtml(collection[i]); } }
readonlyProperty.SetValue(collection, true, null);//IsReadOnly=true } } }

در این کلاس از کتابخانه AntiXSS مایکروسافت استفاده شده است. آخرین نگارش آن‌را از اینجا دریافت نمائید. نکته مهم آن متد Sanitizer.GetSafeHtml است. به کمک آن با خیال راحت می‌توان در یک سایت، از یک ادیتور متنی پیشرفته استفاده کرد. کاربران هنوز می‌توانند تگ‌های HTML را ارسال کنند؛ اما در این بین هرگونه سعی در ارسال عبارات و تگ‌های حاوی حملات XSS پاکسازی می‌شود.

و یک وب کانفیگ نمونه برای استفاده از آن به صورت زیر می‌تواند باشد (تنظیم شده برای IIS6 و 7):
<?xml version="1.0"?>
<configuration>
<system.web>
  <pages validateRequest="false" enableEventValidation="false" />
  <httpRuntime requestValidationMode="2.0" />
  <compilation debug="true" targetFramework="4.0" />
  <httpModules>
    <add name="AntiXssModule" type="AntiXssMdl.AntiXssModule"/>
  </httpModules>
</system.web>
<system.webServer> <validation validateIntegratedModeConfiguration="false"/> <modules> <add name="AntiXssModule" type="AntiXssMdl.AntiXssModule"/> </modules> </system.webServer> </configuration>

برای مثال به تصویر زیر دقت کنید. ماژول فوق، فقط تگ‌های سبز رنگ را (حین ارسال به سرور) مجاز دانسته، اسکریپت ذیل لینک را کلا حذف کرده و تگ‌های موجود در کوئری استرینگ را هم نهایتا (زمانیکه در اختیار برنامه قرار می‌گیرد) حذف خواهد کرد.

دریافت نسخه جدید و نهایی این مثال
مطالب
EF Code First #1

در ادامه بحث ASP.NET MVC می‌شود به ابزاری به نام MVC Scaffolding اشاره کرد. کار این ابزار که توسط یکی از اعضای تیم ASP.NET MVC به نام استیو اندرسون تهیه شده، تولید کدهای اولیه یک برنامه کامل ASP.NET MVC از روی مدل‌های شما می‌باشد. حجم بالایی از کدهای تکراری آغازین برنامه را می‌شود توسط این ابزار تولید و بعد سفارشی کرد. MVC Scaffolding حتی قابلیت تولید کد بر اساس الگوی Repository و یا نوشتن Unit tests مرتبط را نیز دارد. بدیهی است این ابزار جای یک برنامه نویس را نمی‌تواند پر کند اما کدهای آغازین یک سری کارهای متداول و تکراری را به خوبی می‌تواند پیاده سازی و ارائه دهد. زیر ساخت این ابزار، علاوه بر ASP.NET MVC، آشنایی با Entity framework code first است.
در طی سری ASP.NET MVC که در این سایت تا به اینجا مطالعه کردید من به شدت سعی کردم از ابزارگرایی پرهیز کنم. چون شخصی که نمی‌داند مسیریابی چیست، اطلاعات چگونه به یک کنترلر منتقل یا به یک View ارسال می‌شوند، قراردادهای پیش فرض فریم ورک چیست یا زیر ساخت امنیتی یا فیلترهای ASP.NET MVC کدامند، چطور می‌تواند از ابزار پیشرفته Code generator ایی استفاده کند، یا حتی در ادامه کدهای تولیدی آن‌را سفارشی سازی کند؟ بنابراین برای استفاده از این ابزار و درک کدهای تولیدی آن، نیاز به یک پیشنیاز دیگر هم وجود دارد: «Entity framework code first»
امسال دو کتاب خوب در این زمینه منتشر شده‌اند به نام‌های:
Programming Entity Framework: DbContext, ISBN: 978-1-449-31296-1
Programming Entity Framework: Code First, ISBN: 978-1-449-31294-7
که هر دو به صورت اختصاصی به مقوله EF Code first پرداخته‌اند.


در طی روزهای بعدی EF Code first را با هم مرور خواهیم کرد و البته این مرور مستقل است از نوع فناوری میزبان آن؛ می‌خواهد یک برنامه کنسول باشد یا WPF یا یک سرویس ویندوز NT و یا ... یک برنامه وب.
البته از دیدگاه مایکروسافت، M در MVC به معنای EF Code first است. به همین جهت MVC3 به صورت پیش فرض ارجاعی را به اسمبلی‌های آن دارد و یا حتی به روز رسانی که برای آن ارائه داده نیز در جهت تکمیل همین بحث است.


مروری سریع بر تاریخچه Entity framework code first

ویژوال استودیو 2010 و دات نت 4، به همراه EF 4.0 ارائه شدند. با این نگارش امکان استفاده از حالت‌های طراحی database first و model first مهیا است. پس از آن، به روز رسانی‌های EF خارج از نوبت و به صورت منظم، هر از چندگاهی ارائه می‌شوند و در زمان نگارش این مطلب، آخرین نگارش پایدار در دسترس آن 4.3.1 می‌باشد. از زمان EF 4.1 به بعد، نوع جدیدی از مدل سازی به نام Code first به این فریم ورک اضافه شد و در نگارش‌های بعدی آن، مباحث DB migration جهت ساده سازی تطابق اطلاعات مدل‌ها با بانک اطلاعاتی، اضافه گردیدند. در روش Code first، کار با طراحی کلاس‌ها که در اینجا مدل داده‌ها نامیده می‌شوند، شروع گردیده و سپس بر اساس این اطلاعات، تولید یک بانک اطلاعاتی جدید و یا استفاده از نمونه‌ای موجود میسر می‌گردد.
پیشتر در روش database first ابتدا یک بانک اطلاعاتی موجود، مهندسی معکوس می‌شد و از روی آن فایل XML ایی با پسوند EDMX تولید می‌گشت. سپس به کمک entity data model designer ویژوال استودیو، این فایل نمایش داده شده و یا امکان اعمال تغییرات بر روی آن میسر می‌شد. همچنین در روش دیگری به نام model first نیز کار از entity data model designer جهت طراحی موجودیت‌ها آغاز می‌گشت.
اما با روش Code first دیگر در ابتدای امر مدل فیزیکی و یک بانک اطلاعاتی وجود خارجی ندارد. در اینجا EF تعاریف کلاس‌های شما را بررسی کرده و بر اساس آن، اطلاعات نگاشت‌های خواص کلاس‌ها به جداول و فیلدهای بانک اطلاعاتی را تشکیل می‌دهد. البته عموما تعاریف ساده کلاس‌ها بر این منظور کافی نیستند. به همین جهت از یک سری متادیتا به نام ویژگی‌ها یا اصطلاحا data annotations مهیا در فضای نام System.ComponentModel.DataAnnotations برای افزودن اطلاعات لازم مانند نام فیلدها، جداول و یا تعاریف روابط ویژه نیز استفاده می‌گردد. به علاوه در روش Code first یک API جدید به نام Fluent API نیز جهت تعاریف این ویژگی‌ها و روابط، با کدنویسی مستقیم نیز درنظر گرفته شده است. نهایتا از این اطلاعات جهت نگاشت کلاس‌ها به بانک اطلاعاتی و یا برای تولید ساختار یک بانک اطلاعاتی خالی جدید نیز می‌توان کمک گرفت.



مزایای EF Code first

- مطلوب برنامه نویس‌ها! : برنامه نویس‌هایی که مدتی تجربه کار با ابزارهای طراح را داشته باشند به خوبی می‌دانند این نوع ابزارها عموما demo-ware هستند. چندجا کلیک می‌کنید، دوبار Next، سه بار OK و ... به نظر می‌رسد کار تمام شده. اما واقعیت این است که عمری را باید صرف نگهداری و یا پیاده سازی جزئیاتی کرد که انجام آن‌ها با کدنویسی مستقیم بسیار سریعتر، ساده‌تر و با کنترل بیشتری قابل انجام است.
- سرعت: برای کار با EF Code first نیازی نیست در ابتدای کار بانک اطلاعاتی خاصی وجود داشته باشد. کلا‌س‌های خود را طراحی و شروع به کدنویسی کنید.
- سادگی: در اینجا دیگر از فایل‌های EDMX خبری نیست و نیازی نیست مرتبا آن‌ها را به روز کرده یا نگهداری کرد. تمام کارها را با کدنویسی و کنترل بیشتری می‌توان انجام داد. به علاوه کنترل کاملی بر روی کد نهایی تهیه شده نیز وجود دارد و توسط ابزارهای تولید کد، ایجاد نمی‌شوند.
- طراحی بهتر بانک اطلاعاتی نهایی: اگر طرح دقیقی از مدل‌های برنامه داشته باشیم، می‌توان آن‌ها را به المان‌های کوچک و مشخصی، تقسیم و refactor کرد. همین مساله در نهایت مباحث database normalization را به نحوی مطلوب و با سرعت بیشتری میسر می‌کند.
- امکان استفاده مجدد از طراحی کلاس‌های انجام شده در سایر ORMهای دیگر. چون طراحی مدل‌های برنامه به بانک اطلاعاتی خاصی گره نمی‌خورند و همچنین الزاما هم قرار نیست جزئیات کاری EF در آن‌ها لحاظ شود، این کلاس‌ها در صورت نیاز در سایر پروژه‌ها نیز به سادگی قابل استفاده هستند.
- ردیابی ساده‌تر تغییرات: روش اصولی کار با پروژه‌های نرم افزاری همواره شامل استفاده از یک ابزار سورس کنترل مانند SVN، Git، مرکوریال و امثال آن است. به این ترتیب ردیابی تغییرات انجام شده به سادگی توسط این ابزارها میسر می‌شوند.
- ساده‌تر شدن طراحی‌های پیچیده‌تر: برای مثال پیاده سازی ارث بری،‌ ایجاد کلاس‌های خود ارجاع دهنده و امثال آن با کدنویسی ساده‌تر است.


دریافت آخرین نگارش EF


برای دریافت و نصب آخرین نگارش EF نیاز است از NuGet استفاده شود و این مزایا را به همراه دارد:
به کمک NuGet امکان با خبر شدن از به روز رسانی جدید صورت گرفته به صورت خودکار درنظر گرفته شده است و همچنین کار دریافت بسته‌های مرتبط و به روز رسانی ارجاعات نیز در این حالت خودکار است. به علاوه توسط NuGet امکان دسترسی به کتابخانه‌هایی که مثلا در گوگل‌کد قرار دارند و به صورت معمول امکان دریافت آن‌ها برای ما میسر نیست، نیز بدون مشکل فراهم است (برای نمونه ELMAH، که اصل آن از گوگل‌کد قابل دریافت است؛ اما بسته نیوگت آن نیز در دسترس می‌باشد).
پس از نصب NuGet، تنها کافی است بر روی گره References در Solution explorer ویژوال استودیو، کلیک راست کرده و به کمک NuGet آخرین نگارش EF را نصب کرد. در گالری آنلاین آن، عموما EF اولین گزینه است (به علت تعداد بالای دریافت آن).
حین استفاده از NuGet جهت نصب Ef، ابتدا ارجاعاتی به اسمبلی‌های زیر به برنامه اضافه خواهند شد:
System.ComponentModel.DataAnnotations.dll
System.Data.Entity.dll
EntityFramework.dll
بدیهی است بدون استفاده از NuGet، تمام این کارها را باید دستی انجام داد.
سپس در پوشه‌ای به نام packages، فایل‌های مرتبط با EF قرار خواهند گرفت که شامل اسمبلی آن به همراه ابزارهای DB Migration است. همچنین فایل packages.config که شامل تعاریف اسمبلی‌های نصب شده است به پروژه اضافه می‌شود. NuGet به کمک این فایل و شماره نگارش درج شده در آن، شما را از به روز رسانی‌های بعدی مطلع خواهد ساخت:

<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EntityFramework" version="4.3.1" />
</packages>

همچنین اگر به فایل app.config یا web.config برنامه نیز مراجعه کنید، یک سری تنظیمات ابتدایی اتصال به بانک اطلاعاتی در آن ذکر شده است:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</configSections>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
<parameters>
<parameter value="Data Source=(localdb)\v11.0; Integrated Security=True; MultipleActiveResultSets=True" />
</parameters>
</defaultConnectionFactory>
</entityFramework>
</configuration>

همانطور که ملاحظه می‌کنید بانک اطلاعاتی پیش فرضی که در اینجا ذکر شده است، LocalDB می‌باشد. این بانک اطلاعاتی را از این آدرس‌ نیز می‌توانید دریافت کنید.

البته ضرورتی هم به استفاده از آن نیست و از سایر نگارش‌های SQL Server نیز می‌توان استفاده کرد ولی خوب ... مزیت استفاده از آن برای کاربر نهایی این است که «نیازی به یک مهندس برای نصب، راه اندازی و نگهداری ندارد». تنها مشکل آن این است که از ویندوز XP پشتیبانی نمی‌کند. البته SQL Server CE 4.0 این محدودیت را ندارد.
ضمن اینکه باید درنظر داشت EF به فناوری میزبان خاصی گره نخورده است و مثال‌هایی که در اینجا بررسی می‌شوند صرفا تعدادی برنامه کنسول معمولی هستند و نکات عنوان شده در آن‌ها در تمام فناوری‌های میزبان موجود به یک نحو کاربرد دارند.


قراردادهای پیش فرض EF Code first

عنوان شد که اطلاعات کلاس‌های ساده تشکیل دهنده مدل‌های برنامه، برای تعریف جداول و فیلدهای یک بانک اطلاعات و همچنین مشخص سازی روابط بین آن‌ها کافی نیستند و مرسوم است برای پر کردن این خلاء از یک سری متادیتا و یا Fluent API مهیا نیز استفاده گردد. اما در EF Code first یک سری قرار داد توکار نیز وجود دارند که مطلع بودن از آن‌ها سبب خواهد شد تا حجم کدنویسی و تنظیمات جانبی این فریم ورک به حداقل برسند. برای نمونه مدل‌های معروف بلاگ و مطالب آن‌را درنظر بگیرید:

namespace EF_Sample01.Models
{
public class Post
{
public int Id { set; get; }
public string Title { set; get; }
public string Content { set; get; }
public virtual Blog Blog { set; get; }
}
}

using System.Collections.Generic;

namespace EF_Sample01.Models
{
public class Blog
{
public int Id { set; get; }
public string Title { set; get; }
public string AuthorName { set; get; }
public IList<Post> Posts { set; get; }
}
}


یکی از قراردادهای EF Code first این است که کلاس‌های مدل شما را جهت یافتن خاصیتی به نام Id یا ClassId مانند BlogId، جستجو می‌کند و از آن به عنوان primary key و فیلد identity جدول بانک اطلاعاتی استفاده خواهد کرد.
همچنین در کلاس Blog، خاصیت لیستی از Posts و در کلاس Post خاصیت virtual ایی به نام Blog وجود دارند. به این ترتیب روابط بین دو کلاس و ایجاد کلید خارجی متناظر با آن‌را به صورت خودکار انجام خواهد داد.
نهایتا از این اطلاعات جهت تشکیل database schema یا ساختار بانک اطلاعاتی استفاده می‌گردد.
اگر به فضاهای نام دو کلاس فوق دقت کرده باشید، به کلمه Models ختم شده‌اند. به این معنا که در پوشه‌ای به همین نام در پروژه جاری قرار دارند. یا مرسوم است کلاس‌های مدل را در یک پروژه class library مجزا به نام DomainClasses نیز قرار دهند. این پروژه نیازی به ارجاعات اسمبلی‌های EF ندارد و تنها به اسمبلی System.ComponentModel.DataAnnotations.dll نیاز خواهد داشت.


EF Code first چگونه کلاس‌های مورد نظر را انتخاب می‌کند؟

ممکن است ده‌ها و صدها کلاس در یک پروژه وجود داشته باشند. EF Code first چگونه از بین این کلاس‌ها تشخیص خواهد داد که باید از کدامیک استفاده کند؟ اینجا است که مفهوم جدیدی به نام DbContext معرفی شده است. برای تعریف آن یک کلاس دیگر را به پروژه برای مثال به نام Context اضافه کنید. همچنین مرسوم است که این کلاس را در پروژه class library دیگری به نام DataLayer اضافه می‌کنند. این پروژه نیاز به ارجاعی به اسمبلی‌های EF خواهد داشت. در ادامه کلاس جدید اضافه شده باید از کلاس DbContext مشتق شود:

using System.Data.Entity;
using EF_Sample01.Models;

namespace EF_Sample01
{
public class Context : DbContext
{
public DbSet<Blog> Blogs { set; get; }
public DbSet<Post> Posts { set; get; }
}
}

سپس در اینجا به کمک نوع جنریکی به نام DbSet، کلاس‌های دومین برنامه را معرفی می‌کنیم. به این ترتیب، EF Code first ابتدا به دنبال کلاسی مشتق شده از DbContext خواهد گشت. پس از یافتن آن‌، خواصی از نوع DbSet را بررسی کرده و نوع‌های متناظر با آن‌را به عنوان کلاس‌های دومین درنظر می‌گیرد و از سایر کلاس‌های برنامه صرفنظر خواهد کرد. این کل کاری است که باید انجام شود.
اگر دقت کرده باشید، نام کلاس‌های موجودیت‌ها، مفرد هستند و نام خواص تعریف شده به کمک DbSet‌، جمع می‌باشند که نهایتا متناظر خواهند بود با نام جداول بانک اطلاعاتی تشکیل شده.


تشکیل خودکار بانک اطلاعاتی و افزودن اطلاعات به جداول

تا اینجا بدون تهیه یک بانک اطلاعاتی نیز می‌توان از کلاس Context تهیه شده استفاده کرد و کار کدنویسی را آغاز نمود. بدیهی است جهت اجرای نهایی کدها، نیاز به یک بانک اطلاعاتی خواهد بود. اگر تنظیمات پیش فرض فایل کانفیگ برنامه را تغییر ندهیم، از همان defaultConnectionFactory یاده شده استفاده خواهد کرد. در این حالت نام بانک اطلاعاتی به صورت خودکار تنظیم شده و مساوی «EF_Sample01.Context» خواهد بود.
برای سفارشی سازی آن نیاز است فایل app.config یا web.config برنامه را اندکی ویرایش نمود:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
...
</configSections>
<connectionStrings>
<clear/>
<add name="Context"
connectionString="Data Source=(local);Initial Catalog=testdb2012;Integrated Security = true"
providerName="System.Data.SqlClient"
/>
</connectionStrings>
...
</configuration>

در اینجا به بانک اطلاعاتی testdb2012 در وهله پیش فرض SQL Server نصب شده، اشاره شده است. فقط باید دقت داشت که تگ configSections باید در ابتدای فایل قرار گیرد و مابقی تنظیمات پس از آن.
یا اگر علاقمند باشید که از SQL Server CE استفاده کنید، تنظیمات رشته اتصالی را به نحو زیر مقدار دهی نمائید:

<connectionStrings> 
              <add name="MyContextName"
                         connectionString="Data Source=|DataDirectory|\Store.sdf"
                         providerName="System.Data.SqlServerCe.4.0" />
</connectionStrings>

در هر دو حالت، name باید به نام کلاس مشتق شده از DbContext اشاره کند که در مثال جاری همان Context است.
یا اگر علاقمند بودید که این قرارداد توکار را تغییر داده و نام رشته اتصالی را با کدنویسی تعیین کنید، می‌توان به نحو زیر عمل کرد:

public class Context : DbContext
{
    public Context()
      : base("ConnectionStringName")
    {
    }


البته ضرورتی ندارد این بانک اطلاعاتی از پیش موجود باشد. در اولین بار اجرای کدهای زیر، به صورت خودکار بانک اطلاعاتی و جداول Blogs و Posts و روابط بین آن‌ها تشکیل می‌گردد:

using EF_Sample01.Models;

namespace EF_Sample01
{
class Program
{
static void Main(string[] args)
{
using (var db = new Context())
{
db.Blogs.Add(new Blog { AuthorName = "Vahid", Title = ".NET Tips" });
db.SaveChanges();
}
}
}
}


در این تصویر چند نکته حائز اهمیت هستند:
الف) نام پیش فرض بانک اطلاعاتی که به آن اشاره شد (اگر تنظیمات رشته اتصالی قید نگردد).
ب) تشکیل خودکار primary key از روی خواصی به نام Id
ج) تشکیل خودکار روابط بین جداول و ایجاد کلید خارجی (به کمک خاصیت virtual تعریف شده)
د) تشکیل جدول سیستمی به نام dbo.__MigrationHistory که از آن برای نگهداری سابقه به روز رسانی‌های ساختار جداول کمک گرفته خواهد شد.
ه) نوع و طول فیلدهای متنی، nvarchar از نوع max است.

تمام این‌ها بر اساس پیش فرض‌ها و قراردادهای توکار EF Code first انجام شده است.

در کدهای تعریف شده نیز، ابتدا یک وهله از شیء Context ایجاد شده و سپس به کمک آن می‌توان به جدول Blogs اطلاعاتی را افزود و در آخر ذخیره نمود. استفاده از using هم دراینجا نباید فراموش شود، زیرا اگر استثنایی در این بین رخ دهد، کار پاکسازی منابع و بستن اتصال گشوده شده به بانک اطلاعاتی به صورت خودکار انجام خواهد شد.
در ادامه اگر بخواهیم مطلبی را به Blog ثبت شده اضافه کنیم، خواهیم داشت:

using EF_Sample01.Models;

namespace EF_Sample01
{
class Program
{
static void Main(string[] args)
{
//addBlog();
addPost();
}

private static void addPost()
{
using (var db = new Context())
{
var blog = db.Blogs.Find(1);
db.Posts.Add(new Post
{
Blog = blog,
Content = "data",
Title = "EF"
});
db.SaveChanges();
}
}

private static void addBlog()
{
using (var db = new Context())
{
db.Blogs.Add(new Blog { AuthorName = "Vahid", Title = ".NET Tips" });
db.SaveChanges();
}
}
}
}

متد db.Blogs.Find، بر اساس primary key بلاگ ثبت شده، یک وهله از آن‌را یافته و سپس از آن جهت تشکیل شیء Post و افزودن آن به جدول Posts استفاده می‌شود. متد Find ابتدا Contxet جاری را جهت یافتن شیءایی با id مساوی یک جستجو می‌کند (اصطلاحا به آن first level cache هم گفته می‌شود). اگر موفق به یافتن آن شد، بدون صدور کوئری اضافه‌ای به بانک اطلاعاتی از اطلاعات همان شیء استفاده خواهد کرد. در غیراینصورت نیاز خواهد داشت تا ابتدا کوئری لازم را به بانک اطلاعاتی ارسال کرده و اطلاعات شیء Blog متناظر با id=1 را دریافت کند. همچنین اگر نیاز داشتیم تا تنها با سطح اول کش کار کنیم، در EF Code first می‌توان از خاصیتی به نام Local نیز استفاده کرد. برای مثال خاصیت db.Blogs.Local بیانگر اطلاعات موجود در سطح اول کش می‌باشد.
نهایتا کوئری Insert تولید شده توسط آن به شکل زیر خواهد بود (لاگ شده توسط برنامه SQL Server Profiler):

exec sp_executesql N'insert [dbo].[Posts]([Title], [Content], [Blog_Id])
values (@0, @1, @2)
select [Id]
from [dbo].[Posts]
where @@ROWCOUNT > 0 and [Id] = scope_identity()',
N'@0 nvarchar(max) ,@1 nvarchar(max) ,@2 int',
@0=N'EF',
@1=N'data',
@2=1


این نوع کوئرهای پارامتری چندین مزیت مهم را به همراه دارند:
الف) به صورت خودکار تشکیل می‌شوند. تمام کوئری‌های پشت صحنه EF پارامتری هستند و نیازی نیست مرتبا مزایای این امر را گوشزد کرد و باز هم عده‌ای با جمع زدن رشته‌ها نسبت به نوشتن کوئری‌های نا امن SQL اقدام کنند.
ب) کوئرهای پارامتری در مقابل حملات تزریق اس کیوال مقاوم هستند.
ج) SQL Server با کوئری‌های پارامتری همانند رویه‌های ذخیره شده رفتار می‌کند. یعنی query execution plan محاسبه شده آن‌ها را کش خواهد کرد. همین امر سبب بالا رفتن کارآیی برنامه در فراخوانی‌های بعدی می‌گردد. الگوی کلی مشخص است. فقط پارامترهای آن تغییر می‌کنند.
د) مصرف حافظه SQL Server کاهش می‌یابد. چون SQL Server مجبور نیست به ازای هر کوئری اصطلاحا Ad Hoc رسیده یکبار execution plan متفاوت آن‌ها را محاسبه و سپس کش کند. این مورد مشکل مهم تمام برنامه‌هایی است که از کوئری‌های پارامتری استفاده نمی‌کنند؛ تا حدی که گاهی تصور می‌کنند شاید SQL Server دچار نشتی حافظه شده، اما مشکل جای دیگری است.


مشکل! ساختار بانک اطلاعاتی تشکیل شده مطلوب کار ما نیست.

تا همینجا با حداقل کدنویسی و تنظیمات مرتبط با آن، پیشرفت خوبی داشته‌ایم؛ اما نتیجه حاصل آنچنان مطلوب نیست و نیاز به سفارشی سازی دارد. برای مثال طول فیلدها را نیاز داریم به مقدار دیگری تنظیم کنیم، تعدادی از فیلدها باید به صورت not null تعریف شوند یا نام پیش فرض بانک اطلاعاتی باید مشخص گردد و مواردی از این دست. با این موارد در قسمت‌های بعدی بیشتر آشنا خواهیم شد.
نظرات مطالب
کار با کلیدهای اصلی و خارجی در EF Code first
برای مواردی که کلید اصلی Identity نباشه راه حلی هست ؟

کد
namespace TestKeys
{
    class Program
    {
        public class Bill
        {
            [DatabaseGenerated(DatabaseGeneratedOption.None)]
            public string Id { get; set; }
            public decimal Amount { set; get; }
            [ForeignKey("AccountId")]
            public virtual Account Account { get; set; }
            public string AccountId { set; get; }
        }

        public class Account
        {
            [DatabaseGenerated(DatabaseGeneratedOption.None)]
            public string Id { get; set; }
            public string Name { get; set; }
        }

        public class MyContext : DbContext
        {
            public DbSet<Bill> Bills { get; set; }
            public DbSet<Account> Accounts { get; set; }
        }


        public class BillFromWebsrv
        {
            public string Id { get; set; }
            public decimal Amount { set; get; }
            public DateTime DateTime { get; set; }

            public Account Account { get; set; }

        }



        static void Main(string[] args)
        {
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyContext>());
            using (var ctx = new MyContext())
            {

                foreach (var dummyBill in DummyBills())
                {
                    var bl = new Bill { Id = dummyBill.Id, Amount = dummyBill.Amount, Account = dummyBill.Account };

                    ctx.Bills.Add(bl);
                }
                ctx.SaveChanges();
            }


        }

        public static List<BillFromWebsrv> DummyBills()
        {
            return new List<BillFromWebsrv>
            {
                new BillFromWebsrv
                {
                    Id = "1",
                    Amount = 1231,
                    DateTime = DateTime.Now,
                    Account = new Account {Id = "1", Name = "ac1"}
                },
                new BillFromWebsrv
                {
                    Id = "2",
                    Amount = 1232,
                    DateTime = DateTime.Now,
                    Account = new Account {Id = "2", Name = "ac2"}
                },
                new BillFromWebsrv
                {
                    Id = "3",
                    Amount = 1233,
                    DateTime = DateTime.Now,
                    Account = new Account {Id = "2", Name = "ac2"}
                },
                new BillFromWebsrv
                {
                    Id = "4",
                    Amount = 1134,
                    DateTime = DateTime.Now,
                    Account = new Account {Id = "3", Name = "ac3"}
                }
            };
        }
    }
}

ارور
{"Violation of PRIMARY KEY constraint 'PK_dbo.Accounts'. Cannot insert duplicate key in object 'dbo.Accounts'. The duplicate key value is (2).\r\nThe statement has been terminated."} 
نظرات مطالب
الگویی برای مدیریت دسترسی همزمان به ConcurrentDictionary
یک فیلتر رو به صورت زیر نوشتم و در آن از این دیکشنری استفاده کردم وقتی به صورت parallel اجرا می‌کنم متد AddOrUpdate  کلیدهایی که تکراری باشند را به جای اینکه مقدار آن را ویرایش کند یک کلید دیگر با همون مقدار اضافه می‌کند لطفا راهنمایی کنید مشکل کار از کجاست؟ 
 public class LockFilter : ActionFilterAttribute
    {
        static ConcurrentDictionary<StringBuilder, int> _properties;
        static LockFilter()
        {
            _properties = new ConcurrentDictionary<StringBuilder, int>();
        }

        public  int Duration { get; set; }
        public string VaryByParam { get; set; }

        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var actionArguments = context.ActionArguments.Values.Single();
            var properties = VaryByParam.Split(",").ToList();

            StringBuilder key = new StringBuilder();
            foreach (var actionArgument in actionArguments.GetType().GetProperties())
            {
                if (!properties.Any(t => t.Trim().ToLower() == actionArgument.Name.ToLower()))
                    continue;
                var value = actionArguments.GetType().GetProperty(actionArgument.Name).GetValue(actionArguments, null).ToString();
                key.Append(value);
            }

            _properties.AddOrUpdate(key, 1, (x, y) => y + 1);

            // rest of code 
        }
    }

نظرات مطالب
EF Code First #12
با سلام
من از یک سری توابع جنریک استفاده میکنم که نیاز به دسترسی DbContext داره آیا تعریف این توابع در IUnitOfWork درسته؟ یک نمونه

public interface IUnitOfWork
    {
        IDbSet<TEntity> Set<TEntity>() where TEntity : class;
        int SaveChanges();
        void Update<TEntity>(TEntity entity) where TEntity : class;
    }


 public class MyContext : DbContext, IUnitOfWork
    {
        public void Update<TEntity>(TEntity entity) where TEntity : class
        {
            var fqen = GetEntityName<TEntity>();

            object originalItem;
            EntityKey key = (IObjectContextAdapter)this).ObjectContext.CreateEntityKey
                          (fqen, entity);
            if (((IObjectContextAdapter)this).ObjectContext.TryGetObjectByKey(key, out 
                                            originalItem))
            {
               ((IObjectContextAdapter)this).ObjectContext.ApplyCurrentValues
                  (key.EntitySetName, entity);
            }
               ((IObjectContextAdapter)this).ObjectContext.ApplyCurrentValues
                        (key.EntitySetName, entity);
        }

        private string GetEntityName<TEntity>() where TEntity : class
        {
            return string.Format("{0}.{1}", ((IObjectContextAdapter)this).ObjectContext.
            DefaultContainerName, _pluralizer.Pluralize(typeof(TEntity).Name));
        }

        #region IUnitOfWork Members
        public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
        {
            return base.Set<TEntity>();
        }
        #endregion
    }

مطالب
آشنایی با کلاس JavaScriptSerializer

برای استفاده از jQuery Ajax یکی از روش‌های ارسال دیتا به برنامه، تبدیل داده‌ها به فرمت JSON می‌باشد. برای داده‌های ساده، تشخیص این فرمت ساده است. مثلا اگر امضای تابع وب سرویس اجکس ما به صورت زیر باشد:
public static bool IsUserAvailable(string username)
اطلاعات جی‌سونی را که قرار است ارسال کنیم، فرمت زیر را باید داشته باشد:
{'username':'value'}
حال اگر آرگومان‌های ما پیچیده‌تر بودند چطور؟ مثلا بجای یک رشته ساده، یک لیست جنریک داشتیم، فرمت ورودی را چگونه باید تشخیص داد؟
برای این منظور در دات نت 3 و نیم، کلاسی جهت انجام اینگونه تبدیلات پیش بینی شده است که شرح مختصر آن به صورت زیر است:
ابتدا باید ارجاعی را به اسمبلی system.web.extensions به برنامه افزود و سپس جهت سهولت کار می‌توان یک extension method از کلاس JavaScriptSerializer مهیا در فضای نام System.Web.Script.Serialization ایجاد کرد:

public static string ToJson(this object data)
{
return new JavaScriptSerializer().Serialize(data);
}
اکنون چند مثال زیر را در نظر بگیرید:
        public static string GetJsonTest0()
{
var data = "a1";
return data.ToJson();
}

public static string GetJsonTest1()
{
var data = new List<string> { "a1", "a2", "a3" };
return data.ToJson();
}

public static string GetJsonTest2()
{

var lst =
new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("Name", "علی"),
new KeyValuePair<string, object>("Number", 10),
new KeyValuePair<string, object>("Desc", "منابع مورد نیاز")
};

return lst.ToJson();

}
خروجی‌های آن‌ها به ترتیب به صورت زیر خواهند بود:

"a1"
["a1","a2","a3"]
[{"Key":"Name","Value":"علی"},{"Key":"Number","Value":10},{"Key":"Desc","Value":"منابع مورد نیاز"}]

این کلاس همچنین قابلیت Deserialize و تبدیل داده‌هایی به فرمت JSON به اشیاء مورد نظر ما را نیز دارا است.