Beschleunigung des Entity Framework-Kerns

Sei nicht gierig!


Bei der Auswahl von Daten müssen Sie genau so viele auswählen, wie Sie gleichzeitig benötigen. Rufen Sie niemals alle Daten aus einer Tabelle ab!

Falsch:

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

Korrekt:

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();


Falsch:

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

Korrekt:

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

Eine integrierte Datenüberprüfung kann durchgeführt werden, wenn eine Abfrage einige Datensätze zurückgibt.

Falsch:


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;
}

Korrekt:

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();

Wow, wow, wow, getaktet.

Es ist Zeit, Ihr Wissen über LINQ-Techniken ein wenig aufzufrischen.

Schauen wir uns die Unterschiede zwischen ToList AsEnumerable AsQueryable an


Also 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 .
  • Verwenden Sie AsQueryable, wenn Sie eine Datenbankabfrage wünschen, die verbessert werden kann, bevor sie auf der Serverseite ausgeführt wird.


Ein Beispiel für die Verwendung von AsQueryable im einfachsten Fall:

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;
        }


Die Magie des einfachen Lesens


Wenn Sie keine Daten ändern müssen, verwenden Sie einfach die Methode .AsNoTracking () .

Langsame Probenahme

var blogs = context.Blogs.ToList();

Schnellabruf (schreibgeschützt)

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

Fühlen Sie sich schon etwas aufgewärmt?

Arten des Ladens verwandter Daten


Für diejenigen, die vergessen haben, was faules Laden ist .

Lazy Loading (Lazy Loading) bedeutet, dass die zugehörigen Daten beim Zugriff auf die Navigationseigenschaft transparent aus der Datenbank geladen werden. Lesen Sie hier mehr .

Gleichzeitig möchte ich Sie an andere Arten des Ladens von Daten erinnern.

Aktives Laden (Eifriges Laden) bedeutet, dass die zugehörigen Daten als Teil der ersten Anforderung aus der Datenbank geladen werden.

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();
}

Beachtung! Ab Version EF Core 3.0.0 wird mit jedem Include ein zusätzlicher JOIN zu den von relationalen Anbietern generierten SQL-Abfragen hinzugefügt , während in früheren Versionen zusätzliche SQL-Abfragen generiert wurden. Dies kann die Leistung Ihrer Abfragen erheblich verbessern, zum Guten oder zum Schlechten. Insbesondere LINQ-Abfragen mit einer extrem großen Anzahl von Einschlussanweisungen können in mehrere separate LINQ-Abfragen aufgeteilt werden.

Explizites Laden (Explizites Laden) bedeutet, dass die zugehörigen Daten später explizit aus der Datenbank geladen werden.

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();
}

Ruck und Durchbruch! Weitermachen?

Bereit, noch schneller zu werden?


Um das Abrufen komplex strukturierter und sogar abnormalisierter Daten aus einer relationalen Datenbank erheblich zu beschleunigen, gibt es zwei Möglichkeiten: Verwenden Sie indizierte Ansichten (1) oder, noch besser, vorbereitete (berechnete) Daten in einer einfachen flachen Form für die Anzeige (2).

(1) Indizierte Ansicht im Kontext von MS SQL Server

Die indizierte Ansicht verfügt über einen eindeutigen Clustered-Index. Ein eindeutiger Clustered-Index wird in SQL Server gespeichert und wie jeder andere Clustered-Index aktualisiert. Eine indizierte Ansicht ist wichtiger als Standardansichten, die eine komplexe Verarbeitung einer großen Anzahl von Zeilen umfassen, z. B. das Aggregieren einer großen Datenmenge oder das Kombinieren mehrerer Zeilen.

Wenn in Abfragen häufig auf solche Ansichten verwiesen wird, können wir die Leistung verbessern, indem wir einen eindeutigen Clustered-Index für die Ansicht erstellen. Bei einer Standardansicht wird die Ergebnismenge nicht in der Datenbank gespeichert, sondern die Ergebnismenge wird für jede Abfrage berechnet. Bei einem Clustered-Index wird die Ergebnismenge jedoch wie eine Tabelle mit einem Clustered-Index in der Datenbank gespeichert. Abfragen, die die indizierte Ansicht nicht speziell verwenden, können sogar von der Existenz eines Clustered-Index aus der Ansicht profitieren.

