تسريع إطار كيان الإطار

لا تكن جشعا!


عند اختيار البيانات ، تحتاج إلى اختيار العدد الذي تريده بالضبط في كل مرة. لا تقم باسترجاع جميع البيانات من الجدول!

خطأ:

using var ctx = new EFCoreTestContext(optionsBuilder.Options);                
//    ID  ,       !
ctx.FederalDistricts.Select(x=> new { x.ID, x.Name, x.ShortName }).ToList();

بشكل صحيح:

using var ctx = new EFCoreTestContext(optionsBuilder.Options);  
//     ID     !
ctx.FederalDistricts.Select(x=> new { x.Name, x.ShortName }).ToList();
ctx.FederalDistricts.Select(x => new MyClass { Name = x.Name, ShortName = x.ShortName }).ToList();


خطأ:

var blogs = context.Blog.ToList(); //       . ?
//     ?
var somePost = blogs.FirstOrDefault(x=>x.Title.StartWidth(“Hello world!”));

بشكل صحيح:

var somePost = context.Blog.FirstOrDefault(x=>x.Title.StartWidth(“Hello world!”));

يمكن إجراء التحقق من صحة البيانات المتكاملة عندما يُرجع الاستعلام بعض السجلات.

خطأ:


var blogs = context.Blogs.Where(blog => StandardizeUrl(blog.Url).Contains("dotnet")).ToList();

public static string StandardizeUrl(string url)
{
    url = url.ToLower();
    if (!url.StartsWith("http://"))
    {
        url = string.Concat("http://", url);
    }
    return url;
}

بشكل صحيح:

var blogs = context.Blogs.AsEnumerable().Where(blog => StandardizeUrl(blog.Url).Contains("dotnet")).ToList();
 
//  
var blogs = context.Blogs.Where(blog => blog.Contains("dotnet"))
    .OrderByDescending(blog => blog.Rating)
    .Select(blog => new
    {
        Id = blog.BlogId,
        Url = StandardizeUrl(blog.Url)
    })
    .ToList();

نجاح باهر ، نجاح باهر ، نجاح باهر ، سجل أعلى.

حان الوقت لتحديث معرفتك بتقنيات LINQ قليلاً.

دعونا نلقي نظرة على الاختلافات بين ToList AsEnumerable AsQueryable


حتى ToList

  • .
  • .ToList() (lazy loading), .

AsEnumerable

  • (lazy loading)
  • : Func <TSource, bool>
  • ( Where/Take/Skip , , select * from Table1,
  • , N )
  • : Linq-to-SQL + Linq-to-Object.
  • IEnumerable (lazy loading).

AsQueryable

  • (lazy loading)
  • :
    AsQueryable(IEnumerable)  AsQueryable<TElement>(IEnumerable<TElement>) 

  • Expression T-SQL ( ), .
  • DbSet ( Entity Framework) AsQueryable .
  • , Take(5) «select top 5 * SQL» . , SQL , . AsQueryable() , AsEnumerable() T-SQL Linq .
  • استخدم AsQueryable إذا كنت تريد استعلام قاعدة بيانات يمكن تحسينه قبل التشغيل على جانب الخادم.


مثال على استخدام AsQueryable في أبسط الحالات:

