راهنمای آموزشی رایگان entity framework
پیش فرضهای تعیین کلید اصلی در EF Core
به صورت پیش فرض هر خاصیتی که به نام Id و یا type name>Id> باشد، به عنوان primary key تفسیر خواهد شد؛ مانند:
public class Car { public string Id { get; set; }
public class Car { public string CarId { get; set; }
نحوهی تعیین کلید اصلی به صورت صریح
اگر یکی از دو حالت فوق برقرار نباشند، باید کلید اصلی را به نحو صریحی مشخص کرد.
الف) از طریق ویژگیها
public class Car { [Key] public string LicensePlate { get; set; }
ب) با استفاده از روش Fluent API
public class MyContext : DbContext { public DbSet<Car> Cars { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Car>() .HasKey(c => c.LicensePlate); } }
پیشنیاز کار با ویژگیها در EF Core
در اسمبلی که مدلهای موجودیتها شما قرار دارند، نیاز است وابستگی System.ComponentModel.Annotations به فایل project.json پروژه اضافه شود، تا ویژگیهایی مانند Key، شناسایی و قابل استفاده شوند:
{ "dependencies": { "System.ComponentModel.Annotations": "4.1.0" } }
تعیین کلید ترکیبی و یا Composite key
اگر نیاز است چندین خاصیت را به صورت کلید اصلی معرفی کرد که به آن composite key هم میگویند، تنها روش ممکن، استفاده از Fluent API و به صورت زیر است:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Car>() .HasKey(c => new { c.State, c.LicensePlate }); }
روشهای مختلف تولید خودکار مقادیر خواص
حالت پیش فرض تولید مقدار فیلدهای Id عددی، همان حالت خود افزایندهای است که توسط بانک اطلاعاتی کنترل میشود و یا کلید اصلی که از نوع Guid تعیین شود نیز به صورت خودکار توسط بانک اطلاعاتی در حین عملیات Add، مقدار دهی میشود (با استفاده از الگوریتم Guid سری در SQL Server).
اگر این حالات مطلوب شما نیست، حالتهای سه گانهی ذیل را میتوان استفاده کرد:
الف) هیچ دادهی خودکاری تولید نشود
برای اینکار میتوان با استفاده از ویژگی DatabaseGenerated و تنظیم مقدار آن به None، جلوی تولید خودکار کلید اصلی را گرفت. در این حالت باید هم در حین عملیات Add و هم در حین عملیات Update، مقادیر را خودتان مقدار دهی کنید:
public class Blog { [DatabaseGenerated(DatabaseGeneratedOption.None)] public int BlogId { get; set; } public string Url { get; set; } }
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>() .Property(b => b.BlogId) .ValueGeneratedNever(); }
ب) تولید دادههای خودکار فقط در حالت Add
حالت Add به این معنا است که دادههای خواص مشخصی، برای موجودیتهای «جدید»، به صورت خودکار تولید خواهند شد. اینکه آیا واقعا این مقادیر به صورت خودکار تولید میشوند یا خیر، صرفا وابستهاست به بانک اطلاعاتی در حال استفاده. برای مثال SQL Server برای نوعهای Guid، به صورت خودکار با کمک الگوریتم SQL Server sequential GUID، کار مقدار دهی یک چنین فیلدهایی را انجام میدهد.
این فیلدها باید توسط ویژگی DatabaseGenerated و با مقدار Identity مشخص شوند. در اینجا Identity به معنای فیلدهایی است که به صورت خودکار توسط بانک اطلاعاتی مقدار دهی میشوند و الزاما به کلید اصلی اشاره نمیکنند. برای مثال در موجودیت ذیل، خاصیت تاریخ ثبت رکورد، از نوع Identity مشخص شدهاست. به این معنا که در حین ثبت اولیهی رکورد آن، نیازی نیست تا خاصیت Inserted را مقدار دهی کرد. اما اینکه آیا SQL Server یک چنین کاری را به صورت خودکار انجام میدهد، پاسخ آن خیر است. SQL server فقط برای فیلدهای عددی و Guid ایی که با DatabaseGeneratedOption.Identity مزین شده باشند، مقادیر متناظری را به صورت خودکار تولید میکند. برای حالت DateTime نیاز است، مقدار پیش فرض فیلد را صریحا مشخص کرد که توسط ویژگیها میسر نیست و فقط fluent API از آن پشتیبانی میکند.
public class Blog { public int BlogId { get; set; } public string Url { get; set; } [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public DateTime Inserted { get; set; } }
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>() .Property(b => b.Inserted) .ValueGeneratedOnAdd(); }
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>() .Property(b => b.Inserted) .HasDefaultValueSql("getdate()"); }
یک نکته: در حالت DatabaseGeneratedOption.Identity و یا ValueGeneratedOnAdd فوق، اگر مقداری به این نوع فیلدها انتساب داده شده باشد که با مقدار پیش فرض آنها (property.ClrType.GetDefaultValue) متفاوت باشد، از این مقدار جدید، بجای تولید مقداری خودکار، استفاده خواهد شد. برای مثال مقدار پیش فرض رشتهها، نال، مقادیر عددی، صفر و برای Guid مقدار Guid.Empty است. اگر هر مقدار دیگری بجای اینها به فیلدهای فوق انتساب داده شوند، از آنها استفاده میشود.
ج) تولید دادههای خودکار در هر دو حالت Add و Update
تولید دادهها در حالتهای Add و Update به این معنا است که یک چنین خواصی، همواره با فراخوانی متد SaveChanges، دارای مقدار خودکار جدیدی خواهند شد و نیازی نیست در کدها مقدار دهی شوند. برای مشخص سازی این نوع خواص، از ویژگی DatabaseGenerated با مقدار Computed و یا متد ValueGeneratedOnAddOrUpdate در حالت Fluent API میتوان استفاده کرد:
public class Blog { public int BlogId { get; set; } public string Url { get; set; } [DatabaseGenerated(DatabaseGeneratedOption.Computed)] public DateTime LastUpdated { get; set; } }
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>() .Property(b => b.LastUpdated) .ValueGeneratedOnAddOrUpdate(); }
تذکر: در اینجا نیز همانند حالت ValueGeneratedOnAdd، اگر این خواص مشخص شده، دارای مقدار متفاوتی با مقدار پیش فرض آنها باشند، از این مقادیر جدید بجای تولید خودکار مقادیر استفاده خواهد شد.
خواص محاسباتی (Computed Columns) و تفاوت آنها با DatabaseGeneratedOption.Computed
خواص محاسباتی (Computed Columns)، خواصی هستند که مقادیر آنها در بانک اطلاعاتی محاسبه میشوند و کاملا متفاوت هستند با DatabaseGeneratedOption.Computed که مفهوم دیگری دارد. DatabaseGeneratedOption.Computed به این معنا است که این فیلد خاص، با هر بار فراخوانی SaveChanges باید مقدار محاسبه شدهی جدیدی را داشته باشد و روش تولید این مقدار خودکار، یا بر اساس Guidهای سری است، یا توسط فیلدهای خود افزایندهی عددی و یا از طریق مقادیر پیش فرضی مانند getdate در حین ثبت یا به روز رسانی، مقدار دهی میشوند. اما خواص محاسباتی، یکی از امکانات «گزارشگیری سریع» SQL Server هستند و به نحو ذیل، تنها توسط Fluent API قابل تنظیم میباشند:
public class Person { public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string DisplayName { get; set; } } public class MyContext : DbContext { public DbSet<Person> People { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Person>() .Property(p => p.DisplayName) .HasComputedColumnSql("[LastName] + ', ' + [FirstName]"); } }
امکان تعریف Sequence در EF Core 1.0
Sequence قابلیتی است که به SQL Server 2012 اضافه شدهاست و توضیحات بیشتر آنرا در مطلب «نحوه ایجاد Sequence و استفاده آن در Sql Server 2012» میتوانید مطالعه کنید.
در EF Core، امکان مدلسازی Sequence نیز پیش بینی شدهاست. آنها به صورت پیش فرض در مدلها ذکر نمیشوند و همچنین وابستگی به جدول خاصی ندارند. به همین جهت امکان تعریف آنها صرفا توسط Fluent API وجود دارد:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasSequence<int>("OrderNumbers", schema: "shared") .StartsAt(1000).IncrementsBy(5); modelBuilder.Entity<Order>() .Property(o => o.OrderNo) .HasDefaultValueSql("NEXT VALUE FOR shared.OrderNumbers"); }
در مثال فوق، ابتدا یک Sequence نمونه به نام OrderNumbers تعریف شدهاست که از عدد 1000 شروع شده و واحد افزایش آن 5 است. سپس از این نام در قسمت مقدار پیش فرض ستون OrderNo استفاده شدهاست.
و یا از Sequence ها میتوان برای تعیین مقدار پیش فرض Primary key بجای حالت identity خود افزایش یابنده استفاده کرد:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasSequence<int>("PrimaryKeyWithSequenceSequence"); modelBuilder.Entity<PrimaryKeyWithSequence>(entity => { entity.Property(e => e.PrimaryKeyWithSequenceId).HasDefaultValueSql("NEXT VALUE FOR [PrimaryKeyWithSequenceSequence]"); }); }
[PrimaryKeyWithSequenceSequence] دریافت و سپس بجای فیلد id درج میشود.
به این روش الگوریتم Hi-Low هم میگویند که یکی از مهمترین اهداف آن داشتن یک سری Id منحصربفرد، جهت بالابردن سرعت insertها در یک batch است. در حالت عادی insertها، ابتدا یک insert انجام میشود، سپس کوئری گرفته شده و آخرین Id درج شده به کلاینت بازگشت داده میشود. این روش، برای انجام تنها یک insert، سریع است. اما برای batch insert، به شدت کارآیی پایینی دارد. به همین جهت دسترسی به بازهای از اعداد منحصربفرد، پیش از شروع به insert تعداد زیادی رکورد، سرعت نهایی کار را بالا میبرد.
نحوهی تعریف ایندکسها در EF Core 1.0
برای افزودن ایندکسها به EF Core 1.0، تنها روش میسر، استفاده از Fluent API است (و برخلاف EF 6.x از روش data annotations فعلا پشتیبانی نمیکند؛ هرچند API جدید آن نسبت به EF 6.x بسیار واضحتر است و با ابهامات کمتر).
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>() .HasIndex(b => b.Url) .HasName("Index_Url");
modelBuilder.Entity<Blog>().HasIndex(b => b.Url).HasName("Index_Url").IsUnique();
modelBuilder.Entity<Person>() .HasIndex(idx => new { idx.FirstName, idx.LastName }) .IsUnique();
یک نکته: اگر از پروایدر SQL Server استفاده میکنید، میتوان متد الحاقی ویژهای را به نام ForSqlServerIsClustered نیز برای تعریف clustered indexes، در این زنجیره ذکر کرد.
امکان تعریف Alternate Keys در EF Core 1.0
به Unique Constraints در EF Core، نام Alternate Keys را دادهاند و این مورد نیز تنها از طریق Fluent API قابل تنظیم است:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Car>() .HasAlternateKey(c => c.LicensePlate) .HasName("AlteranteKey_LicensePlate"); }
اگر متد HasName در اینجا ذکر نشود، نام پیش فرض آن <type name>_<property name> خواهد بود و اگر همانند composite keys و یا ایندکسهای ترکیبی، چند خاصیت ذکر شوند، قسمت property name به جمع نام تمام خواص ذکر شده و جدا شدهی با _ تنظیم میشود.
برای نمونه اگر یک Alternate Key ترکیبی را به صورت ذیل تعریف کنیم:
modelBuilder.Entity<Person>() .HasAlternateKey(x => new { x.FirstName, x.LastName });
table.UniqueConstraint("AK_Persons_FirstName_LastName", x => new { x.FirstName, x.LastName });
سؤال: یک Unique Constraint با Unique Index چه تفاوتی دارد؟
در پشت صحنه، پیاده سازی یک Unique Constraint با Unique Index تفاوتی ندارند. فقط از دیدگاه روشنتر شدن مقصود، استفادهی از Unique Constraint ترجیح داده میشود.
البته از دیدگاه بانک اطلاعاتی پیاده سازی کننده نیز برای نمونه SQL Server، این تفاوتها وجود دارند:
الف) یک Unique Constraint را نمیتوان غیرفعال کرد؛ برخلاف Unique Indexها.
ب) Unique Constraintها موارد اضافهتری را مانند FILLFACTOR و IGNORE_DUP_KEY نیز میتوانند تنظیم کنند.
ج) امکان تعریف فیلترها برای Unique Indexها وجود دارد؛ برخلاف Unique Constraint ها.
که البته از دیدگاه EF، این سه مورد اهمیتی ندارند و بیشتر روشنتر شدن مقصود، هدف اصلی آنها است.
LocalDb دیتابیس توصیه شده برای ویژوال استودیو است و برای انواع پروژهها مانند اپلیکیشنهای وب میتواند استفاده شود. هنگام استفاده از این دیتابیس در IIS Express یا Cassini همه چیز طبق انتظار کار میکند. اما به محض آنکه بخواهید از آن در Full IIS استفاده کنید با خطاهایی مواجه میشوید. مقصود از Full IIS همان نسخه ای است که بهمراه ویندوز عرضه میشود و در قالب یک Windows Service اجرا میگردد.
هنگام استفاده از Full IIS دو خاصیت از LocalDb باعث بروز مشکل میشوند:
- LocalDb نیاز دارد پروفایل کاربر بارگذاری شده باشد
- بصورت پیش فرض، وهله LocalDb متعلق به یک کاربر بوده و خصوصی است
در ادامه این مقاله روی بارگذاری پروفایل کاربر تمرکز میکنیم، و در قسمت بعدی به مالکیت وهله LocalDb میپردازیم.
بارگذاری پروفایل کاربر
بگذارید دوباره به خطای موجود نگاهی بیاندازیم:
System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 0 - [x89C50120])
این پیغام خطا زیاد مفید نیست، اما LocalDb اطلاعات بیشتری در Event Log ویندوز ذخیره میکند. اگر Windows Logs را باز کنید و به قسمت Application بروید پیغام زیر را مشاهده خواهید کرد.Reported at line: 400
بعلاوه این پیام خطا:
Cannot get a local application data path. Most probably a user profile is not loaded. If LocalDB is executed under IIS, make sure that profile loading is enabled for the current user.
به احتمال زیاد تعداد بیشتری از این دو خطا در تاریخچه وقایع وجود خواهد داشت، چرا که منطق کانکشن ADO.NET چند بار سعی میکند در بازههای مختلف به دیتابیس وصل شود.
پیغام خطای دوم واضح است، نیاز است پروفایل کاربر را بارگذاری کنیم. انجام اینکار زیاد مشکل نیست، هر Application Pool در IIS تنظیماتی برای بارگذاری پروفایل کاربر دارد که از قسمت Advanced Settings قابل دسترسی است. متاسفانه پس از انتشار سرویس پک 1 برای Windows 7 مسائل کمی پیچیدهتر شد. در حال حاظر فعال کردن loadUserProfile برای بارگذاری کامل پروفایل کاربر به تنهایی کافی نیست، و باید setProfileEnvironment را هم فعال کنیم. برای اطلاعات بیشتر در این باره به مستندات KB 2547655 مراجعه کنید. بدین منظور باید فایل applicationHost.config را ویرایش کنید. فایل مذکور در مسیر C:\Windows\System32\inetsrv\config قرار دارد. همانطور که در مستندات KB 2547655 توضیح داده شده، باید پرچم هر دو تنظیمات را برای ASP.NET 4.0 فعال کنیم:
<add name="ASP.NET v4.0" autoStart="true" managedRuntimeVersion="v4.0" managedPipelineMode="Integrated"> <processModel identityType="ApplicationPoolIdentity" loadUserProfile="true" setProfileEnvironment="true" /> </add>
جای هیچ نگرانی نیست، چرا که این پیغام خطا انتظار میرود. همانطور که در ابتدا گفته شد، دو خاصیت LocalDb باعث بروز این خطاها میشوند و ما هنوز به خاصیت دوم نپرداخته ایم. بصورت پیش فرض وهلههای LocalDb خصوصی (private) هستند و در Windows account جاری اجرا میشوند. بنابراین ApplicationPoolIdentity در IIS به وهلههای دیتابیس دسترسی نخواهد داشت. در قسمت دوم این مقاله، راههای مختلفی را برای رفع این مشکل بررسی میکنیم.
مشکلی که داشتم با نمونه مثال عنوان شده این کتابخانه در dot NET Core این بود :
به همین خاطر سعی کردم تا کدهای کتابخانه jqGridHelper قدیمی که بر روی نسخه قبلی ASP.NET MVC به راحتی برای انواع بانکهای اطلاعاتی از قبیل SQL Server و MongoDB کار میکرد را تغییراتی بدهم تا بتوان از این مثال برای استفاده در dot Net Core در انواع بانکهای اطلاعاتی SQL Server و MongoDB و غیره که قبلا استفاده میشد به راحتی استفاده کرد و هم کدنویسی سمت سرور برای نمایش و فیلترینگ انواع گریدها را به شدت کاهش میدهد
Demo-AspNetCore-JqGrid.rar
1- اندازه گیری تعداد Transactionها در واحد زمان روی یک Database خاص در SQL Server
جهت بدست آوردن تعداد Transactionها در واحد زمان( Transactions Per Second ) روی یک Database خاص در یک سیستم عملیاتی، جهت ارتقاء سخت افزاری ، تست فشار و ... میتوانید از یک DMV با نام sys.dm_os_performance_counters به طریق زیر استفاده نمائید:declare @cntr_value bigint Select @cntr_value=cntr_value from sys.dm_os_performance_counters where instance_name='AdventureWorks' and counter_name='Write Transactions/sec' /* ایجاد یک تاخیر مثلاً یک ثانیه */ waitfor delay '00:00:01' Select cntr_value -@cntr_value from sys.dm_os_performance_counters where instance_name='AdventureWorks' and counter_name='Write Transactions/sec'
2- sys.sp_MSforeachtable
از رویههای ذخیره شده UnDocumented در SQL Server میباشد و این قابلیت را دارا است که برای هر یک از جداول موجود در یک بانک اطلاعاتی، یک رویهای را اجرا کند. برای مثال با استفاده از دستور زیر، میتوانید تعداد سطرها، اندازهی دادهها و ایندکسهای یک جدول را بدست آورید
EXEC sys.sp_MSforeachtable 'sp_spaceused ''?''';
USE [AdventureWorksDW2008R2] GO CREATE TABLE #TableSpaceUsed( [name] [nvarchar](120) NULL, [rows] [nvarchar](120) NULL, [reserved] [nvarchar](120) NULL, [data] [nvarchar](120) NULL, [index_size] [nvarchar](120) NULL, [unused] [nvarchar](120) NULL ) ON [PRIMARY] Insert Into #TableSpaceUsed EXEC sys.sp_MSforeachtable 'sp_spaceused ''?'''; Select * from #TableSpaceUsed Order by CAST([rows] as int) desc Drop table #TableSpaceUsed
مروری بر Claim
Relying party application
هر برنامه سمت client که از Claim پشتیبانی کند
مزایای Claim
- جدا سازی برنامه از جزییات شناسایی
- انعطاف پذیری در احراز هویت
- Single sign-on
- عدم نیاز به VPN
- متحد کردن مجموعه با دیگر شرکت ها
- متحد کردن مجموعه با سرویسهای غیر از AD
عناصر Claim
Claim شامل عناصر زیر میباشد :
- Token
- Claim
- Provider/Issuer
- Sharepoint STS
- ADFS
- ACS
- OID
- ,و غیره