توسعه سیستم مدیریت محتوای DNTCms - قسمت دوم
2 سوال داشتم از خدمتتون
اول اینکه چرا تمام پراپرتیها به صورت Vitual تعریف شده اند؟
دوم اینکه آیا بهتر نیست در چنین سیستمی که تعداد رکوردها اینقدر بالا است از سیستم Database First استفاده نمود.چون احتمال تغییرات دیتابیس زیاد است و حتی استفاده از مایگریشن هم با سرعت کمی انجام میشود.بنا براین بهتر است با SQL Management Studio به دیتا بیس متصل شد و تغییرات را اعمال کرد.
ASP.NET MVC #22
LocalDB چیست؟
LocalDB نسخهای جدید از Sql server express است که به توسعه دهندگان این اجازه را میدهد تا با نصب آن، از نصب کامل دیگر نسخههای Sql server جلوگیری نمایند. LocalDB برای برنامههایی که به صورت Local و بر روی یک سیستم اجرا میشوند مورد استفاده قرار میگیرد.
مزایای استفاده از این نسخه
- فایل نصب با حجم بسیار کم. (28.2MB برای نسخه 32 بیتی و 33.7MB برای نسخه 64بیتی)
- سادگی ( بدون نیاز به انجام تنظیمات خاص بر روی سیستم)
- اجرا در محیطهایی که کاربر جاری دسترسی مدیریتی ندارد.(برای اجرای آن نیاز به Permissionهای مدیریتی نیست و یک کاربر سطح پایین هم میتواند آن را اجرا کند)
- سادگی نصب
- همانند Sql server Express سازگاری کاملی با T-Sql دارد. همچنین از Stored Procedureها ، دادههای جغرافیایی و مکانی ( geometry and geography ها) ، Triggers و Viewها پشتیبانی میکند.
- سازگاری با Provider معمولی Sql server
- عدم اجرای سرویس خاصی در حافظه برای مدیریت دیتابیس. پروسسهای LocalDb هر زمان که نیاز باشد اجرا میشوند و هر زمان که به آنها نیاز نداشته باشیم به صورت اتوماتیک متوقف میشوند.
- پشتیبانی از خصوصیت AttachDbFileName در کانکشن استرینگ جهت استفاده از فایل بانک اطلاعات به صورت مستقیم
- سرویس پکهای جدید جهت LocalDB به راحتی برروی نسخه موجود نصب میشوند و نسخه قبلی را به روز رسانی میکنند.
- نصب یک LocalDB برای همه کاربران یک کامپیوتر
- پشتیبانی کامل از Silent Installation
- امکان استفاده از آن توسط Asp.net
- پشتیبانی از XML (XQuery و XPath) و BLOB
- پشتیبانی از Ado.net sync framework
- پشتیبانی از LINQ
- پشتیبانی از Distributed transactions
- کانکشنهای نامحدود (البته به صورت Local)
- نیاز به نصب Sql server 2012 native client . این مورد به همراه LocalDB روی سیستم نصب نمیشود
- نیاز به دسترسی مدیریتی جهت نصب
- 140MB فضای خالی دیسک سخت
- به روز رسانی دات نت فریم ورک 4 به 4.0.2 و یا نسخههای بالاتر
- عدم پشتیبانی از Windows xp ، Window server 2003 و Windows 2000
- عدم امکان نصب نسخه 32 بیتی بر روی ویندوز 64 بیتی (حتما باید نسخه 64 بیتی آن را نصب کنید)
- فقط میتوان به صورت Local از آن استفاده کرد. امکان استفاده تحت شبکه وجود ندارد و فقط به کانکشنهای Local پاسخ میدهد.
- فقط توسط Sql server 2012 management studio در دسترس میباشد. LocalDB را نمیتوان از طریق Management studioهای قدیمی مدیریت کرد.
- عدم پشتیبانی Visual Studio 2010 از LocalDB
- عدم اجرا بر روی موبایلهای هوشمند
- محدودیت سایز بانک اطلاعات : 10GB
- عدم پشتیبانی از قابلیت FileStream
- محدودیت استفاده از فقط یک CPU
- عدم امکان Debuging دستورات Sql در هنگام اتصال به LocalDB
نحوه اتصال به LocalDB توسط Sql Server Management Studio
اگر net framework. خود را از نسخه 4 به 4.0.2 و یا نسخههای بعد از آن به روز رسانی کرده باشید میتوان توسط Sql Server 2012 Management Studio به Sql server LocalDB وصل شد. عبارت local)\v11.0) را به عنوان نام سرور وارد نمایید.
مجددا لازم به ذکر است که امکان اتصال توسط Management Studioهای قبلی به بانک LocalDB امکان پذیر نمیباشد.
چرا از این نوع داده استفاده کنیم؟
نخستین پرسشی که ممکن است برای شما پیش بیاید این است که چرا بهتر است از این نوع داده استفاده کنیم. برای پاسخ به این پرسش باید راهکارهای گذشته را بررسی کنیم. معمولاً طراحان پایگاه دادهها برای استفاده از تاریخ خورشیدی، زمان را به صورت میلادی ثبت میکنند؛ سپس با یک scalar-valued function زمان درج شده را به خورشیدی تبدیل میکنند. در این صورت میتوان با یک تابع کوچک دیگر بخش مربوط به ساعت را نیز از همان ستون به دست آورد. در این صورت میتوانیم از کلیهی متدهای مربوط به DateTime در SQL از جمله افزایش و کاهش و تفاضل دو تاریخ بهره برد. برخی دیگر از طراحان، ستونی از نوع (char(10 در نظر میگیرند و تاریخ خورشیدی را به صورت دهکاراکتری در آن ذخیره میکنند. این روش هرچند نیاز به تبدیل به خورشیدی را ندارد ولی کلیهی مزایایی که در استفاده از DateTime به آنها دسترسی داریم از دست میدهیم. افزون بر این جهت نگهداری زمان باید یک فیلد دیگر از نوع کاراکتری و یا در نگارشهای نوینتر از نوع time تعریف کنیم. برخی دیگر از هر دو را در کنار هم استفاده میکنند و در واقع جهت سرعت بالاتر نمایش و بررسی دادهها از طریق محیط SQL Server از فیلد کاراکتری تاریخ خورشیدی و برای مقایسه و بدست آوردن ساعت از فیلد نوع DateTime استفاده میکنند.
از نظر فضای اشغالشده نوع DataTime، هشت بایت، smalldatetime (در صورت استفاده) 4 بایت و فیلد 10 کاراکتری تاریخ 10 بایت فضا اشغال میکند در صورتی که نوع JalaliDate با درنظر گرفتن همهی مزایای انواع دادهی استفادهشده برای تاریخ، فقط 8 بایت فضا اشغال میکند. با استفاده از این نوع به راحتی دادهی تاریخ را بر اساس تقویم ایرانی اعتبارسنجی میکنید و بخشهای مختلف زمان از سال تا ثانیه را با یک متد به دست میآورید. میتوانید به راحتی به تاریخ خود زمانی را بیفزایید یا بکاهید و در گزارشها بدون نگرانی از تبدیل درست استفاده کنید. چون کدباز است میتوانید با کمی حوصله امکانات دیگر مد نظر خود را به آن بیفزایید و از آن در SQL بهره ببرید.
چگونه این نوع داده را حذف کنم!؟
شما میتوانید به سادگی نوع دادهی ایجادشده توسط CLR را در مسیر زیر بیابید و اقدام به حذف آن نمایید:
همانطور که مشاهده میشود؛ حتی نوع دادهی سیستمی hierarchyid که جهت ساختار سلسلهمراتبی مانند چارت سازمانی یا درخت تجهیزات استفاده میشود؛ نیز یک نوع دادهی CLR است.
آیا راه دیگری نیز برای افزودن این نوع داده به SQL به جز Publish کردن وجود دارد؟
مانند بسیاری دیگر از گونههای پروژه، در اینجا نیز شما یک فایل DLL خواهید داشت. این فایل برپایهی تنظیماتی که شما در قسمت Properties پروژهی خود انجام میدهید ساخته میشود. پس از تغییر مسیر فایل DLL در دستور زیر توسط یک New Query از Database خود، آن را اجرا کنید:
CREATE ASSEMBLY JalaliDate FROM 'F:\prgJalaliDate.dll' WITH PERMISSION_SET = SAFE;
ALTER ASSEMBLY JalaliDate FROM 'F:\prgJalaliDate.dll'
select * from sys.assemblies select * from sys.assembly_files
CREATE TYPE dbo.JalaliDate EXTERNAL NAME JalaliDate.[JalaliDate];
همچنین چنانچه در SQL Server 2012 از منوی راستکلیک پایگاه دادهها روی گزینه Tasks و سپس Generate Scripts را انتخاب کنیم، از مشاهدهی سند ساخته شده، درخواهیم یافت که حتی دستورهای مربوط به ساخت اسمبلی CLR با تبدیل فایل به کد در Scripts وجود دارد و با اجرای آن در سروری دیگر، انتقال مییابد.
GO /****** Object: SqlAssembly [prgJalaliDate] Script Date: 2013/04/30 08:27:00 ب.ظ ******/ CREATE ASSEMBLY [prgJalaliDate] FROM 0x4D5A90000300000004000000FFFF0000B8000000000000 ..... بقیهی کدها حذف شده WITH PERMISSION_SET = SAFE GO ALTER ASSEMBLY [prgJalaliDate] ADD FILE FROM 0x4D6963726F736F667420432F432B2B204D534620372E30300D0A1A44530..... بقیهی کدها حذف شده AS N'prgJalaliDate.pdb' GO /****** Object: UserDefinedType [dbo].[JalaliDate] Script Date: 2013/04/30 08:27:00 ب.ظ ******/ CREATE TYPE [dbo].[JalaliDate] EXTERNAL NAME [prgJalaliDate].[JalaliDate] GO
دنباله دارد ...
از SQL Server 2005 نوع دادهی varbinary(max) معرفی شد که برخی از چالشهای بهکاربری Image را کاست و دربارهی بسیاری از موارد مانند ذخیرهی عکس پرسنلی هنوز هم کاربرد دارد؛ ولی توجه داشته باشید که استفاده از این فیلد فقط برای فایلهای کمتر از 256 کیلوبایت سفارش شده است و برای بالاتر از آن، کارآیی کاهش فراوانی خواهد یافت.
در SQL Server 2008 نوع دادهی جدیدی به نام FileStream به وجود آمد به این شکل که یک FileGroup از نوع Data FileStream به پایگاهداده افزوده میشود و در واقع با یک پوشه در سیستم فایل در پیوند است. از این پس هنگام ساخت یک جدول به جای استفاده از نوع دادهی varbinary از نوع FileStream استفاده میکنیم با مد نظر داشتن این نکته که حتماً باید یک فیلد از نوع Uniqueidentifier هم در آن جدول تعریف شده باشد. شیوهی کار نیز به این صورت خواهد بود که خود رکورد در جدول ذخیره میشود و فقط محتوای فایل در آن مسیری از NTFS ذخیره میشود. برخلاف روش درج مسیر فایل در جدول که پس از حذف رکورد، فایل همچنان در سیستم فایل میماند؛ این بار با حذف رکورد فایل مربوطه نیز حذف خواهد شد. افزون بر این مدیریت پشتیبانی از فایلها نیز برعهدهی پایگاه دادهها خواهد بود. اندازهی فایلها در FileStream محدودیتهای پیشین را نخواهد داشت و شما به اندازهی حجم درایو هارددیسک میتوانید فایل در آن ذخیره کنید. نکتهی دیگر دربارهی فایلهای با حجم سنگین که میتوانید Stream مربوط به یک فایل را به صورت بخشبخش در سمت مشتری بارگذاری کنید و به او نشان دهید. در FileStream امنیت و تراکنش فایلها برعهدهی SQL Server است و از این دیدگاه بسیار سادهتر و کارآتر از FileSystem است. (برای آشنایی بیشتر با FileStream، این نوشتار از مهندس وحید نصیری را مطالعه کنید.)
گونهی FileTable از ویژگیهای نوین SQL Server 2012 است که تکمیلکنندهی FileStream است. FileTable آمیزشی از FileStream با hierarchyid و سیستم فایل ویندوز برای ارائهی تواناییهای نوین مدیریت BLOB در SQL Server است. FileTable همانگونه که از دو واژهی تشکیلدهندهاش پیداست؛ همزمان یک جدول و یک سیستم فایل معمولی است.
FileTable به هر روی یک جدول از پایگاهدادههای SQL Server است با یک تفاوت که ساختار آن از پیش تعریفشده است. ستونهای FileTable و نوع دادهی آن از پیش توسط SQL Server مشخص شده است. ستونهای تشکیلدهندهی FileTable دربرگیرندهی جدول زیر است:
هر ردیف از FileTable نمایندهی یک فایل یا پوشه در File System است. ستون path_locator که از نوع hierarchyid است نشاندهندهی مسیر یک فایل یا پوشه است. hierarchyid که از SQL Server 2008 معرفی شده است؛ بهترین نوع داده برای نگهداری ارتباط ساختار سلسلهمراتبی مانند چارت سازمانی، درخت تجهیزات یک کارخانه و یا در همین نمونه درخت فایلها و پوشهها است. پس میتوانیم از همهی امکانات hierarchyid در FileTable نیز برخوردار شویم. اینکه این فایل به ترتیب در چه پوشههایی قرار گرفته است یا اینکه این پوشه شامل چه فایلها یا پوشههایی خواهد بود. اینکه پوشههای همفرزند پوشهی جاری کدام است و یا یا توابع مربوط به جابهجایی فایلها و پوشهها.
دنباله دارد...
ثبت و نگهداری تاریخ خورشیدی در SQL Server از دیرباز یکی از نگرانیهای برنامهنویسان و
طراحان پایگاه دادهها بوده است. در این نوشتار، راهکار تعریف یک DataType در SQL Server 2012 به روش CLR آموزش داده
خواهد شد.
در ویژوال استودیو یک پروژهی جدید از نوع SQL Server Database Project
به شکل زیر ایجاد کنید:
متن موجود در صفحهی بازشده را کاملاً حذف کرده و با کد زیر جایگزین کنید.
(در کد زیر همهی توابع لازم برای مقداردهی به سال، ماه، روز، ساعت، دقیقه و ثانیه و البته گرفتن مقدار از آنها، تبدیل تاریخ خورشیدی به میلادی، گرفتن تاریخ به تنهایی، گرفتن زمان به تنهایی، افزایش یا کاهش زمان برپایهی یکی از متغیرهای زمان و بررسی و اعتبارسنجی انواع بخشهای زمان گنجانده شده است. در صورت پرسش یا پیشنهاد روی هر کدام در قسمت نظرات، پیام خود را بنویسید.)
using System; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; [Serializable()] [SqlUserDefinedType(Format.Native)] public struct JalaliDate : INullable { private Int16 m_Year; private byte m_Month; private byte m_Day; private byte m_Hour; private byte m_Minute; private byte m_Second; private bool is_Null; public Int16 Year { get { return (this.m_Year); } set { m_Year = value; } } public byte Month { get { return (this.m_Month); } set { m_Month = value; } } public byte Day { get { return (this.m_Day); } set { m_Day = value; } } public byte Hour { get { return (this.m_Hour); } set { m_Hour = value; } } public byte Minute { get { return (this.m_Minute); } set { m_Minute = value; } } public byte Second { get { return (this.m_Second); } set { m_Second = value; } } public bool IsNull { get { return is_Null; } } public static JalaliDate Null { get { JalaliDate jl = new JalaliDate(); jl.is_Null = true; return (jl); } } public override string ToString() { if (this.IsNull) { return "NULL"; } else { return this.m_Year.ToString("D4") + "/" + this.m_Month.ToString("D2") + "/" + this.m_Day.ToString("D2") + " " + this.Hour.ToString("D2") + ":" + this.Minute.ToString("D2") + ":" + this.Second.ToString("D2"); } } public static JalaliDate Parse(SqlString s) { if (s.IsNull) { return Null; } System.Globalization.PersianCalendar pers = new System.Globalization.PersianCalendar(); string str = Convert.ToString(s); string[] JDate = str.Split(' ')[0].Split('/'); JalaliDate jl = new JalaliDate(); jl.Year = Convert.ToInt16(JDate[0]); byte MonthsInYear = (byte)pers.GetMonthsInYear(jl.Year); jl.Month = (byte.Parse(JDate[1]) <= MonthsInYear ? (byte.Parse(JDate[1]) > 0 ? byte.Parse(JDate[1]) : (byte)1) : MonthsInYear); byte DaysInMonth = (byte)pers.GetDaysInMonth(jl.Year, jl.Month); ; jl.Day = (byte.Parse(JDate[2]) <= DaysInMonth ? (byte.Parse(JDate[2]) > 0 ? byte.Parse(JDate[2]) : (byte)1) : DaysInMonth); if (str.Split(' ').Length > 1) { string[] JTime = str.Split(' ')[1].Split(':'); jl.Hour = (JTime.Length >= 1 ? (byte.Parse(JTime[0]) < 23 && byte.Parse(JTime[0]) >= (byte)0 ? byte.Parse(JTime[0]) : (byte)0) : (byte)0); jl.Minute = (JTime.Length >= 2 ? (byte.Parse(JTime[1]) < 59 && byte.Parse(JTime[1]) >= (byte)0 ? byte.Parse(JTime[1]) : (byte)0) : (byte)0); jl.Second = (JTime.Length >= 3 ? (byte.Parse(JTime[2]) < 59 && byte.Parse(JTime[2]) >= (byte)0 ? byte.Parse(JTime[2]) : (byte)0) : (byte)0); } else { jl.Hour = 0; jl.Minute = 0; jl.Second = 0; } return (jl); } public SqlString GetDate() { return this.m_Year.ToString("D4") + "/" + this.m_Month.ToString("D2") + "/" + this.m_Day.ToString("D2"); } public SqlString GetTime() { return this.Hour.ToString("D2") + ":" + this.Minute.ToString("D2") + ":" + this.Second.ToString("D2"); } public SqlDateTime ToGregorianTime() { System.Globalization.PersianCalendar pers = new System.Globalization.PersianCalendar(); return SqlDateTime.Parse(pers.ToDateTime(this.Year, this.Month, this.Day, this.Hour, this.Minute, this.Second, 0).ToString()); } public SqlString JalaliDateAdd(SqlString interval, int increment) { System.Globalization.PersianCalendar pers = new System.Globalization.PersianCalendar(); DateTime dt = pers.ToDateTime(this.Year, this.Month, this.Day, this.Hour, this.Minute, this.Second, 0); string CInterval = interval.ToString(); bool isConvert = true; switch (CInterval) { case "Year": dt = pers.AddYears(dt, increment); break; case "Month": dt = pers.AddMonths(dt, increment); break; case "Day": dt = pers.AddDays(dt, increment); break; case "Hour": dt = pers.AddHours(dt, increment); break; case "Minute": dt = pers.AddMinutes(dt, increment); break; case "Second": dt = pers.AddSeconds(dt, increment); break; default: isConvert = false; break; } if (isConvert == true) { this.Year = (Int16)pers.GetYear(dt); this.Month = (byte)pers.GetMonth(dt); this.Day = (byte)pers.GetDayOfMonth(dt); this.Hour = (byte)pers.GetHour(dt); this.Minute = (byte)pers.GetMinute(dt); this.Second = (byte)pers.GetSecond(dt); } return this.m_Year.ToString("D4") + "/" + this.m_Month.ToString("D2") + "/" + this.m_Day.ToString("D2") + " " + this.Hour.ToString("D2") + ":" + this.Minute.ToString("D2") + ":" + this.Second.ToString("D2"); } }
از منوهای بالا روی منوی Bulild و سپس گزینهی Publish prgJalaliDate کلیک کتید:
در پنجرهی بازشده روی دکمهی Edit کلیک کنید سپس تنظیمات مربوط به اتصال به پایگاه داده را انجام دهید.
روی دکمهی OK کلیک کنید و سپس در پنجرهی اولیه، روی دکمهی Publish کلیک کتید:
به همین سادگی، DataType مربوطه در SQL Server 2012 ساخته میشود. خبر خوش اینکه شما میتوانید با راستکلیک روی نام پروژه و انتخاب گزینهی Properties در قسمت Project Setting تنظیمات مربوط به نگارش SQL Server را انجام دهید. (از نگارش 2005 به بعد در VS 2012 پشتیبانی میشود.)
اکنون زمان آن رسیده است که DataType ایجادشده را در SQL Server 2012 بیازماییم. SQL Server را باز کنید و دستور زیر را در آن اجرا کتید.
USE Northwind GO CREATE TABLE dbo.TestTable ( Id int NOT NULL IDENTITY (1, 1), TestDate dbo.JalaliDate NULL ) ON [PRIMARY] GO
اکنون چند رکورد درون این جدول درج میکنیم:
Insert into TestTable (TestDate) Values ('1392/02/09'),('1392/02/09 22:40'),('1392/12/30 22:40')
این خطا به این خاطر است که CLR را در SQL Server فعال نکرده ایم. جهت فعالکردن CLR دستور زیر را اجرا کنید:
sp_configure 'clr enabled', 1 Reconfigure
Insert into TestTable (TestDate) Values ('1392/02/09'),('1392/02/09 22:40'),('1392/12/30 22:40')
اکنون زمان آن رسیده است که توسط یک پرسوجو، همهی توابعی که در سیشارپ برای این نوع داده نوشتیم، بیازماییم. پرسوجوی زیر را اجرا کنید:
Select TestDate.ToString() as JalaliDateTime, TestDate.GetDate() as JalaliDate, TestDate.GetTime() as JalaliTime, TestDate.ToGregorianTime() as GregorianTime, TestDate.JalaliDateAdd('Day',1) JalaliTomorrow, TestDate.Month as JalaliMonth from TestTable
نیازی به گفتن نیست که میتوانید به سادگی از توابع مربوط به DateTime در SQL Server بهره ببرید. برای مثال برای به دست آوردن فاصلهی میان دو روز از پرسوجوی زیر استفاده کنید:
Declare @a JalaliDate = '1392/02/07 00:00:00' Declare @b JalaliDate = '1392/02/05 00:00:00' SELECT DATEDIFF("DAY",@b.ToGregorianTime(),@a.ToGregorianTime()) AS DiffDate
شاد و پیروز باشید.
- در هم اکنون: هاست سرور SQL در ویندوز و استفاده از آن در کلاینتهای لینوکسی با SQL Client
- و یا اگر از یک ORM استفاده میکنید (مانند EF یا NH)، چون در این حالت کدهای شما وابستگی به بانک اطلاعاتی مورد استفاده ندارند، سوئیچ کردن به بانکهای اطلاعاتی دیگر، ساده خواهد بود؛ مگر اینکه از قابلیتهای ORM استفاده نکرده باشید و مستقیما SQL نویسی ویژهی آن بانک اطلاعاتی خاص را انجام داده باشید. در غیر اینصورت (استفاده از ORM؛ بدون SQL نویسی مستقیم و ویژه)، حداکثر کاری که باید انجام دهید، تغییر پروایدرهای ابتدای برنامه است؛ بدون تغییری در کدهای اصلی برنامه
تنظیمات امنیتی دسترسی به سرور RavenDB
- همچنین حالت نصب embedded آن دسترسی از بیرون ندارد و فقط از طریق برنامه قابل استفاده است.
کار با RavenDB از طریق REST API آن
- البته میشود پورتهای دسترسی خارجی به یک سرور را با فایروال بست. به این ترتیب فقط برنامه نصب شده در آن سرور امکان اتصال را خواهد داشت (خیلیها با SQL Server هم به همین نحو کار میکنند؛ یک برنامه وب و یک برنامه سرور SQL دارند روی یک سرور. برنامه وب سفارشی، لایه اتصال امن به بانک اطلاعاتی است).
- همچنین حالت نصب embedded آن دسترسی از بیرون ندارد و فقط از طریق برنامه قابل استفاده است.