public IEnumerable<EmailView> GetEmails(out int totalRecords, Guid? deviceWorkGroupID,
                DateTime? timeStart, DateTime? timeEnd, string search, int? confirmStateID, int? stateTypeID, int? limitOffset, int? limitRowCount, string orderBy, bool desc)
        {
            var r = new List<EmailView>();

            using (var db = new GJobEntities())
            {
                var query = db.Emails.AsQueryable();

                if (timeStart != null && timeEnd != null)
                {
                    query = query.Where(p => p.Created >= timeStart && p.Created <= timeEnd);
                }

                if (stateTypeID != null && stateTypeID > -1)
                {
                    query = query.Where(p => p.EmailStates.OrderByDescending(x => x.AtTime).FirstOrDefault().EmailStateTypeID == stateTypeID);
                }


                if (confirmStateID != null && confirmStateID > -1)
                {
                    var boolValue = confirmStateID == 1 ? true : false;
                    query = query.Where(p => p.IsConfirmed == boolValue);
                }

                if (!string.IsNullOrEmpty(search))
                {
                    search = search.ToLower();
                    query = query.Where(p => (p.Subject + " " + p.CopiesEmails + " " + p.ToEmails + " " + p.FromEmail + " " + p.Body)
                                        .ToLower().Contains(search));
                }

                if (deviceWorkGroupID != Guid.Empty)
                {
                    query = query.Where(x => x.SCEmails.FirstOrDefault().SupportCall.Device.DeviceWorkGroupDevices.FirstOrDefault(p => p.DeviceWorkGroupID == deviceWorkGroupID) != null);
                }

                totalRecords = query.Count();
                query = query.OrderByDescending(p => p.Created);
                if (limitOffset.HasValue)
                {
                    query = query.Skip(limitOffset.Value).Take(limitRowCount.Value);
                }
                var items = query.ToList(); //    

                foreach (var item in items)
                {
                    var n = new EmailView
                    {
                        ID = item.ID,
                        SentTime = item.SentTime,
                        IsConfirmed = item.IsConfirmed,
                        Number = item.Number,
                        Subject = item.Subject,
                        IsDeleted = item.IsDeleted,
                        ToEmails = item.ToEmails,
                        Created = item.Created,
                        CopiesEmails = item.CopiesEmails,
                        FromEmail = item.FromEmail,
                    };

                    //     - 

                    r.Add(n);
                }
            }

            return r;
        }


سحر القراءة البسيطة


إذا لم تكن بحاجة إلى تغيير البيانات ، فما عليك سوى العرض باستخدام طريقة ASAsoTracking () .

أخذ عينات بطيئة

var blogs = context.Blogs.ToList();

الجلب السريع (للقراءة فقط)

var blogs = context.Blogs.AsNoTracking().ToList();

هل تشعر بأنك قد تحسنت بالفعل قليلاً؟

أنواع تحميل البيانات ذات الصلة


بالنسبة لأولئك الذين نسوا ما هو كسول التحميل .

التحميل البطيء ( التحميل البطيء) يعني أن البيانات المرتبطة تم تحميلها بشفافية من قاعدة البيانات عند الوصول إلى خاصية التنقل. اقرأ المزيد هنا .

وفي الوقت نفسه ، دعني أذكرك بأنواع أخرى من تحميل البيانات ذات الصلة.

الحمل النشط (Eager loading) يعني أنه يتم تحميل البيانات المرتبطة من قاعدة البيانات كجزء من الطلب الأولي.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
            .ThenInclude(post => post.Author)
                .ThenInclude(author => author.Photo)
        .Include(blog => blog.Owner)
            .ThenInclude(owner => owner.Photo)
        .ToList();
}

انتباه! بدءًا من الإصدار EF Core 3.0.0 ، سيؤدي كل تضمين إلى إضافة JOIN إضافي إلى استعلامات SQL التي تم إنشاؤها بواسطة موفري العلاقات ، بينما أنتجت الإصدارات السابقة استعلامات SQL إضافية. هذا يمكن أن يغير بشكل كبير أداء استفساراتك ، للأفضل أو للأسوأ. على وجه الخصوص ، يمكن تقسيم استعلامات LINQ التي تحتوي على عدد كبير جدًا من عبارات التضمين إلى عدة استعلامات LINQ منفصلة.

التحميل الصريح ( التحميل الصريح) يعني أن البيانات المرتبطة تم تحميلها صراحة من قاعدة البيانات لاحقًا.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);

    var goodPosts = context.Entry(blog)
        .Collection(b => b.Posts)
        .Query()
        .Where(p => p.Rating > 3)
        .ToList();
}

رعشة واختراق! الانتقال؟

هل أنت مستعد لتسريع المزيد؟