Die Darstellung eines Index hat bestimmte Kosten in Form von Produktivität. Wenn wir eine indizierte Ansicht erstellen, muss SQL Server jedes Mal, wenn wir die Daten in den Basistabellen ändern, nicht nur die Indexdatensätze in diesen Tabellen, sondern auch die Indexdatensätze in der Ansicht unterstützen. In SQL Server-Editionen für Entwickler und Unternehmen kann das Optimierungsprogramm Ansichtsindizes verwenden, um Abfragen zu optimieren, die keine indizierte Ansicht angeben. In anderen Editionen von SQL Server muss die Abfrage jedoch eine indizierte Ansicht enthalten und einen NOEXPAND-Hinweis enthalten, um den Index in der Ansicht nutzen zu können.

(2) Wenn Sie eine Anfrage stellen müssen, bei der mehr als drei Ebenen verwandter Tabellen in Höhe von drei oder mehr mit erhöhtem CRUD angezeigt werden müssenLaden, der beste Weg wäre, die Ergebnismenge regelmäßig zu berechnen, in einer Tabelle zu speichern und für die Anzeige zu verwenden. Die resultierende Tabelle, in der die Daten gespeichert werden, sollte einen Primärschlüssel und Indizes für die Suchfelder in LINQ enthalten .

Was ist mit Asynchronität?


Ja! Wir verwenden es wo immer möglich! Hier ist ein Beispiel:

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();
}

Und ja, haben Sie etwas vergessen, um die Produktivität zu steigern? Buum!

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


Hinweis: Die Do () -Methode wurde nur zu Demonstrationszwecken hinzugefügt, um die Funktionsfähigkeit der GetFederalDistrictsAsync () -Methode anzuzeigen . Wie meine Kollegen richtig bemerkt haben, wird ein weiteres Beispiel für reine Asynchronität benötigt.

Und lassen Sie es mich basierend auf dem Konzept einer Ansichtskomponente in ASP .NET Core geben :

//  
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



Ich möchte Sie daran erinnern, wenn Abfragen im Entity Framework Core ausgeführt werden.

Beim Aufrufen von LINQ-Anweisungen erstellen Sie einfach eine Abfrageansicht im Speicher. Die Anforderung wird erst nach Verarbeitung der Ergebnisse an die Datenbank gesendet.

Im Folgenden sind die häufigsten Vorgänge aufgeführt, die dazu führen, dass eine Anforderung an die Datenbank gesendet wird.

  • Durchlaufen Sie die Ergebnisse in einer for-Schleife.
  • Verwenden eines Operators wie ToList, ToArray, Single, Count.
  • Binden von Daten aus Abfrageergebnissen an die Benutzeroberfläche.

Wie organisiere ich den EF Core-Code in Bezug auf die Anwendungsarchitektur?


(1) Aus Sicht der Architektur der Anwendung müssen Sie sicherstellen, dass der Zugangscode zu Ihrer Datenbank an einer klar definierten Stelle (isoliert) isoliert / getrennt ist. Auf diese Weise können Sie Datenbankcode finden, der sich auf die Leistung auswirkt.

(2) Mischen Sie den Zugangscode für Ihre Datenbank nicht mit anderen Teilen der Anwendung, wie z. B. der Benutzeroberfläche oder der API. Somit kann der Datenbankzugriffscode geändert werden, ohne sich um andere Probleme zu kümmern, die nicht mit der Datenbank zusammenhängen.

Wie speichere ich Daten korrekt und schnell mit SaveChanges ?


Wenn die eingefügten Datensätze identisch sind, ist es sinnvoll, für alle Datensätze einen Speichervorgang zu verwenden.

Falsch

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();
}
}

Korrekt

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();
}
}

Es gibt immer Ausnahmen von der Regel. Wenn der Transaktionskontext komplex ist, dh aus mehreren unabhängigen Vorgängen besteht, können Sie nach jedem Vorgang speichern. Und es ist noch korrekter, asynchronen Speicher in einer Transaktion zu verwenden.

//    
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();
        }
    }
}

Trigger, berechnete Felder, benutzerdefinierte Funktionen und EF Core


Um die Belastung von Anwendungen mit EF Core zu verringern, ist es sinnvoll, einfach berechnete Felder und Datenbankauslöser zu verwenden. Es ist jedoch besser, sich nicht einzumischen, da die Anwendung sehr verwirrend sein kann. Benutzerdefinierte Funktionen können jedoch besonders bei Abrufvorgängen sehr nützlich sein!

Parallelität in EF Core


