public bool sample(object query) { System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly; foreach (var item in query.GetType().GetProperties(flags)) { string test = item.GetValue(query,null).ToString(); // Do Something ... } return false; }
سپس اگر صفحه Layout.cshtml_ باز کنید، با متا تگی به فرم زیر روبرو میشوید که توسط آن میتوانیم اندازه صفحه نمایش را مشخص کنیم:
زمانیکه Request ای صادر میشود، موارد ذیل تشکیل خواهند شد:
اگر دقت کنید در قسمت User-Agent یکسری مشخصات از سیستم نمایان است که ما طبق آن میتوانیم صفحه مربوطه را بارگذاری کنیم.
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); AuthConfig.RegisterAuth(); InitializeDisplayModeProvider(); } protected void InitializeDisplayModeProvider() { var phone = new DefaultDisplayMode("Phone") { ContextCondition = ctx => ctx.GetOverriddenUserAgent() != null && ctx.GetOverriddenUserAgent().Contains("iPhone") }; var mobile = new DefaultDisplayMode("Tablet") { ContextCondition = ctx => ctx.GetOverriddenUserAgent() != null && ctx.GetOverriddenUserAgent().Contains("iPad") }; DisplayModeProvider.Instance.Modes.Insert(0, phone); DisplayModeProvider.Instance.Modes.Insert(1, mobile); }
اگر Extension ای را برای تست شبیه سازی محیط موبایل یا تبلت، نصب کنید، میتوانید آن را مشاهده کنید.
توسط ویژگیهای جدیدی که در نسخه 2012 به بحث window افزوده شد میتوانیم مسالهای running total و running average را به شکل بهینه ای حل کنیم.
ابتدا این دو مساله را بدون بکارگیری ویژگیهای جدید، حل نموده و سپس سراغ توابع جدید خواهم رفت.
قبل از هر چیزی لازم است جدول زیر ساخته شود و دادههای نمونه در آن درج شود:
create table testTable ( day_nbr integer not null primary key clustered, value integer not null check (value > 0) ); insert into testTable values (10, 7), (20, 15), (30, 3), (40, 9), (50, 17), (60, 25), (70, 10);
مساله running total بسیار ساده است، یعنی جمع مقدار سطر جاری با مقادیر سطرهای قبلی (بر اساس یک ترتیب معین)
running average هم مشابه به running total هست با این تفاوت که میانگین مقادیر سطرجاری وسطرهای قبلی محاسبه میشود.
و نتیجه به صورت نمودار:
راه حل در SQL Server 2000
توسط دو correlated scalar subquery در ماده SELECT میتوانیم مقادیر دو ستون مورد نظر با محاسبه کنیم:
select *, runningTotal = (select sum(value) from testTable where day_nbr <= t.day_nbr), runningAverage = (select avg(value) from testTable where day_nbr <= t.day_nbr) from testTable t;
اگر به نقشه اجرای این query نگاه کنید گره(عملگر) inner join دو بار بکار رفته است (به وجود دو subquery)، که این عدد در روش توابع تجمعی window به صفر کاهش پیدا خواهد کرد
راه حل در SQL Server 2005
توسط cross apply به سادگی میتوانیم دو subquery که در روش قبل بود را به یکی کاهش دهیم:
select * from testTable t cross apply (select sum(value) as runningTotal, avg(value) as runningAverage from testTable where day_nbr <= t.day_nbr)d;
این بار تنها یک عملگر inner join در نقشه اجرای query مشاهده میشود:
راه حل در SQL Server 2012
با اضافه شدن برخی از ویژگیهای استاندارد به ماده OVER مثل rows و range شاهد بهبودی در عملکرد queryها هستیم.
یکی از کاربردهای توابع تجمعی window حل مساله running total و running average است.
به تصویر زیر توجه کنید، همانطور که در قبل توضیح دادم ما به سطرجاری و سطرهای پیشین نیاز داریم تا اعمال تجمعی (جمع و میانگین) را روی مقادیر بدست آمده انجام دهیم. در تصویر زیر سطرجاری و سطرهای قبلی به ازای هر سطری به وضوح قابل مشاهده است، مثلا هنگامی که سطر جاری برابر با روز 30 است ما خود سطر جاری (current row) و تمام سطرهای پیشین و قبلی (unbounded preceding) را نیاز داریم.
و اکنون query مورد نظر
select *, sum(value) over(order by day_nbr rows between unbounded preceding and current row) as runningTotal, avg(value) over(order by day_nbr rows between unbounded preceding and current row) as runningAverage from testTable
در نقشه اجرای این query دیگر خبری از عملگر inner join نخواهد بود که به معنای عملکرد بهتر query است.
افزونه Sql Tools برای Visual Studio
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 تعریف شوند یا نام پیش فرض بانک اطلاعاتی باید مشخص گردد و مواردی از این دست. با این موارد در قسمتهای بعدی بیشتر آشنا خواهیم شد.
در این قسمت تلاش میکنم در خصوص محیط BIMS (Business Intelligence Management Studio) و همچنین AdventureWorksDW2008R2 توضیحاتی را ارائه کنم. در ابتدا در خصوص طراحی انجام شده در Data Warehouse مربوط به پایگاه دادهی Adventure Works 2008 توضیحاتی ارایه میگردد.
شاید بهترین کار در خصوص آشنایی با یک پایگاه داده نگاه کردن به دیاگرام کلی آن پایگاه داده باشد. بنابر این در ابتدا میبایست یک دیاگرام از پایگاه دادهی AdventureWorksDW2008R2 بسازیم (این کار را در SQL Server Management Studio انجام میدهیم) . قبل از ساخت دیاگرام میبایست کاربر Sa را به عنوان Owner پایگاه داده معرفی کنیم.
برای این منظور ابتدا Properties پایگاه دادهی AdventureWorksDW2008R2 را گرفته و به قسمت Files رفته و با انتخاب دکمهی ... در مقابل Owner و جستجوی کاربر Sa ، اقدام به مشخص کردن مالک پایگاه داده میکنیم. و سپس دکمهی Ok را میزنیم.
مطابق شکل زیر
سپس یک دیاگرام کلی از پایگاه داده تولید میکنیم. مانند شکل زیر
با یک نگاه اجمالی مشخص میگردد که نام تمامی جداول پایگاه دادهی DW یا با کلمهی Dim یا با کلمهی Fact شروع شدهاند.
همان طور که در مقالهی شمارهی یک نیز عنوان شد، چندین روش طراحی DW وجود دارد :
1. ستاره ای
2. دانه برفی
3. کهکشانی
دقت داشته باشید که جداول Fact دارای فیلدهای عددی نیز میباشد که توسط مراحل ETL پر شدهاند و جداول Dimension دارای ابعادی هستند که به شاخصهای موجود در یک جدول Fact معنا میدهند. به عبارت دیگر شاخص میزان فروش اینترنتی، یک Measure میباشد. اما با ارایه دو دایمنشن، به یک واکشی، عملا ما یک Measure داریم که بر اساس آن دو بعد، ماهیت پیدا کرده است. به عنوان مثال میزان فروش اینترنتی بر اساس سال و ماه و روز و براساس کشور خریدار مشخص میشود.
یکی از روشهای تهیهی DW این میباشد که کاربران خبره در هر سیستم، مشخص نمایند چه گزارشاتی مورد نظر آنها میباشد. سپس توسط تیم پشتیبانی آن سیستمها، جداول Fact,Dimension مورد نیاز برای حصول گزارش مربوطه تهیه گردد.
شاید ذکر این نکته جالب باشد که برای توسعهی یک پایگاه دادهی Multidimensional توسط Solution های ماکروسافت نیازی به آشنایی با یک محیط کار ( IDE ) جدید نمیباشد. همان طور هم که در مقالهی قبلی اشاره شد، برای Deploy کردن یک پایگاه دادهی چند بعدی ( Multidimensional ) از خود محیط Visual Studio .Net استفاده میشود. بنابر این آن دسته از برنامه نویسانی که با این محیط آشنا میباشند به راحتی میتوانند به توسعهی پایگاه دادهی چند بعدی بپردازند.
لازم به ذکر میباشد که اساسا هدف من از شروع این سری مقالات ، آموزش MDX Query ها میباشد و نه آموزش BIMS ، با این وجود در این قسمت و در قسمت بعدی، توضیحات مقدماتی کار با BIMS ارایه میگردد و همچنین در فرصت مناسب در خصوص BIMS یک مجموعه مقالهی جامع ارایه خواهم کرد.
در ابتدا اجزا BIMS را برای شما توضیح میدهم و سپس در خصوص ساخت هر کدام از آنها و ترتیب ساخت آنها توضیحاتی ارایه خواهم داد.
مسیر باز کردن برنامهی SQL Server Business Intelligence Development Studio = BIDS در زیر آمده است:
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft SQL Server 2012\ SQL Server Data Tools
دقت داشته باشید که در صورت استفاده از نسخهی Sql Server 2008 میبایست مسیر زیر را جستجو نمایید:
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft SQL Server 2008 R2
با نگاه کردن به محیط BIMS می توانید پنجرهی Solution Explorer را مشاهده کنید .(در صورت عدم مشاهده، میتوانید این پنجره را از منوی View باز کنید)
در پنجرهی Solution Explorer ابتدا نام Solution و در زیر آن، نام پروژه را خواهیم دید (نام پروژه و نام پایگاه دادهی چند بعدی، مشابه یکدیگر میباشند) و در زیر نام پروژه، موارد زیر را میبینیم:
1. Data Source
2. Data Source View
3. Cubes
4. Dimensiones
5. ….
Data Source : عملا برقرار کنندهی پروژه با Data Warehouse میباشد. دقت داشته باشید که امکان تهیه یک پایگاه دادهی چند بعدی از چندین DW وجود دارد و حتا نوع DW ها میتواند متفاوت باشد (به عبارت دیگر ما میتوانیم چندین DW در RDBMS های متفاوت داشته باشیم و همهی آنها را در یک Multidimensional Database تجمیع کنیم). برای انجام چنین کاری باید چندین Data Source تعریف کنیم.
Data Source View : هر Data Source میتواند دارای چندین تقسیم بندی با مفاهیم Business ی باشد. برای هر کدام از این دسته بندیها میتوانیم یک یا چند Data Source View ایجاد کنیم . به عبارت دیگر ایجاد Data Source View ها سبب خلاصه شدن تعداد جداول Fact , Dimension براساس یک بیزینس خاص میباشد و در ادامه راحتتر میتوانیم Cube ها را تولید کنیم.
نکته: جداول Fact , Dimension در ساختار D ata Warehouse ساخته میشوند.
Cubes : محل تعریف Cube ها در این قسمت میباشد. در سری آموزش SSAS در خصوص نحوهی ساخت Cube ها شرح کاملی ارایه خواهم کرد.
Dimensions : با توجه به این که در روال ساخت Cube ما مشخص میکنیم چه Dimension هایی داریم، یک سری از Dimension ها به صورت پیش فرض در این قسمت قرار میگیرند و البته در صورت تغییر در Data Source View میتوانیم یک Dimension را به صورت دستی در این قسمت ایجاد نماییم و سپس آن را به Cube مورد نظر اضافه نماییم.
دقت داشته باشید که برای ساخت یک پروژه میبایست بعد از ساخت Data Warehouse در برنامهی BIMS اقدام به ساخت یک Data Source کنیم و سپس با توجه به Businessهای موجود در سیستمهای OLTP اقدام به ساخت Data Source Viewهای مناسب کرده و در نهایت اقدام به ساخت Cube کنیم. بعد از انجام تنظیمات مختلف در Cube مانند ساخت Hierarchy , KPI و ... نیاز میباشد که پروژه را Deploy کنیم تا پایگاه دادهی چند بعدی (MDB) ساخته شود.
در قسمت بعدی نحوهی ساخت یک پروژه در SSAS و چگونگی باز کردن یک پایگاه داده را بررسی خواهیم کرد.
این مطلب به عنوان اولین بخش از این سری مطالب منتشر میشود.
هدف این نوشته بررسی جزییات برنامه نویسی در رابطه با کلاس و شیء نیست. بلکه دریافتن چگونگی شکل گرفتن ایده شیء گرایی و علت مفید بودن آن است.
مشاهده مفاهیم شیء گرایی در پیرامون خود
حتماً در دنیای برنامه نویسی شیء گرا بارها با کلمات کلاس و شیء روبرو شده اید. درک صحیح از این مفاهیم بسیار مهم و البته بسیار ساده است. کار را با یک مثال شروع میکنیم. به تصویر زیر نگاه کنید.در سمت راست بخشی از نقشه یک ساختمان و در سمت چپ ساختمان ساخته شده بر اساس این نقشه را میبینید. ساختمان همان شیء است. و نقشه ساختمان کلاس آن است چراکه امکان ایجاد اشیائی که تحت عنوان ساختمان طبقه بندی (کلاس بندی) میشوند را فراهم میکند. به همین سادگی. کلاسها طرح اولیه، نقشه یا قالبی هستند که جزییات یک شی را توصیف میکنند.
حتماً با من موافق هستید اگر بگویم:
- در نقشه ساختمان نمیتوانید زندگی کنید اما در خود ساختمان میتوانید.
- از روی یک نقشه میتوان به تعداد دلخواه ساختمان ساخت.
- هنگامی که در یک ساختمان زندگی میکنید نیازی نیست تا دقیقاً بدانید چگونه ساخته شده و مثلاً سیم کشی یا لوله کشیهای آن چگونه است! تنها کافیست بدانید برای روشن شدن لامپ باید کلید آن را بزنید.
- ساختمان دارای ویژگی هایی مانند متراژ، ضخامت دیوار، تعداد پنجره و ابعاد هر یک و ... است که در هنگام ساخت و بر اساس اطلاعات موجود در نقشه تعیین شده اند.
- ساختمان دارای کارکرد هایی است. مانند بالا و پایین رفتن آسانسور و یا باز و بسته شدن درب پارکینگ. هر یک از این کارکردها نیز بر اساس اطلاعات موجود در نقشه پیاده سازی و ساخته شده اند.
- ساختمان تمام اجزای لازم برای اینکه از آن بتوانیم استفاده کنیم و به عبارتی در آن بتوانیم زندگی کنیم را در خود دارد.
سوال: کلاس یا نقشه ایجاد گاو چیست؟ اگر از من بپرسید خواهم گفت طرح اولیه گاو هم ممکن است وجود داشته باشد البته در اختیار خداوند و با سطح دسترسی ملکوت!
اتومبیل، تلویزیون و ... همگی مثال هایی از اشیاء پیرامون ما در دنیای واقعی هستند که حتماً میتوانید کلاس یا نقشه ایجاد آنها را نیز بدست آورید و یا ویژگیها و کارکردهای آنها را برشمارید.
مفاهیم شیء گرایی در مهندسی نرم افزار
مفاهیمی که تاکنون در مورد دنیای واقعی مرور کردیم همان چیزی است که در دنیای برنامه نویسی ـ به عقیده من دنیای واقعیتر از دنیای واقعی ـ با آن سر و کار داریم. علت این امر آن است که اصولاً ایده روش برنامه نویسی شیء گرا با مشاهده محیط پیرامون ما به وجود آمده است.برای نوشتن برنامه جهت حل یک مسئله بزرگ باید بتوان آن مسئله را به بخشهای کوچکتری تقسیم نمود. در این رابطه مفهوم شیء و کلاس با همان کیفیتی که در محیط پیرامون ما وجود دارد به صورت مناسبی امکان تقسیم یه مسئله بزرگ به بخشهای کوچکتر را فراهم میکند. و سبب میشود هماهنگی و تقارن و تناظر خاصی بین اشیاء برنامه و دنیای واقعی بوجود آید که یکی از مزایای اصلی روش شیء گراست.
از آنجا که در یک برنامه اصولاً همه چیز و همه مفاهیم در قالب کدها و دستورات برنامه معنا دارد، کلاس و شیء نیز چیزی بیش از قطعاتی کد نیستند. قطعه کد هایی که بسته بندی شده اند تا تمام کار مربوط به هدفی که برای آنها در نظر گرفته شده است را انجام دهند.
همان طور که در هر زبان برنامه نویسی دستوراتی برای کارهای مختلف مانند تعریف یک متغیر یا ایجاد یک حلقه و ... در نظر گرفته شده است، در زبانهای برنامه نویسی شیء گرا نیز دستوراتی وجود دارد تا بتوان قطعه کدی را بر اساس مفهوم کلاس بسته بندی کرد.
به طور مثال قطعه کد زیر را در زبان برنامه نویسی سی شارپ در نظر بگیرید.
class Player { public string Name; public int Age; public void Walk() { // کدهای مربوط به پیاده سازی راه رفتن } public void Run() { // کدهای مربوط به پیاده سازی دویدن } }
این کلاس به چه دردی میخورد؟ کجا میتوانیم از این کلاس استفاده کنیم؟
پاسخ این است که این کلاس ممکن است برای ما هیچ سودی نداشته باشد و هیچ کجا نتوانیم از آن استفاده کنیم. اما بیایید فرض کنیم برنامه نویسی هستیم که قصد داریم یک بازی فوتبال بنویسیم. به جای آنکه قطعات کد مجزایی برای هر یک از بازیکنان و کنترل رفتار و ویژگیهای آنان بنویسیم با اندکی تفکر به این نکته پی میبریم که همه بازیکنان مشترکات بسیاری دارند و به عبارتی در یک گروه یا کلاس قابل دسته بندی هستند. پس سعی میکنیم نقشه یا قالبی برای بازیکنها ایجاد کنیم که دربردارنده ویژگیها و رفتارهای آنها باشد.
همان طور که در نقشه ساختمان نمیتوانیم زندگی کنیم این کلاس هم هنوز آماده انجام کارهای واقعی نیست. چراکه برخی مقادیر هنوز برای آن تنظیم نشده است. مانند نام بازیکن و سن و ....
و همان طور که برای سکونت لازم است ابتدا یک ساختمان از روی نقشه ساختمان بسازیم برای استفاده واقعی از کلاس یاد شده نیز باید از روی آن شیء بسازیم. به این فرآیند وهله سازی یا نمونه سازی نیز میگویند. یک زبان برنامه نویسی شیء گرا دستوراتی را برای وهله سازی نیز در نظر گرفته است. در C# کلمه کلیدی new این وظیفه را به عهده دارد.
Player objPlayer = new Player(); objPlayer.Name = “Ali Karimi”; objPlayer.Age = 30; objPlayer.Run();
تمام آنچه که بازیکن برای انجام امور مربوط به خود نیاز دارد در کلاس بازیکن کپسوله میشود. بدیهی است در یک برنامه واقعی ویژگیها و رفتارهای بسیار بیشتری باید برای کلاس بازیکن در نظر گرفته شود. مانند پاس دادن، شوت زدن و غیره.
به این ترتیب ما برای هر برنامه میتوانیم مسئله اصلی را به تعدادی مسئله کوچکتر تقسیم کنیم و وظیفه حل هر یک از مسائل کوچک را به یک شیء واگذار کنیم. و بر اساس اشیاء تشخیص داده شده کلاسهای مربوطه را بنویسیم. برنامه نویسی شیء گرا سبب میشود تا مسئله توسط تعدادی شیء که دارای نمونههای متناظری در دنیای واقعی هستند حل شود که این امر زیبایی و خوانایی و قابلیت نگهداری و توسعه برنامه را بهبود میدهد.
احتمالاً تاکنون متوجه شده اید که برای نگهداری ویژگیهای اشیاء از متغیرها و برای پیاده سازی رفتارها یا کارکردهای اشیاء از توابع استفاده میکنیم.
با توجه به این که هدف این مطلب بررسی مفهوم شیء گرائی بود و نه جزییات برنامه نویسی، بنابراین بیان برخی مفاهیم در این رابطه را که بیشتر در مهندسی نرم افزار معنا دارند تا در دنیای واقعی در مطالب بعدی بررسی میکنیم.
(function ($, undefined) { $.fn.extend({ 'rtlColResizable': function (options) { var defaults = { //Default values for the plugin's options here }; options = $.extend(defaults, options); var isMouseButtonPressed; var $resizingElement = undefined; var resizingElementStartWidth; var mouseCursorStartX; var isCursorInResizingPosition; var addResizingCursorStyle = function ($element) { $element.css({ 'cursor': 'col-resize', 'user-select': 'none', '-o-user-select': 'none', '-ms-user-select': 'none', '-moz-user-select': 'none', '-khtml-user-select': 'none', '-webkit-user-select': 'none', }); }; var removeResizingCursorStyle = function ($element) { $element.css({ 'cursor': 'default', 'user-select': 'text', '-o-user-select': 'text', '-ms-user-select': 'text', '-moz-user-select': 'text', '-khtml-user-select': 'text', '-webkit-user-select': 'text', }); }; var canResize = function (e) { return (e.offsetX || e.clientX - $(e.target).offset().left) < 10; }; return this.each(function () { var opts = options; var tableColumns = $(this).find('th'); tableColumns.filter(':not(:last-child)').mousedown(function (e) { $resizingElement = $(this); isMouseButtonPressed = true; mouseCursorStartX = e.pageX; resizingElementStartWidth = $resizingElement.width(); }); tableColumns.mousemove(function (e) { if (canResize(e)) { addResizingCursorStyle($(e.target)); isCursorInResizingPosition = true; } else if (!isMouseButtonPressed) { removeResizingCursorStyle($(e.target)); isCursorInResizingPosition = false; } if (isCursorInResizingPosition && isMouseButtonPressed) { $resizingElement.width(resizingElementStartWidth + (mouseCursorStartX - e.pageX)); } }); $(document).mouseup(function () { if (isMouseButtonPressed) { removeResizingCursorStyle($resizingElement); isMouseButtonPressed = false; } }); }); } }); })(jQuery);
<!DOCTYPE html> <html> <head> <title>rtlColResizable Sample</title> </head> <body> <table id="myTable"> <thead> <tr> <th>ستون1</th> <!-- نام ستونها را حتما در این تگ باید تعریف کنید --> <th>ستون2</th> <th>ستون3</th> </tr> </thead> <tr> <td>داده مربوط به ستون 1</td> <td>داده مربوط به ستون 2 </td> <td>داده مربوط به ستون 3</td> </tr> </table> <script type="text/javascript" src="jquery-1.9.1.min.js"></script> <script type="text/javascript" src="jquery.rtlColResizable.js"></script> <script> $('table#myTable').rtlColResizable(); </script> </body> </html>
اگر میخواید به کاربرانتون امکان ارسال لینک مطلب از طریق واتس آپ و تلگرام و ... به وسیله تلفن همراهشون، بدید. مطلب بخونید
چه نوع محیط کاری را بیشتر ترجیح میدهید؟