لتسريع بشكل كبير عند جلب بيانات منظمة بشكل معقد وحتى غير طبيعية من قاعدة بيانات علائقية ، هناك طريقتان للقيام بذلك: استخدام طرق العرض المفهرسة (1) أو ، بشكل أفضل ، البيانات المعدة مسبقًا (المحسوبة) في نموذج مسطح بسيط للعرض (2).

(1) عرض مفهرس في سياق MS SQL Server

تحتوي طريقة العرض المفهرسة على فهرس مجمع فريد. يتم تخزين فهرس مجمع فريد في SQL Server ويتم تحديثه مثل أي فهرس مجمع آخر. تعتبر طريقة العرض المفهرسة أكثر أهمية من طرق العرض القياسية ، والتي تتضمن معالجة معقدة لعدد كبير من الصفوف ، على سبيل المثال ، تجميع كمية كبيرة من البيانات أو دمج صفوف متعددة.

إذا تمت الإشارة إلى طرق العرض هذه غالبًا في الاستعلامات ، فيمكننا تحسين الأداء من خلال إنشاء فهرس مجمع فريد للعرض. بالنسبة للعرض القياسي ، لا يتم تخزين مجموعة النتائج في قاعدة البيانات ؛ بدلاً من ذلك ، يتم حساب مجموعة النتائج لكل استعلام ، ولكن في حالة الفهرس المجمع ، يتم تخزين مجموعة النتائج في قاعدة البيانات بنفس طريقة الجدول مع فهرس مجمع. قد تستفيد الاستعلامات التي لا تستخدم العرض المفهرس على وجه التحديد من وجود فهرس مجمع من العرض.

إن تمثيل المؤشر له تكلفة معينة في شكل إنتاجية. إذا أنشأنا طريقة عرض مفهرسة ، في كل مرة نقوم فيها بتغيير البيانات في الجداول الأساسية ، يجب أن يدعم SQL Server ليس فقط سجلات الفهرس في هذه الجداول ، ولكن أيضًا سجلات الفهرس في طريقة العرض. في إصدارات SQL Server للمطورين والشركات ، يمكن للمحسن استخدام فهارس العرض لتحسين الاستعلامات التي لا تحدد طريقة عرض مفهرسة. ومع ذلك ، في إصدارات أخرى من SQL Server ، يجب أن يتضمن الاستعلام طريقة عرض مفهرسة وتقديم تلميح NOEXPAND للاستفادة من الفهرس في طريقة العرض.

(2) إذا كنت بحاجة إلى تقديم طلب يتطلب عرض أكثر من ثلاثة مستويات من الجداول ذات الصلة بمبلغ ثلاثة أو أكثر مع زيادة CRUDأفضل طريقة هي حساب مجموعة النتائج بشكل دوري وحفظها في جدول واستخدامها للعرض. يجب أن يحتوي الجدول الناتج الذي سيتم تخزين البيانات فيه على مفتاح أساسي وفهارس في حقول البحث في LINQ .

ماذا عن التزامن؟


نعم! نستخدمه حيثما أمكن! هنا مثال:

public void Do()
{
    var myTask = GetFederalDistrictsAsync ();
    foreach (var item in myTask.Result)
    {
         // 
    }
}

public async Task<List<FederalDistrict>> GetFederalDistrictsAsync()
{
    var conn = configurationRoot.GetConnectionString("EFCoreTestContext");
    optionsBuilder.UseSqlServer(conn);
    using var context = new EFCoreTestContext(optionsBuilder.Options);
    return await context.FederalDistricts.ToListAsync();
}

ونعم ، هل نسيت أي شيء لزيادة الإنتاجية؟ رائع!

return await context.FederalDistricts.<b>AsNoTracking()</b>.ToListAsync();


ملاحظة: تمت إضافة طريقة Do () لأغراض العرض التوضيحي فقط ، للإشارة إلى قابلية تشغيل طريقة GetFederalDistrictedAsync () . كما لاحظ زملائي بشكل صحيح ، هناك حاجة إلى مثال آخر على التزامن الخالص.

واسمحوا لي أن أعطيها بناء على مفهوم مكون العرض في ASP .NET Core :

