پس از مدتی که از شروع به کار یک سیستم میگذرد، همانطور که تعریف ایندکسهای مفید سرعت جستجوها را بالا میبرد، ایجاد fragmentation در آنها نیز تاثیر منفی در کارآیی خواهد داشت. به همین منظور نیاز است هر از چندگاهی بررسی شود میزان fragmentation ایندکسها چقدر است. اگر این میزان بیش از 30 درصد بود توصیه شده است که از دستور DBCC INDEXDEFRAG استفاده شود یا بازسازی مجدد ( rebuild ) ایندکسها صورت گیرد.
یکی دیگر از امکانات dmv های اس کیوال سرورهای 2005 به بعد، ارائه آمار میزان fragmentation ایندکسها است که کوئری آن به صورت زیر میتواند باشد:
USE dbName;
SELECT OBJECT_NAME(DMV.object_id) AS TABLE_NAME,
SI.NAME AS INDEX_NAME,
avg_fragmentation_in_percent AS FRAGMENT_PERCENT,
DMV.record_count
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'SAMPLED') AS
DMV
LEFT OUTER JOIN SYS.INDEXES AS SI
ON DMV.OBJECT_ID = SI.OBJECT_ID
AND DMV.INDEX_ID = SI.INDEX_ID
WHERE avg_fragmentation_in_percent > 10
AND index_type_desc IN ('CLUSTERED INDEX', 'NONCLUSTERED INDEX')
AND DMV.record_count >= 2000
ORDER BY
TABLE_NAME DESC
باید در نظر داشت که اجرای این کوئری بر روی یک دیتابیس حجیم زمانبر بوده و احتمالا عملکرد سیستم را تحت تاثیر قرار میدهد. بنابراین استفاده از آن در خارج از ساعات کاری باید مد نظر باشد. بازسازی ایندکسها نیز به همین صورت است.
برای بازسازی تمامی ایندکسهای یک دیتابیس مفروض میتوان از کوئری زیر استفاده کرد:
DECLARE @TableName VARCHAR(255)
DECLARE @sql NVARCHAR(500)
DECLARE @fillfactor INT
SET @fillfactor = 80
DECLARE TableCursor CURSOR
FOR
SELECT OBJECT_SCHEMA_NAME([object_id]) + '.' + NAME AS TableName
FROM sys.tables
OPEN TableCursor
FETCH NEXT FROM TableCursor INTO @TableName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @sql = 'ALTER INDEX ALL ON ' + @TableName +
' REBUILD WITH (FILLFACTOR = ' + CONVERT(VARCHAR(3), @fillfactor) + ')'
EXEC (@sql)
FETCH NEXT FROM TableCursor INTO @TableName
END
CLOSE TableCursor
DEALLOCATE TableCursor
In this post I described the problem that by default, DataAnnotation validation doesn't recursively inspect all properties in an object for DataAnnotation attributes. There are several solutions to this problem, but in this post I used the MiniValidation library from Damian Edwards. This simple library provides a convenience wrapper around DataAnnotation validation, as well as providing features like recursive validation. Finally I showed how you can replace the built-in DataAnnotation validation with a MiniValidation-based validator
var builder = WebApplication.CreateBuilder(args); builder.Services.AddOptions<MySettings>() .BindConfiguration("MySettings") .ValidateMiniValidation() // 👈 Replace with mini validation .ValidateOnStart(); var app = builder.Build();
OptionsValidationException: DataAnnotation validation failed for 'MySettings' member: 'Nested.Value' with errors: 'The Value field is required.'.; DataAnnotation validation failed for 'MySettings' member: 'Nested.Count' with errors: 'The field Count must be between 1 and 100.'. Microsoft.Extensions.Options.OptionsFactory<TOptions>.Create(string name) Microsoft.Extensions.Options.OptionsMonitor<TOptions>+<>c__DisplayClass10_0.<Get>b__0()
روش متداول تنظیمات EF Core در برنامههای ASP.NET Core، به صورت معرفی یک DbContext سفارشی، به سیستم تزریق وابستگیهای آن است و سپس میتوان به وهلهای از این Context، توسط تزریق آن به سازندههای کلاسهای مختلف برنامه، دسترسی یافت. به این معنا که به ازای هر درخواست رسیده، یک وهلهی جدید از DbContext ایجاد خواهد شد. در نگارش 2، روش جدیدی برای ثبت DbContext برنامه معرفی شدهاست که در صورت بکارگیری آن، بجای وهله سازی مجدد Contextها، ابتدا استخر موجود Contextها بررسی میشود و در صورت مهیا بودن نمونهای، بجای نمونه سازی از صفر آن، از این نمونهی موجود، استفادهی مجدد خواهد شد. در پایان کار درخواست، تنها وضعیت این Context به حالت اولیه برگردانده شده و سپس به استخر Contextها برای استفادهی مجدد بازگشت داده میشود. این مفهوم درحقیقت پیاده سازی مفهوم connection pooling موجود در ADO.NET است. به این ترتیب هزینهی ساخت و ایجاد اتصالات به بانک اطلاعاتی به شدت کاهش خواهد یافت.
نحوهی معرفی DbContext pooling
اینبار بجای روش قبلی و استفاده از متد AddDbContext
از متد جدید AddDbContextPool استفاده میشود:
محدودیتهای روش DbContext pooling
در حالت استفادهی از روش AddDbContextPool، دیگر متد OnConfiguring کلاس Context سفارشی شما فراخوانی نخواهد شد. بنابراین تمام تنظیمات ابتدایی برنامه را باید به همان کلاس آغازین برنامه منتقل کنید و کلاس Context، این تنظیمات را به صورت ذیل از طریق سازندهی آن دریافت میکند:
همچنین باید درنظر داشت که استفادهی مجدد از یک Context به معنای حفظ مقادیر فیلدهای private کلاس Context سفارشی شما نیز میشود. در اینجا پس از پایان هر درخواست، تنها وضعیت Context از دیدگاه EF به حالت اول بازگشت داده میشود؛ اما حالت شیء Context و تمام اطلاعات فیلدهای خصوصی آن در همان حالت قبلی (و همان وهلهی موجود پیشین و اصلی) رها میشوند. چون وهله سازی مجددی از آن صورت نخواهد گرفت.
یک مثال: بررسی بهبود کارآیی برنامه در حالت استفادهی از DbContext pooling
کدهای کامل این مثال را برای اجرا میتوانید از اینجا دریافت کنید: ContextPooling.zip
در اینجا یکبار حالت متداول AddDbContext
و سپس روش جدید AddDbContextPool
بررسی و اجرا شدهاند. نتیجهی نهایی به صورت ذیل است:
همانطور که ملاحظه میکنید، در حالت ContextPooling، تعداد وهله سازیهای صورت گرفته به شدت کاهش یافتهاست و همچنین قابلیت پاسخدهی برنامه به علت کاهش سربار اتصال به بانک اطلاعاتی نیز حدود 55 درصد بهبود یافتهاست.
نحوهی معرفی DbContext pooling
اینبار بجای روش قبلی و استفاده از متد AddDbContext
services.AddDbContext<BloggingContext>( options => options.UseSqlServer(connectionString));
services.AddDbContextPool<BloggingContext>( options => options.UseSqlServer(connectionString));
محدودیتهای روش DbContext pooling
در حالت استفادهی از روش AddDbContextPool، دیگر متد OnConfiguring کلاس Context سفارشی شما فراخوانی نخواهد شد. بنابراین تمام تنظیمات ابتدایی برنامه را باید به همان کلاس آغازین برنامه منتقل کنید و کلاس Context، این تنظیمات را به صورت ذیل از طریق سازندهی آن دریافت میکند:
public class BloggingContext : DbContext { public BloggingContext(DbContextOptions<BloggingContext> options) : base(options){}
همچنین باید درنظر داشت که استفادهی مجدد از یک Context به معنای حفظ مقادیر فیلدهای private کلاس Context سفارشی شما نیز میشود. در اینجا پس از پایان هر درخواست، تنها وضعیت Context از دیدگاه EF به حالت اول بازگشت داده میشود؛ اما حالت شیء Context و تمام اطلاعات فیلدهای خصوصی آن در همان حالت قبلی (و همان وهلهی موجود پیشین و اصلی) رها میشوند. چون وهله سازی مجددی از آن صورت نخواهد گرفت.
یک مثال: بررسی بهبود کارآیی برنامه در حالت استفادهی از DbContext pooling
کدهای کامل این مثال را برای اجرا میتوانید از اینجا دریافت کنید: ContextPooling.zip
در اینجا یکبار حالت متداول AddDbContext
public static void RunWithoutContextPooling() { Console.WriteLine("\nRun Without ContextPooling"); var serviceProvider = new ServiceCollection() .AddEntityFrameworkSqlServer() .AddDbContext<BloggingContext>( c => c.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Demo.ContextPooling;Trusted_Connection=True;ConnectRetryCount=0;")) .BuildServiceProvider(); new RunTests().Start(serviceProvider); }
public static void RunWithContextPooling() { Console.WriteLine("\nRun With ContextPooling"); var serviceProvider = new ServiceCollection() .AddEntityFrameworkSqlServer() .AddDbContextPool<BloggingContext>( c => c.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Demo.ContextPooling;Trusted_Connection=True;ConnectRetryCount=0;"), poolSize: 16) .BuildServiceProvider(); new RunTests().Start(serviceProvider); }
Run Without ContextPooling [10:49:30.728] Context creations: 637 | Requests per second: 597 [10:49:31.746] Context creations: 1069 | Requests per second: 1050 [10:49:32.765] Context creations: 1088 | Requests per second: 1067 [10:49:33.784] Context creations: 1139 | Requests per second: 1119 [10:49:34.802] Context creations: 1138 | Requests per second: 1117 [10:49:35.831] Context creations: 1153 | Requests per second: 1120 [10:49:36.845] Context creations: 1126 | Requests per second: 1111 [10:49:37.873] Context creations: 1014 | Requests per second: 987 [10:49:38.898] Context creations: 1139 | Requests per second: 1111 [10:49:39.918] Context creations: 1086 | Requests per second: 1065 Total context creations: 10592 Requests per second: 1034 Run With ContextPooling [10:49:40.982] Context creations: 32 | Requests per second: 1388 [10:49:41.991] Context creations: 0 | Requests per second: 1691 [10:49:43.014] Context creations: 0 | Requests per second: 1684 [10:49:44.031] Context creations: 0 | Requests per second: 1702 [10:49:45.049] Context creations: 0 | Requests per second: 1694 [10:49:46.067] Context creations: 0 | Requests per second: 1401 [10:49:47.075] Context creations: 0 | Requests per second: 1510 [10:49:48.107] Context creations: 0 | Requests per second: 1669 [10:49:49.127] Context creations: 0 | Requests per second: 1679 [10:49:50.147] Context creations: 0 | Requests per second: 1688 Total context creations: 32 Requests per second: 1610