Wenn Sie alles parallelisieren möchten, um die Geschwindigkeit zu erhöhen, brechen Sie ab: EF Core unterstützt nicht die Ausführung mehrerer paralleler Operationen in einer Instanz des Kontexts. Warten Sie, bis ein Vorgang abgeschlossen ist, bevor Sie mit dem nächsten beginnen. Dazu müssen Sie normalerweise in jeder asynchronen Operation das Schlüsselwort await angeben.

EF Core verwendet asynchrone Abfragen, um zu vermeiden, dass der Fluss beim Ausführen einer Abfrage in der Datenbank blockiert wird. Asynchrone Anforderungen sind wichtig, um eine schnelle Reaktion der Benutzeroberfläche in Thick Clients sicherzustellen. Sie können auch den Durchsatz in einer Webanwendung erhöhen, in der Sie den Thread für andere Anforderungen freigeben können. Hier ist ein Beispiel:

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

Was wissen Sie über LINQ-kompilierte Abfragen?


Wenn Sie eine Anwendung haben, die wiederholt strukturell ähnliche Abfragen im Entity Framework ausführt, können Sie die Leistung häufig verbessern, indem Sie die Abfrage einmal kompilieren und mehrmals mit unterschiedlichen Parametern ausführen. Beispielsweise muss eine Anwendung möglicherweise alle Kunden in einer bestimmten Stadt erreichen. Die Stadt wird zur Laufzeit vom Benutzer im Formular angegeben. LINQ to Entities unterstützt zu diesem Zweck die Verwendung kompilierter Abfragen.

Ab .NET Framework 4.5 werden LINQ-Abfragen automatisch zwischengespeichert. Sie können jedoch weiterhin kompilierte LINQ-Abfragen verwenden, um diese Kosten in nachfolgenden Läufen zu senken, und kompilierte Abfragen können effizienter sein als LINQ-Abfragen, die automatisch zwischengespeichert werden. Beachten Sie, dass LINQ to Entities-Abfragen, die den Operator Enumerable.Contains auf speicherinterne Sammlungen anwenden, nicht automatisch zwischengespeichert werden. Die Parametrisierung speicherinterner Sammlungen in kompilierten LINQ-Abfragen ist ebenfalls nicht zulässig.

Viele Beispiele finden Sie hier .

Machen Sie keine großen DbContext-Kontexte!


Im Allgemeinen kenne ich viele von Ihnen, wenn nicht fast alle, von faulen f_u__c_k__e_r__s und der gesamten Datenbank, die Sie in einen Kontext stellen. Dies gilt insbesondere für den Database-First-Ansatz. Und vergebens machst du es! Das Folgende ist ein Beispiel dafür, wie der Kontext unterteilt werden kann. Natürlich müssen Verbindungstabellen zwischen Kontexten dupliziert werden, dies ist ein Minus. Auf die eine oder andere Weise ist es besser, über eine Aufteilung nachzudenken, wenn Sie mehr als 50 Tabellen im Kontext haben.

Verwenden der Kontextgruppierung (Pooling von DdContext)


Die Bedeutung des DbContext- Pools besteht darin, die Wiederverwendung von DbContext- Instanzen aus dem Pool zu ermöglichen, was in einigen Fällen zu einer besseren Leistung führen kann, als jedes Mal eine neue Instanz zu erstellen. Dies ist auch der Hauptgrund für das Erstellen eines Verbindungspools in ADO.NET, obwohl Leistungssteigerungen für Verbindungen bedeutender sind, da Verbindungen im Allgemeinen eine schwierigere Ressource sind.

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();
        }

Wie vermeide ich unnötige Fehler mit CRUD in EF Core?


Fügen Sie niemals Berechnungen in denselben Code ein. Trennen Sie immer die Bildung / Vorbereitung eines Objekts und dessen Einfügen / Aktualisieren. Verteilen Sie es einfach nach Funktionen: Überprüfen der vom Benutzer eingegebenen Daten, Berechnen der erforderlichen vorläufigen Daten, Zuordnen oder Erstellen eines Objekts und der tatsächlichen CRUD-Operation.

Was tun, wenn die Anwendungsleistung wirklich schlecht ist?


Bier wird hier definitiv nicht helfen. Was jedoch helfen wird, ist die Trennung von Lesen und Schreiben in der Anwendungsarchitektur, gefolgt von der Zuordnung dieser Operationen zu Sockets. Denken Sie an die Verwendung des CQRS-Musters (Command and Query Responsibility Segregation) und versuchen Sie auch, Tabellen in Einfügungen aufzuteilen und zwischen zwei Datenbanken zu lesen.

Beschleunigen Sie Anwendungen für Sie, Freunde und Kollegen!

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


All Articles