//  
public class PopularPosts : ViewComponent
    {
        private readonly IStatsRepository _statsRepository;

        public PopularPosts(IStatsRepository statsRepository)
        {
            _statsRepository = statsRepository;
        }

        public async Task<IViewComponentResult> InvokeAsync()
        {
           //         -
            var federalDistricts = await _statsRepository.GetFederalDistrictsAsync(); 
            var model = new TablePageModel()
            {
                FederalDistricts = federalDistricts,
            };

            return View(model);
        }
    }
    // 
    
    /// <summary>
    ///  -   .... -
    /// </summary>
    public interface IStatsRepository
    {
        /// <summary>
        ///        
        /// </summary>
        /// <returns></returns>
        IEnumerable<FederalDistrict> FederalDistricts();

        /// <summary>
        ///        
	/// !!!
        /// </summary>
        /// <returns></returns>
        Task<List<FederalDistrict>> GetFederalDistrictsAsync();
    }	
	
    /// <summary>
    /// -   .... -
    /// </summary>
    public class StatsRepository : IStatsRepository
    {
        private readonly DbContextOptionsBuilder<EFCoreTestContext>
            optionsBuilder = new DbContextOptionsBuilder<EFCoreTestContext>();
        private readonly IConfigurationRoot configurationRoot;

        public StatsRepository()
        {
            IConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
                    .SetBasePath(Environment.CurrentDirectory)
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
            configurationRoot = configurationBuilder.Build();
        }

        public async Task<List<FederalDistrict>> GetFederalDistrictsAsync()
        {
            var conn = configurationRoot.GetConnectionString("EFCoreTestContext");
            optionsBuilder.UseSqlServer(conn);
            using var context = new EFCoreTestContext(optionsBuilder.Options);
            return await context.FederalDistricts.Include(x => x.FederalSubjects).ToListAsync();
        }

        public IEnumerable<FederalDistrict> FederalDistricts()
        {
            var conn = configurationRoot.GetConnectionString("EFCoreTestContext");
            optionsBuilder.UseSqlServer(conn);

            using var ctx = new EFCoreTestContext(optionsBuilder.Options);
            return ctx.FederalDistricts.Include(x => x.FederalSubjects).ToList();
        }
    }

   //         Home\Index 
    <div id="tableContainer">
            @await Component.InvokeAsync("PopularPosts")
     </div>
  //   HTML      Shared\Components\PopularPosts\Default.cshtml



دعني أذكرك عندما يتم تنفيذ الاستعلامات في Entity Framework Core.

عند استدعاء عبارات LINQ ، يمكنك ببساطة إنشاء طريقة عرض استعلام في الذاكرة. يتم إرسال الطلب إلى قاعدة البيانات فقط بعد معالجة النتائج.

فيما يلي العمليات الأكثر شيوعًا التي تؤدي إلى إرسال طلب إلى قاعدة البيانات.

  • كرر النتائج في حلقة for.
  • استخدام عامل تشغيل ، مثل ToList و ToArray و Single و Count.
  • ربط البيانات من نتائج الاستعلام إلى واجهة المستخدم.

كيفية تنظيم كود EF Core من حيث بنية التطبيق؟


(1) من وجهة نظر بنية التطبيق ، تحتاج إلى التأكد من عزل / فصل رمز الوصول إلى قاعدة البيانات الخاصة بك في مكان محدد بوضوح (في عزلة). يتيح لك هذا العثور على رمز قاعدة البيانات التي تؤثر على الأداء.

(2) لا تخلط رمز الوصول لقاعدة البيانات الخاصة بك مع أجزاء أخرى من التطبيق ، مثل واجهة المستخدم أو API. وبالتالي ، يمكن تغيير رمز الوصول إلى قاعدة البيانات دون القلق بشأن قضايا أخرى لا تتعلق بقاعدة البيانات.

كيفية حفظ البيانات بشكل صحيح وسريع باستخدام SaveChanges ؟


إذا كانت السجلات المدرجة هي نفسها ، فمن المنطقي استخدام عملية حفظ واحدة لجميع السجلات.

