An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
var department = (from d in context.Departments where d.Name == DepartmentNames.English select d).FirstOrDefault();
یک اپلیکیشن با SQL Membership بسازید
حال با استفاده از ابزار ASP.NET Configuration دو کاربر جدید بسازید: oldAdminUser و oldUser.
نقش جدیدی با نام Admin بسازید و کاربر oldAdminUser را به آن اضافه کنید.
بخش جدیدی با نام Admin در سایت خود بسازید و فرمی بنام Default.aspx به آن اضافه کنید. همچنین فایل web.config این قسمت را طوری پیکربندی کنید تا تنها کاربرانی که در نقش Admin هستند به آن دسترسی داشته باشند. برای اطلاعات بیشتر به این لینک مراجعه کنید.
پنجره Server Explorer را باز کنید و جداول ساخته شده توسط SQL Membership را بررسی کنید. اطلاعات اصلی کاربران که برای ورود به سایت استفاده میشوند، در جداول aspnet_Users و aspnet_Membership ذخیره میشوند. دادههای مربوط به نقشها نیز در جدول aspnet_Roles ذخیره خواهند شد. رابطه بین کاربران و نقشها نیز در جدول aspnet_UsersInRoles ذخیره میشود، یعنی اینکه هر کاربری به چه نقش هایی تعلق دارد.
برای مدیریت اساسی سیستم عضویت، مهاجرت جداول ذکر شده به سیستم جدید ASP.NET Identity کفایت میکند.
مهاجرت به Visual Studio 2013
- برای شروع ابتدا Visual Studio Express 2013 for Web یا Visual Studio 2013 را نصب کنید.
- حال پروژه ایجاد شده را در نسخه جدید ویژوال استودیو باز کنید. اگر نسخه ای از SQL Server Express را روی سیستم خود نصب نکرده باشید، هنگام باز کردن پروژه پیغامی به شما نشان داده میشود. دلیل آن وجود رشته اتصالی است که از SQL Server Express استفاده میکند. برای رفع این مساله میتوانید SQL Express را نصب کنید، و یا رشته اتصال را طوری تغییر دهید که از LocalDB استفاده کند.
- فایل web.config را باز کرده و رشته اتصال را مانند تصویر زیر ویرایش کنید.
- پنجره Server Explorer را باز کنید و مطمئن شوید که الگوی جداول و دادهها قابل رویت هستند.
- سیستم ASP.NET Identity با نسخه 4.5 دات نت فریم ورک و بالاتر سازگار است. پس نسخه فریم ورک پروژه را به آخرین نسخه (4.5.1) تغییر دهید.
پروژه را Build کنید تا مطمئن شوید هیچ خطایی وجود ندارد.
نصب پکیجهای NuGet
- Microsoft.AspNet.Identity.Owin
- Microsoft.Owin.Host.SystemWeb
- Microsoft.Owin.Security.Facebook
- Microsoft.Owin.Security.Google
- Microsoft.Owin.Security.MicrosoftAccount
- Microsoft.Owin.Security.Twitter
مهاجرت دیتابیس فعلی به سیستم ASP.NET Identity
در پنجره کوئری باز شده، تمام محتویات فایل Migrations.sql را کپی کنید. سپس اسکریپت را با کلیک کردن دکمه Execute اجرا کنید.
ممکن است با اخطاری مواجه شوید مبنی بر آنکه امکان حذف (drop) بعضی از جداول وجود نداشت. دلیلش آن است که چهار عبارت اولیه در این اسکریپت، تمام جداول مربوط به Identity را در صورت وجود حذف میکنند. از آنجا که با اجرای اولیه این اسکریپت چنین جداولی وجود ندارند، میتوانیم این خطاها را نادیده بگیریم. حال پنجره Server Explorer را تازه (refresh) کنید و خواهید دید که پنج جدول جدید ساخته شده اند.
لیست زیر نحوه Map کردن اطلاعات از جداول SQL Membership به سیستم Identity را نشان میدهد.
- aspnet_Roles --> AspNetRoles
- aspnet_Users, aspnet_Membership --> AspNetUsers
- aspnet_UsersInRoles --> AspNetUserRoles
ساختن مدلها و صفحات عضویت
کلاس User باید کلاس IdentityUser را که در اسمبلی Microsoft.AspNet.Identity.EntityFramework وجود دارد گسترش دهد. خاصیت هایی را تعریف کنید که نماینده الگوی جدول AspNetUser هستند. خواص ID, Username, PasswordHash و SecurityStamp در کلاس IdentityUser تعریف شده اند، بنابراین این خواص را در لیست زیر نمیبینید.
public class User : IdentityUser { public User() { CreateDate = DateTime.Now; IsApproved = false; LastLoginDate = DateTime.Now; LastActivityDate = DateTime.Now; LastPasswordChangedDate = DateTime.Now; LastLockoutDate = DateTime.Parse("1/1/1754"); FailedPasswordAnswerAttemptWindowStart = DateTime.Parse("1/1/1754"); FailedPasswordAttemptWindowStart = DateTime.Parse("1/1/1754"); } public System.Guid ApplicationId { get; set; } public string MobileAlias { get; set; } public bool IsAnonymous { get; set; } public System.DateTime LastActivityDate { get; set; } public string MobilePIN { get; set; } public string Email { get; set; } public string LoweredEmail { get; set; } public string LoweredUserName { get; set; } public string PasswordQuestion { get; set; } public string PasswordAnswer { get; set; } public bool IsApproved { get; set; } public bool IsLockedOut { get; set; } public System.DateTime CreateDate { get; set; } public System.DateTime LastLoginDate { get; set; } public System.DateTime LastPasswordChangedDate { get; set; } public System.DateTime LastLockoutDate { get; set; } public int FailedPasswordAttemptCount { get; set; } public System.DateTime FailedPasswordAttemptWindowStart { get; set; } public int FailedPasswordAnswerAttemptCount { get; set; } public System.DateTime FailedPasswordAnswerAttemptWindowStart { get; set; } public string Comment { get; set; } }
حال برای دسترسی به دیتابیس مورد نظر، نیاز به یک DbContext داریم. اسمبلی Microsoft.AspNet.Identity.EntityFramework کلاسی با نام IdentityDbContext دارد که پیاده سازی پیش فرض برای دسترسی به دیتابیس ASP.NET Identity است. نکته قابل توجه این است که IdentityDbContext آبجکتی از نوع TUser را میپذیرد. TUser میتواند هر کلاسی باشد که از IdentityUser ارث بری کرده و آن را گسترش میدهد.
در پوشه Models کلاس جدیدی با نام ApplicationDbContext بسازید که از IdentityDbContext ارث بری کرده و از کلاس User استفاده میکند.
public class ApplicationDbContext : IdentityDbContext<User> { }
مدیریت کاربران در ASP.NET Identity توسط کلاسی با نام UserManager انجام میشود که در اسمبلی Microsoft.AspNet.Identity.EntityFramework قرار دارد. چیزی که ما در این مرحله نیاز داریم، کلاسی است که از UserManager ارث بری میکند و آن را طوری توسعه میدهد که از کلاس User استفاده کند.
در پوشه Models کلاس جدیدی با نام UserManager بسازید.
public class UserManager : UserManager<User> { }
کلمه عبور کاربران بصورت رمز نگاری شده در دیتابیس ذخیره میشوند. الگوریتم رمز نگاری SQL Membership با سیستم ASP.NET Identity تفاوت دارد. هنگامی که کاربران قدیمی به سایت وارد میشوند، کلمه عبورشان را توسط الگوریتمهای قدیمی SQL Membership رمزگشایی میکنیم، اما کاربران جدید از الگوریتمهای ASP.NET Identity استفاده خواهند کرد.
کلاس UserManager خاصیتی با نام PasswordHasher دارد. این خاصیت نمونه ای از یک کلاس را ذخیره میکند، که اینترفیس IPasswordHasher را پیاده سازی کرده است. این کلاس هنگام تراکنشهای احراز هویت کاربران استفاده میشود تا کلمههای عبور را رمزنگاری/رمزگشایی شوند. در کلاس UserManager کلاس جدیدی بنام SQLPasswordHasher بسازید. کد کامل را در لیست زیر مشاهده میکنید.
public class SQLPasswordHasher : PasswordHasher { public override string HashPassword(string password) { return base.HashPassword(password); } public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword) { string[] passwordProperties = hashedPassword.Split('|'); if (passwordProperties.Length != 3) { return base.VerifyHashedPassword(hashedPassword, providedPassword); } else { string passwordHash = passwordProperties[0]; int passwordformat = 1; string salt = passwordProperties[2]; if (String.Equals(EncryptPassword(providedPassword, passwordformat, salt), passwordHash, StringComparison.CurrentCultureIgnoreCase)) { return PasswordVerificationResult.SuccessRehashNeeded; } else { return PasswordVerificationResult.Failed; } } } //This is copied from the existing SQL providers and is provided only for back-compat. private string EncryptPassword(string pass, int passwordFormat, string salt) { if (passwordFormat == 0) // MembershipPasswordFormat.Clear return pass; byte[] bIn = Encoding.Unicode.GetBytes(pass); byte[] bSalt = Convert.FromBase64String(salt); byte[] bRet = null; if (passwordFormat == 1) { // MembershipPasswordFormat.Hashed HashAlgorithm hm = HashAlgorithm.Create("SHA1"); if (hm is KeyedHashAlgorithm) { KeyedHashAlgorithm kha = (KeyedHashAlgorithm)hm; if (kha.Key.Length == bSalt.Length) { kha.Key = bSalt; } else if (kha.Key.Length < bSalt.Length) { byte[] bKey = new byte[kha.Key.Length]; Buffer.BlockCopy(bSalt, 0, bKey, 0, bKey.Length); kha.Key = bKey; } else { byte[] bKey = new byte[kha.Key.Length]; for (int iter = 0; iter < bKey.Length; ) { int len = Math.Min(bSalt.Length, bKey.Length - iter); Buffer.BlockCopy(bSalt, 0, bKey, iter, len); iter += len; } kha.Key = bKey; } bRet = kha.ComputeHash(bIn); } else { byte[] bAll = new byte[bSalt.Length + bIn.Length]; Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length); Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length); bRet = hm.ComputeHash(bAll); } } return Convert.ToBase64String(bRet); } }
دقت کنید تا فضاهای نام System.Text و System.Security.Cryptography را وارد کرده باشید.
متد EncodePassword کلمه عبور را بر اساس پیاده سازی پیش فرض SQL Membership رمزنگاری میکند. این الگوریتم از System.Web گرفته میشود. اگر اپلیکیشن قدیمی شما از الگوریتم خاصی استفاده میکرده است، همینجا باید آن را منعکس کنید. دو متد دیگر نیز بنامهای HashPassword و VerifyHashedPassword نیاز داریم. این متدها از EncodePassword برای رمزنگاری کلمههای عبور و تایید آنها در دیتابیس استفاده میکنند.
سیستم SQL Membership برای رمزنگاری (Hash) کلمههای عبور هنگام ثبت نام و تغییر آنها توسط کاربران، از PasswordHash, PasswordSalt و PasswordFormat استفاده میکرد. در روند مهاجرت، این سه فیلد در ستون PasswordHash جدول AspNetUsers ذخیره شده و با کاراکتر '|' جدا شده اند. هنگام ورود کاربری به سایت، اگر کله عبور شامل این فیلدها باشد از الگوریتم SQL Membership برای بررسی آن استفاده میکنیم. در غیر اینصورت از پیاده سازی پیش فرض ASP.NET Identity استفاده خواهد شد. با این روش، کاربران قدیمی لازم نیست کلمههای عبور خود را صرفا بدلیل مهاجرت اپلیکیشن ما تغییر دهند.
کلاس UserManager را مانند قطعه کد زیر بروز رسانی کنید.
public UserManager() : base(new UserStore<User>(new ApplicationDbContext())) { this.PasswordHasher = new SQLPasswordHasher(); }
ایجاد صفحات جدید مدیریت کاربران
- فایلهای Register.aspx.cs و Login.aspx.cs از کلاس UserManager استفاده میکنند. این ارجاعات را با کلاس UserManager جدیدی که در پوشه Models ساختید جایگزین کنید.
- همچنین ارجاعات استفاده از کلاس IdentityUser را به کلاس User که در پوشه Models ساختید تغییر دهید.
- لازم است توسعه دهنده مقدار ApplicationId را برای کاربران جدید طوری تنظیم کند که با شناسه اپلیکیشن جاری تطابق داشته باشد. برای این کار میتوانید پیش از ساختن حسابهای کاربری جدید در فایل Register.aspx.cs ابتدا شناسه اپلیکیشن را بدست آورید و اطلاعات کاربر را بدرستی تنظیم کنید.
private Guid GetApplicationID() { using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString)) { string queryString = "SELECT ApplicationId from aspnet_Applications WHERE ApplicationName = '/'"; //Set application name as in database SqlCommand command = new SqlCommand(queryString, connection); command.Connection.Open(); var reader = command.ExecuteReader(); while (reader.Read()) { return reader.GetGuid(0); } return Guid.NewGuid(); } }
var currentApplicationId = GetApplicationID(); User user = new User() { UserName = Username.Text, ApplicationId=currentApplicationId, …};
EF Code First #12
namespace EF_Sample07.DataLayer.Context { public interface IUnitOfWork { IDbSet<TEntity> Set<TEntity>() where TEntity : class; int SaveAllChanges(); void MarkAsChanged<TEntity>(TEntity entity) where TEntity : class; } } namespace EF_Sample07.DataLayer.Context { public class Sample07Context : DbContext, IUnitOfWork { public DbSet<Category> Categories { set; get; } public DbSet<Product> Products { set; get; } public new IDbSet<TEntity> Set<TEntity>() where TEntity : class { return base.Set<TEntity>(); } public int SaveAllChanges() { return base.SaveChanges(); } public void MarkAsChanged<TEntity>(TEntity entity) where TEntity : class { base.Entry<TEntity>(entity).State = EntityState.Modified; } } }
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 }
در قسمتهای قبلی (^ و ^) راهکارهایی جهت بالا بردن کارآیی، ارائه شد. در ادامه، به آخرین قسمت این سری اشاره خواهم کرد.
فراخوانی متد شناسایی تغییرات
یادآوری: قبل از هر چیز با توجه به این مقاله دانستن این نکته الزامی است که فراخوانی برخی متدها مانند DbSet.Add سبب فراخوانی DataContext.ChangeTracker.DetectChanges خواهند شد.
فرض کنید قصد افزودن 2000 موجودیت دانش آموز را دارید:
for (int i = 0; i < 2000; i++) { Pupil pupil = GetNewPupil(); db.Pupils.Add(pupil); } db.SaveChanges();
اگر به تصویر بالا دقت کنید بیش از 34 ثانیه (خط 193 قسمت سوم شکل) جهت افزودن 2000 موجودیت به کانتکست سپری شده است. در حالی که درج این 2000 موجودیت کمی بیش از 1 ثانیه (خط 195 قسمت سوم شکل) که 379 میلی ثانیه (قسمت دوم شکل) آن مربوط به اجرای کوئری اختصاص یافته طول کشیده است.
بیشترین زمان صرف شدهی برای درج 2000 موجودیت، در کد برنامه سپری شده است که با بررسی بیشتر در پروفایلر، متوجه زمان بر بودن فراخوانی متد ()DetectChanges که در فضای نام Data.Entity.Core وجود دارد خواهید شد. این متد 2000 بار به تعداد موجودیت هایی که قصد داریم به بانک اطلاعاتی اضافه نماییم، فراخوانی شده است.
همانطور که در شکل بالا مشخص است همان 34 ثانیه جهت ردیابی تغییرات صرف شده است. EF ردیابی تغییرات را بصورت پیش فرض هر زمانی که قصد افزودن یا ویرایش موجودیتی را داشته باشید، انجام خواهد داد و هر چه موجودیتهای بیشتری را بخواهید ویرایش یا اضافه نمایید، این زمان نیز بیشتر خواهد شد. در حقیقت زمان لازم برای الگوریتم ردیابی تغییرات بصورت نمایی با رشد موجودیتها افزوده میشود. به عبارت دیگر اگر این تعداد موجودیتها را به 4000 عدد برسانید، مدت زمان لازم بیش از 2 برابر افزوده خواهد شد.
راه حل اول:
استفاده از متد ()AddRange ارائه شده در EF6 که جهت درج دستهای (Bulk Insert) ارائه شده است:
var list = new List<Pupil>(); for (int i = 0; i < 2000; i++) { Pupil pupil = GetNewPupil(); list.Add(pupil); } db.Pupils.AddRange(list); db.SaveChanges();
راه حل دوم:
در سناریوهای پیچیده، مانند درج دستهای چندین موجودیت، شاید مجبور به خاموش نمودن این قابلیت شوید:
db.Configuration.AutoDetectChangesEnabled = false;
ردیابی تغییرات
هنگامی که موجودیتی را از بانک اطلاعاتی دریافت نمایید، میتوانید آن را ویرایش نمایید و مجددا به بانک اطلاعاتی اعمال نمایید. چون EF اطلاعی از قصد شما برای موجودیت نمیداند، مجبور است تغییرات شما را زیر نظر بگیرد که این زیر نظر گرفتن، هزینه و سربار دارد و این سربار و هزینه برای دادههای زیاد، بیشتر خواهد شد. بنابراین اگر قصد دارید اطلاعاتی فقط خواندنی را از بانک اطلاعاتی دریافت نمایید، بهتر است صراحتا به EF بگویید این موجودیت را تحت ردیابی قرار ندهد.string city = "New York"; List<School> schools = db.Schools .AsNoTracking() .Where(s => s.City == city) .Take(100) .ToList();
استفاده از متد AsNoTracking در کد بالا سبب خواهد شد 100 مدرسه که در شهر نیویورک وجود دارد توسط EF، بدون تحت نظر گرفتن آنها از بانک اطلاعاتی دریافت شوند و سرباری نیز تحمیل نشود.
ویوهای از قبل کامپایل شده
معمولا، هنگامی که از EF برای اولین بار استفاده مینمایید، ویوهایی ایجاد میگردد که برای ایجاد کوئریها مورد استفاده قرار میگیرند. این ویوها در طول حیات برنامه فقط یکبار ایجاد میشوند. ولی همین یکبار هم زمانبر هستند. خوشبختانه راههایی وجود دارد که ایجاد این ویوها را در زمان runtime انجام نداد و آن راه، استفاده از ویوهای از پیش کامپایل شده است. یکی از راههای ایجاد این ویوها استفاده از Entity Framework Power Tools است. بعد از نصب اکستنشن، بر روی فایل کانتکست راست کلیک کرده و سپس گزینهی Generate Views را از منوی Entity Framework انتخاب کنید.
توجه داشته باشید که هر تغییری را بعد از ایجاد این ویوها بر روی کانتکست اعمال نمایید، باید آنها را مجددا تولید کنید. برای آشنایی بیشتر با این ویوها به این لینک مراجعه کنید. هم چنین پکیج نیوگتی بنام EFInteractiveViews نیز برای این منظور تهیه و توزیع شده است.
حذف کوئریهای ابتدایی غیر ضروری
در هنگام شروع به کار با EF، چندین کوئری آغازین بر روی دیتابیس اجرا میشوند. یکی از کوئریهای آغازین جهت تشخیص نسخهی دیتابیس است که همانطور در تصویر زیر مشاهده میکنید، در حدود چند میلی ثانیه میباشد.
با توجه به توضیحات، در صورتیکه اطلاعی از نسخهی دیتابیس دارید، میتوانید این کوئری ابتدایی را تحریف نمایید. برای اینکار میتوان توسط متد ()ResolveManifestToken کلاسی که اینترفیس IManifestTokenResolver را پیاده سازی کرده است، نسخهی دیتابیس را برگردانیم و از یک رفت و برگشت به دیتابیس جلوگیری نماییم.
public class CustomManifestTokenResolver : IManifestTokenResolver { public string ResolveManifestToken(DbConnection connection) { return "2014"; } }
public class CustomDbConfiguration : DbConfiguration { public CustomDbConfiguration() { SetManifestTokenResolver(new CustomManifestTokenResolver()); } }
تخریب کانتکست
تخریب و از بین بردن کانتکست هنگامی که به آن نیاز نداریم بسیار ضروری است. یکی از روشهای اصولی برای Disposing کانتکست، محصور کردن آن بوسیله دستور Using است (البته فرض بر این است که قرار نیست از الگوهای اشاره شده استفاده نماییم). در صورت عدم تخریب صحیح کانتکست باید منتظر آسیب جدی به کارایی Garbage Collector جهت آزاد سازی منابع مورد استفاده کانتکست و هم چنین باز نمودن اتصالات جدید به دیتابیس باشید.
پاسخگویی به چندین درخواست بر روی یک کانکشن
EF از قابلیتی بنام Multiple Result Sets میتواند بهره ببرد که این قابلیت باعث میشود بر روی یک کانکشن ایجاد شده، یک یا چند درخواست از دیتابیس ارسال و یا دریافت شود که سبب کاهش تعداد رفت و برگشت به دیتابیس میشود. کاربرد دیگر این قابلیت، زمانی است که تاخیر زیادی (latency) بین اپلیکیشن و دیتابیس وجود دارد.
برای فعالسازی کافی است مقدار زیر را در کانکشن استرینگ اضافه نمایید:
MultipleActiveResultSets=True;
استفاده از متدهای ناهمگام
در C#5 و EF6 پشتیبانی خوبی از متدهای ناهمگام فراهم شده است و اکثر متدهایی مانند ToListAsync, CountAsync, FirstAsync, SaveChangesAsync و غیره که باعث رفت و برگشت به دیتابیس میشوند امکان پشتیبانی ناهمگام را نیز دارند. البته این قابلیت برای برنامههای یک درخواست در یک زمان شاید مفید نباشد؛ ولی برای برنامههای وبی برعکس. در برنامه وب جهت پشتیبانی از بارگذاری همزمان (concurrent) قابلیت ناهمگام (Async) سبب خواهد شد منابع تا زمان اجرای کوئری به ThreadPool بازگردانده شود و برای سرویس دهی مهیا باشند که باعث افزایش scalability خواهد شد.
بررسی و آزمایش با دادههای واقعی
در اکثر مواقع کارآیی با حجیم شدن دادهها کاهش پیدا میکند (البته در صورت عدم رعایت اصول استاندارد). بنابراین بررسی کارآیی در محیط هایی با حجم دادههای بالا ضروری است. هیچ چیز بدتر از آن نیست که همه چیز در محیط توسعه خوب و بی نقص باشد ولی در محیط عملیاتی به شکست بیانجامد. به همین جهت سعی کنید از ابزارهای تولید داده (^ و ^ و ^) برای ایجاد دادههای آزمایشی استفاده نمایید. سپس کارآیی کوئری خود را مورد بررسی و آزمایش قرار دهید.
در این بین به راحتی میتوان چندین نمونه از این ORMها را نام برد مثل IBatis , Hibernate ,Nhibernate و EF که از معروفترین آنها هستند.
من در حال حاضر قصد شروع یک پروژه اندرویدی را دارم و دوست دارم بجای استفادهی از Sqlitehelper، از یک ORM مناسب بهره ببرم که چند سوال برای من پیش میآید. آیا ORM ای برای آن تهیه شده است؟ اگر آری چندتا و کدامیک از آنها بهتر هستند؟ شاید در اولین مورد کتابخانهی Hibernate جاوا را نام ببرید؛ ولی توجه به این نکته ضروری است که ما در مورد پلتفرم موبایل و محدودیتهای آن صحبت میکنیم. یک کتابخانه همانند Hibernate مطمئنا برای یک برنامه اندروید چه از نظر حجم نهایی برنامه و چه از نظر حجم بزرگش در اجرا، مشکل زا خواهد بود و وجود وابستگیهای متعدد و دارا بودن بسیاری از قابلیتهایی که اصلا در بانکهای اطلاعاتی موبایل قابل اجرا نیست، باعث میشود این فریمورک انتخاب خوبی برای یک برنامه اندروید نباشد.
معیارهای انتخاب یک فریم ورک مناسب برای موبایل:
- سبک بودن: مهمترین مورد سبک بودن آن است؛ چه از لحاظ اجرای برنامه و چه از لحاظ حجم نهایی برنامه
- سریع بودن: مطمئنا ORMهای طراحی شدهی موجود، از سرعت خیلی بدی برخوردار نخواهند بود؛ اگر سر زبان هم افتاده باشند. ولی باز هم انتخاب سریع بودن یک ORM، مورد علاقهی بسیاری از ماهاست.
- یادگیری آسان و کانفیگ راحت تر.
OrmLight
این فریمورک مختص اندروید طراحی نشده ولی سبک بودن آن موجب شدهاست که بسیاری از برنامه نویسان از آن در برنامههای اندرویدی استفاده کنند. این فریم ورک جهت اتصالات JDBC و Spring و اندروید طراحی شده است.
نحوه معرفی جداول در این فریمورک به صورت زیر است:
@DatabaseTable(tableName = "users") public class User { @DatabaseField(id = true) private String username; @DatabaseField private String password; public User() { // ORMLite needs a no-arg constructor } public User(String username, String password) { this.username = username; this.password = password; } // Implementing getter and setter methods public String getUserame() { return this.username; } public void setName(String username) { this.username = username; } public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } }
SugarORM
این فریمورک مختص اندروید طراحی شده است. یادگیری آن بسیار آسان است و به راحتی به یاد میماند. همچنین جداول مورد نیاز را به طور خودکار خواهد ساخت. روابط یک به یک و یک به چند را پشتیبانی میکند و عملیات CURD را با سه متد Save,Delete و Find که البته FindById هم جزء آن است، پیاده سازی میکند.
برای استفاده از این فریمورک نیاز است ابتدا متادیتاهای زیر را به فایل manifest اضافه کنید:
<meta-data android:name="DATABASE" android:value="my_database.db" /> <meta-data android:name="VERSION" android:value="1" /> <meta-data android:name="QUERY_LOG" android:value="true" /> <meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="com.my-domain" />
public class User extends SugarRecord<User> { String username; String password; int age; @Ignore String bio; //this will be ignored by SugarORM public User() { } public User(String username, String password,int age){ this.username = username; this.password = password; this.age = age; } }
باقی عملیات آن از قبیل اضافه کردن یک رکورد جدید یا حذف رکورد(ها) به صورت زیر است:
User johndoe = new User(getContext(),"john.doe","secret",19); johndoe.save(); //ذخیره کاربر جدید در دیتابیس //حذف تمامی کاربرانی که سنشان 19 سال است List<User> nineteens = User.find(User.class,"age = ?",new int[]{19}); foreach(user in nineteens) { user.delete(); }
موقعیکه بحث کارآیی و سرعت پیش میآید نام GreenDAO هست که میدرخشد. طبق گفتهی سایت رسمی آن این فریمورک میتواند در ثانیه چند هزار موجودیت را اضافه و به روزرسانی و بارگیری نماید. این لیست حاوی برنامههایی است که از این فریمورک استفاده میکنند. جدول زیر مقایسهای است بین این کتابخانه و OrmLight که نشان میدهد 4.5 برابر سریعتر از OrmLight عمل میکند.
آموزش راه اندازی آن در اندروید استادیو، سورس آن در گیت هاب و مستندات رسمی آن.
Active Android
این کتابخانه از دو طریق فایل JAR و به شیوه maven قابل استفاده است که میتوانید روش استفادهی از آن را در این لینک ببینید و سورس اصلی آن هم در این آدرس قرار دارد. بعد از اینکه کتابخانه را به پروژه اضافه کردید، دو متادیتای زیر را که به ترتیب نام دیتابیس و ورژن آن هستند، به manifest اضافه کنید:
<meta-data android:name="AA_DB_NAME" android:value="my_database.db" /> <meta-data android:name="AA_DB_VERSION" android:value="1" />
public class MyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActiveAndroid.initialize(this); //ادامه برنامه } }
@Table(name = "User") public class User extends Model { @Column(name = "username") public String username; @Column(name = "password") public String password; public User() { super(); } public User(String username,String password) { super(); this.username = username; this.password = password; } }
ORMDroid
از آن دست کتابخانههایی است که سادگی و کم حجم بودن شعار آنان است و سعی دارند تا حد ممکن همه چیز را خودکار کرده و کمترین کانفیگ را نیاز داشته باشد. حجم فعلی آن حدود 20 کیلوبایت بوده و نمیخواهند از 30 کیلوبایت تجاوز کند.
برای استفادهی از آن ابتدا دو خط زیر را جهت معرفی تنظیمات به manifest اضافه کنید:
<meta-data android:name="ormdroid.database.name" android:value="your_database_name" /> <meta-data android:name="ormdroid.database.visibility" android:value="PRIVATE||WORLD_READABLE||WORLD_WRITEABLE" />
ORMDroidApplication.initialize(someContext);
public class Person extends Entity { public int id; public String name; public String telephone; } //==================== Person p = Entity.query(Person.class).where("id=1").execute(); p.telephone = "555-1234"; p.save(); // یا Person person = Entity.query(Person.class).where(eql("id", id)).execute(); p.telephone = "555-1234"; p.save();
سورس آن در گیت هاب
در اینجا سعی کردیم تعدادی از کتابخانههای محبوب را معرفی کنیم ولی تعداد آن به همین جا ختم نمیشود. ORMهای دیگری نظیر AndRom و سایر ORM هایی که در این لیست معرفی شده اند وجود دارند.
نکته نهایی اینکه خوب میشود دوستانی که از این ORMهای مختص اندروید استفاده کرده اند؛ نظراتشان را در مورد آنها بیان کنند و مزایا و معایب آنها را بیان کنند.
در این قسمت در خصوص نحوهی نصب SSAS صحبت خواهم کرد .
تصور میکنم بهتر است در خصوص آنچه در هنگام نصب SQL Server انتخاب میکنیم، دقت بیشتری کنیم. بسیار دیده ام که برخی از دوستان و همکاران در هنگام نصب SQL Server در قسمت انتخاب Featureها تمامی آنها را انتخاب کرده در صورتی که تنها به Database Engine نیاز دارند و عملا با این کار ، کارایی Database Server خود را پایین میآورند .
بنابر این توصیه میکنم در پنجره ی Feature Selection فقط آنچه را که نیاز دارید نصب نمایید .
بنابر این در صورتی که شما جزو آن دسته دوستانی میباشید که در پنجره ی Feature Selection تمامی گزینهها را انتخاب نموده اید، خوب نیازی به نصب مجدد SSAS ندارید و شما ناخواسته این سرویس را برروی Database Server خود نصب نموده اید .
در صورتی که شما قبلا این سرویس را برروی سرور خود نصب نکرده اید و فقط Database Engine را نصب نموده اید مراحل زیر را طی نمایید.
1. در ابتدا Set Up مربوط به SQL Server 2012 را اجرا نمایید. و در صفحهی ابتدایی برنامهی نصب ، مطابق شکل زیر روی Installation کلیک کنید. و در قسمت سمت راست گزینهی New SQL Server stand-alone installation or add features to an existing installation را انتخاب نمایید.
2. د رپنجرهی Setup Support Rules مطمئن شوید که تمامی پیش شرایط نصب را دارید (در صورتی که Warning داشته باشید احتمالا در مراحل بعدی ، نصب برنامه با مشکل روبرو خواهد شد یا بعد از نصب برخی قسمتهای برنامه به درستی کار نمیکنند. ) در صورتی که در قسمتی با Warning روبرو شدید بعد از برطرف کردن مشکل دکمهی Rerun را بزنید به عبارت دیگر نیازی نمیباشد مراحل نصب را از ابتدا ادامه دهید. سپس دکمهی OK را بفشارید .
3. در پنجرهی بعدی دکمهی Install را بزنید. سپس دوباره صفحهی Setup Support Rules را خواهید داشت. مطمئن شوید تمامی پیش شرایط Passed شده باشند. سپس دکمهی Next را بزنید.
4. در پنجرهی بعدی گزینهی Add features to and existing instance of SQL Server 2012 را انتخاب نمایید اگر شما قبلا فقط DataBase Engine را نصب کرده اید و در غیر این صورت Perform a new installation of SQL Server 2012 را انتخاب نماید. سپس دکمهی Next را بزنید.
5. در صفحهی Feature Selection گزینهی Analysis Services را مطابق شکل زیر انتخاب نمایید.و سپس دکمهی Next را بزنید .
6. در صفحهی بعدی برنامهی نصب به شما توضیحاتی در خصوص مقدار فضای Hard برای نصب سرویس(های) انتخاب شده ، نمایش میدهد. دکمهی Next را بزنید
7. در صفحهی Server configuration مد SQL Server Analysis Services را بر روی حالت Automatic تنطیم کنید. سپس دکمهی Next را بزنید.
8. سپس در صفحهی Analysis Services Confiquration گزینهی Multidimensional and Data Mining Mode را انتخاب نمایید. و همچنین برای مشخص نمودن Administrator سرویس SSAS نام کاربر را در قسمت پایین پنجره وارد نمایید. در صورتی که شما با کاربری که عملا Administrator سرویس SSAS میباشد در سیستم عامل ویندوز لاگین نموده اید میتوانید دکمهی Add Current User را بزنید. سپس دکمهی Next را بزنید .
9. در صفحهی بعد بررسیهای Installation Configuration Rules انجام میگردد . دقت داشته باشید که تمامی موارد Passed گردیده باشند. سپس دکمهی Next را بزنید .
10. در صفحهی Ready to install دکمهی Install را بفشارید.
11. در صورتی که نصب با موفقیت انجام شده باشد، صفحه ای به شکل زیر خواهید دید.
خوب به شما تبریک میگوییم شما هم اکنون سرویس SSAS را برروی سرور خود نصب نموده اید. برای اطمینان از تنظیمات Registry توصیه میکنم سیستم عامل خود را Restart نمایید.
برای اطمینان از نصب سرویس SSAS بر روی سیستم خود میتوانید در پنجرهی Run عبارت services.msc را وارد کنید .
سپس در قسمت سرویسها شما میتوانید سرویس SSAS را مشاهده نمایید مطابق شکل زیر.
در قسمتهای بعدی این سری از آموزشهای MDX Query تلاش خواهم کرد طریقهی نصب پایگاه دادهی Adventure Work DW و همچنین ساخت پایگاه دادهی Multidimensional مربوط به Adventure Work DW را آموزش دهم.
قابلیتهای نسخههای مختلف Sql Server Express 2008
نسخه / قابلیت | Database Engine | Management Studio Basic | Full-Text Search | Reporting Services |
Management Studio Basic | X | |||
Runtime Only | X | |||
with Tools | X | X | ||
with Advanced Services | X | X | X | X |
(SQL Server 2008 Express (Runtime Only
SQL Server 2008 Express with Tools
SQL Server 2008 Express with Advanced Services
دو حالت برای نصب SQL Server وجود دارد:
نصب SQL Server Express 2008 از طریق Command Prompt
Setup.exe /q /Action=Install /Hideconsole /Features=SQL,Tools /InstanceName=SQLExpress /SQLSYSADMINACCOUNTS="Builtin\Administrators" /SQLSVCACCOUNT="<DomainName\UserName>" /SQLSVCPASSWORD="<StrongPassword>
باتوجه به سیاستها نصب میتوانید از پارامترها دیگری نیز استفاده کنید. بعنوان مثال پارامترهای زیر برای نصب روی سیستمی که نام کاربری و پسورد انرا نداریم مناسب است:
setup.exe /q /Action=Install /Features=SQL /InstanceName=SQLExpress /SECURITYMODE=SQL /SAPWD="1234567" /SQLSYSADMINACCOUNTS="Builtin\Administrators" /SQLSVCACCOUNT="NT AUTHORITY\SYSTEM" /SQLSVCSTARTUPTYPE="Automatic" /TCPENABLED=1
Setup.exe /q /Hideconsole /ACTION=upgrade /INSTANCENAME=SQLExpress
برای مشاهده دیگر پارامترها به مستندات MSDN مراجعه کنید. همچنین میتوان نصب از طریق فایل Configuration را نیز انجام داد.