خطأ

using(var db = new NorthwindEntities())
{
var transaction = db.Database.BeginTransaction();

try
{
    //   1
    var  obj1 = new Customer();
    obj1.CustomerID = "ABCDE";
    obj1.CompanyName = "Company 1";
    obj1.Country = "USA";
    db.Customers.Add(obj1);

  //          db.SaveChanges();

    //   2
    var  obj2 = new Customer();
    obj2.CustomerID = "PQRST";
    obj2.CompanyName = "Company 2";    
    obj2.Country = "USA";
    db.Customers.Add(obj2);

    //   
    db.SaveChanges();

    transaction.Commit();
}
catch
{
    transaction.Rollback();
}
}

بشكل صحيح

using(var db = new NorthwindEntities())
{
var transaction = db.Database.BeginTransaction();

try
{
   //  1
    var  obj1 = new Customer();
    obj1.CustomerID = "ABCDE";
    obj1.CompanyName = "Company 1";
    obj1.Country = "USA";
    db.Customers.Add(obj1); 

    //   2
    var  obj2 = new Customer();
    obj2.CustomerID = "PQRST";
    obj2.CompanyName = "Company 2";    
    obj2.Country = "USA";
    db.Customers.Add(obj2);

   //    N 
    db.SaveChanges();

    transaction.Commit();
}
catch
{
    transaction.Rollback();
}
}

دائما هنا استثناءات لهذه القاعدة. إذا كان سياق المعاملة معقدًا ، أي يتكون من عدة عمليات مستقلة ، يمكنك حفظ بعد كل عملية. ومن الأصح استخدام التخزين غير المتزامن في المعاملة.

//    
public async Task<IActionResult> AddDepositToHousehold(int householdId, DepositRequestModel model)
{
    using (var transaction = await Context.Database.BeginTransactionAsync(IsolationLevel.Snapshot))
    {
        try
        {
            //     
            var deposit = this.Mapper.Map<Deposit>(model);
            await this.Context.Deposits.AddAsync(deposit);

            await this.Context.SaveChangesAsync();

            //    
               var debtsToPay = await this.Context.Debts.Where(d => d.HouseholdId == householdId && !d.IsPaid).OrderBy(d => d.DateMade).ToListAsync();

            debtsToPay.ForEach(d => d.IsPaid = true);

            await this.Context.SaveChangesAsync();

            //   
            var household = this.Context.Households.FirstOrDefaultAsync(h => h.Id == householdId);

            household.Balance += model.DepositAmount;

            await this.Context.SaveChangesAsync();

            transaction.Commit();
            return this.Ok();
        }
        catch
        {
            transaction.Rollback();
            return this.BadRequest();
        }
    }
}

المشغلات والحقول المحسوبة والوظائف المخصصة و EF Core


لتقليل الحمل على التطبيقات التي تحتوي على EF Core ، من المنطقي استخدام الحقول المحسوبة البسيطة ومشغلات قاعدة البيانات ، ولكن من الأفضل عدم المشاركة ، حيث يمكن أن يكون التطبيق مربكًا للغاية. ولكن يمكن أن تكون الوظائف التي يحددها المستخدم مفيدة للغاية خاصةً أثناء عمليات الجلب!

التزامن في EF Core


إذا كنت ترغب في موازنة كل شيء لتسريع ، ثم توقف: EF Core لا يدعم تنفيذ العديد من العمليات المتوازية في حالة واحدة من السياق. انتظر حتى تكتمل عملية واحدة قبل البدء في العملية التالية. للقيام بذلك ، تحتاج عادةً إلى تحديد الكلمة الرئيسية المنتظرة في كل عملية غير متزامنة.

تستخدم EF Core الاستعلامات غير المتزامنة لتجنب حظر التدفق عند تنفيذ استعلام في قاعدة البيانات. تعتبر الطلبات غير المتزامنة مهمة لضمان استجابة سريعة لواجهة المستخدم في العملاء الكثيرين. يمكنهم أيضًا زيادة الإنتاجية في تطبيق الويب ، حيث يمكنك تحرير سلسلة الرسائل للتعامل مع الطلبات الأخرى. هنا مثال:

public async Task<List<Blog>> GetBlogsAsync()
{
    using (var context = new BloggingContext())
    {
        return await context.Blogs.ToListAsync();
    }
}

ماذا تعرف عن استعلامات LINQ المترجمة؟


إذا كان لديك تطبيق يقوم بشكل متكرر بإجراء استعلامات متشابهة هيكليًا في Entity Framework ، يمكنك غالبًا تحسين الأداء من خلال تجميع الاستعلام مرة واحدة وتنفيذه عدة مرات بمعلمات مختلفة. على سبيل المثال ، قد يحتاج التطبيق إلى الحصول على جميع العملاء في مدينة معينة ؛ يشار إلى المدينة في وقت التشغيل من قبل المستخدم في النموذج. تدعم LINQ إلى الكيانات استخدام الاستعلامات المترجمة لهذا الغرض.

بدءًا من .NET Framework 4.5 ، يتم تخزين استعلامات LINQ مؤقتًا تلقائيًا. ومع ذلك ، لا يزال بإمكانك استخدام استعلامات LINQ المترجمة لتقليل هذه التكلفة في عمليات التشغيل اللاحقة ، ويمكن أن تكون الاستعلامات المترجمة أكثر كفاءة من استعلامات LINQ التي يتم تخزينها مؤقتًا تلقائيًا. لاحظ أن استعلامات LINQ إلى الكيانات التي تطبق عامل التعداد Enumerable.Contains على مجموعات في الذاكرة لا يتم تخزينها مؤقتًا تلقائيًا. أيضًا ، لا يُسمح بتعيين المعلمات للمجموعات الموجودة في الذاكرة في استعلامات LINQ المترجمة.

يمكن العثور على العديد من الأمثلة هنا .

لا تقم بعمل سياقات DbContext كبيرة!


بشكل عام ، أعرف الكثير منكم ، إن لم يكن جميعهم تقريبًا ، من f_u__c_k__e_r__s البطيئة وقاعدة البيانات بأكملها ، تضعها في سياق واحد ، وهذا ينطبق بشكل خاص على نهج قاعدة البيانات أولاً. وعبثا تفعل ذلك! فيما يلي مثال على كيفية تقسيم السياق. بالطبع ، يجب تكرار جداول الاتصال بين السياقات ، وهذا هو ناقص. بطريقة أو بأخرى ، إذا كان لديك أكثر من 50 جدولًا في السياق ، فمن الأفضل التفكير في تقسيمها.

استخدام تجميع السياق (تجميع DdContext)


معنى تجمع DbContext هو السماح بإعادة استخدام مثيلات DbContext من التجمع ، والتي يمكن أن تؤدي في بعض الحالات إلى أداء أفضل من إنشاء مثيل جديد في كل مرة. هذا أيضًا هو السبب الرئيسي لإنشاء تجمع اتصال في ADO.NET ، على الرغم من أن مكاسب الأداء للاتصالات ستكون أكثر أهمية نظرًا لأن الاتصالات هي مورد أصعب بشكل عام.

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace Demos
{
    public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
    }

    public class BloggingContext : DbContext
    {
        public static long InstanceCount;

        public BloggingContext(DbContextOptions options)
            : base(options)
            => Interlocked.Increment(ref InstanceCount);

        public DbSet<Blog> Blogs { get; set; }
    }

    public class BlogController
    {
        private readonly BloggingContext _context;

        public BlogController(BloggingContext context) => _context = context;

        public async Task ActionAsync() => await _context.Blogs.FirstAsync();
    }

    public class Startup
    {
        private const string ConnectionString
            = @"Server=(localdb)\mssqllocaldb;Database=Demo.ContextPooling;Integrated Security=True;ConnectRetryCount=0";

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<BloggingContext>(c => c.UseSqlServer(ConnectionString));
        }
    }

    public class Program
    {
        private const int Threads = 32;
        private const int Seconds = 10;

        private static long _requestsProcessed;

        private static async Task Main()
        {
            var serviceCollection = new ServiceCollection();
            new Startup().ConfigureServices(serviceCollection);
            var serviceProvider = serviceCollection.BuildServiceProvider();

            SetupDatabase(serviceProvider);

            var stopwatch = new Stopwatch();

            MonitorResults(TimeSpan.FromSeconds(Seconds), stopwatch);

            await Task.WhenAll(
                Enumerable
                    .Range(0, Threads)
                    .Select(_ => SimulateRequestsAsync(serviceProvider, stopwatch)));
        }

        private static void SetupDatabase(IServiceProvider serviceProvider)
        {
            using (var serviceScope = serviceProvider.CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetService<BloggingContext>();

                if (context.Database.EnsureCreated())
                {
                    context.Blogs.Add(new Blog { Name = "The Dog Blog", Url = "http://sample.com/dogs" });
                    context.Blogs.Add(new Blog { Name = "The Cat Blog", Url = "http://sample.com/cats" });
                    context.SaveChanges();
                }
            }
        }
        private static async Task SimulateRequestsAsync(IServiceProvider serviceProvider, Stopwatch stopwatch)
        {
            while (stopwatch.IsRunning)
            {
                using (var serviceScope = serviceProvider.CreateScope())
                {
                    await new BlogController(serviceScope.ServiceProvider.GetService<BloggingContext>()).ActionAsync();
                }

                Interlocked.Increment(ref _requestsProcessed);
            }
        }

        private static async void MonitorResults(TimeSpan duration, Stopwatch stopwatch)
        {
            var lastInstanceCount = 0L;
            var lastRequestCount = 0L;
            var lastElapsed = TimeSpan.Zero;

            stopwatch.Start();

            while (stopwatch.Elapsed < duration)
            {
                await Task.Delay(TimeSpan.FromSeconds(1));

                var instanceCount = BloggingContext.InstanceCount;
                var requestCount = _requestsProcessed;
                var elapsed = stopwatch.Elapsed;
                var currentElapsed = elapsed - lastElapsed;
                var currentRequests = requestCount - lastRequestCount;

                Console.WriteLine(
                    $"[{DateTime.Now:HH:mm:ss.fff}] "
                    + $"Context creations/second: {instanceCount - lastInstanceCount} | "
                    + $"Requests/second: {Math.Round(currentRequests / currentElapsed.TotalSeconds)}");

                lastInstanceCount = instanceCount;
                lastRequestCount = requestCount;
                lastElapsed = elapsed;
            }

            Console.WriteLine();
            Console.WriteLine($"Total context creations: {BloggingContext.InstanceCount}");
            Console.WriteLine(
                $"Requests per second:     {Math.Round(_requestsProcessed / stopwatch.Elapsed.TotalSeconds)}");

            stopwatch.Stop();
        }

كيفية تجنب الأخطاء غير الضرورية مع CRUD في EF Core؟


لا تقم أبدًا بإدخال الحسابات في نفس الرمز. افصل دائمًا تكوين / تحضير الكائن وإدراجه / تحديثه. فقط قم بنشرها عن طريق الوظيفة: فحص البيانات التي أدخلها المستخدم ، وحساب البيانات الأولية اللازمة ، وتعيين أو إنشاء كائن ، وعملية CRUD الفعلية.

ماذا تفعل عندما تكون الأشياء سيئة حقًا مع أداء التطبيق؟


بالتأكيد لن تساعد البيرة هنا. ولكن ما سيساعد هو فصل القراءة والكتابة في بنية التطبيق ، متبوعًا بتخصيص هذه العمليات على المقابس. فكر في استخدام نمط الفصل بين مسؤولية الأوامر والاستعلام (CQRS) ، وحاول أيضًا تقسيم الجداول إلى إدراج وقراءة بين قاعدتي بيانات.

تطبيقات السرعة لك والأصدقاء والزملاء!

Source: https://habr.com/ru/post/undefined/


All Articles