Загрузка экземпляра объекта занимает более 1 секунды - PullRequest
1 голос
/ 09 июля 2019

Я столкнулся с одной интересной вещью в EF.Если мы получим дочернюю сущность, используя базовую сущность, загрузка сущностей займет больше времени.Моя модель выглядит так:

public abstract class BaseDocument
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

public abstract class ComplexDocument : BaseDocument
{
    public string AuthorName { get; set; }
}

public abstract class SimpleDocument : BaseDocument
{
    public int Level { get; set; }
}

public abstract class OfficeDocument : ComplexDocument
{
    public string OfficeName { get; set; }
}

public abstract class ClassDocument : SimpleDocument
{
    public string HeadName { get; set; }
}

public class WordDocument : OfficeDocument
{
    public int PagesCount { get; set; }
}

public class ExcelDocument : OfficeDocument
{
    public int SheetsCount { get; set; }
}

public class TextDocument : ClassDocument
{
    public int LinesCount { get; set; }
}

Я использую подход TPT .Вот дерево наследования inheritance tree Вот мой класс контекста:

public class Context : DbContext
{
    public Context() : base(@"Server=(localdb)\MSSQLLocalDB;Database=EFSIX;Trusted_Connection=True;")
    {
        Database.CreateIfNotExists();
    }
    public DbSet<BaseDocument> BaseDocuments { get; set; }
    public DbSet<ComplexDocument> ComplexDocuments { get; set; }
    public DbSet<SimpleDocument> SimpleDocuments { get; set; }
    public DbSet<OfficeDocument> OfficeDocuments { get; set; }
    public DbSet<ClassDocument> ClassDocuments { get; set; }
    public DbSet<ExcelDocument> ExcelDocuments { get; set; }
    public DbSet<WordDocument> WordDocuments { get; set; }
    public DbSet<TextDocument> TextDocuments { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
       modelBuilder.Entity<BaseDocument>().ToTable("BaseDocuments");
       modelBuilder.Entity<ComplexDocument>().ToTable("ComplexDocuments");
       modelBuilder.Entity<SimpleDocument>().ToTable("SimpleDocuments");
       modelBuilder.Entity<OfficeDocument>().ToTable("OfficeDocuments");
       modelBuilder.Entity<ExcelDocument>().ToTable("ExcelDocuments");
       modelBuilder.Entity<WordDocument>().ToTable("WordDocuments");
       modelBuilder.Entity<ClassDocument>().ToTable("ClassDocuments");
       modelBuilder.Entity<TextDocument>().ToTable("TextDocuments");
    }
    public IQueryable<T> GetEntities<T>() where T : class
    {
        return Set<T>();
    }
}

Я создаю некоторые данные:

static void CreateTestData()
    {
        using (Context context = new Context())
        {
            for (int i = 0; i < 20; i++)
            {
                ExcelDocument excel = new ExcelDocument()
                {
                    Id = Guid.NewGuid(),
                    AuthorName = $"ExcelAuthor{i}",
                    Name = $"Excel{i}",
                    OfficeName = $"ExcelOffice{i}",
                    SheetsCount = (i + 1) * 10
                };
                context.ExcelDocuments.Add(excel);

                WordDocument word = new WordDocument()
                {
                    Id = Guid.NewGuid(),
                    AuthorName = $"WordAuthor{i}",
                    Name = $"Word{i}",
                    OfficeName = $"WordOffice{i}",
                    PagesCount = (i + 2) * 10
                };
                context.WordDocuments.Add(word);

                TextDocument text = new TextDocument()
                {
                    Id = Guid.NewGuid(),
                    Name = $"Text{i}",
                    LinesCount = (i + 3) * 10,
                    HeadName = $"Head{i}",
                    Level = i + 5
                };
                context.TextDocuments.Add(text);
            }
            context.SaveChanges();
        }
    }

Я сделал несколько двух методовдля получения WordDocument из БД.Один из них использует BaseDocument, а другой - WordDocument.Оба возвращают 20 экземпляров WordDocument:

 static long ReadBaseDoc()
    {
        using (Context context = new Context())
        {
            var words= context.GetEntities<BaseDocument>().Where(e => e.Name.StartsWith("Word"));
            Stopwatch stopwatch = Stopwatch.StartNew();
            var instacnes = excel.ToList();
            stopwatch.Stop();
            return stopwatch.ElapsedMilliseconds;
        }
    }
    static long ReadWordDoc()
    {
        using (Context context = new Context())
        {
            var words = context.GetEntities<WordDocument>().Where(e => e.Name.StartsWith("Word"));
            Stopwatch stopwatch = Stopwatch.StartNew();
            var instacnes = words.ToList();
            stopwatch.Stop();
            return stopwatch.ElapsedMilliseconds;
        }
    }

Я тестировал метод моли отдельно, несколько раз, в среднем метод ReadWordDoc занимает 25 мс, а метод ReadBaseDoc - 52 мс (экземпляры одинаковы).Сейчас это не слишком большая проблема, но когда у нас сложное наследование, это занимает более 1 секунды.Я создал 10 классов и унаследовал от BaseDocument.После этого я выполнил ReadBaseDoc и ReadWordDoc методы.ReadWordDoc заняло 25 мс, а ReadBaseDoc заняло 1023 мс.Экземпляры одинаковы, почему ReadBaseDoc занимает больше времени?Как лучше всего избежать подобных проблем в EF?

1 Ответ

0 голосов
/ 10 июля 2019

Взгляните здесь .Есть способы сделать EF быстрее, но в этих сложных сценариях ORM просто создает больше проблем, чем решает.

Один из способов в вашем случае - попытаться изменить наследование на TablePerType, МОЖЕТ, что это будет немногонемного быстрее.

Другой способ - найти медленный запрос и использовать для них Dapper - это будет намного быстрее.

Последний способ - создать репозиторий с оперативным кешем, который загружаетполная база данных в память и поддерживает ее в актуальном состоянии - это должно быть одно приложение в приложении.Если у вас несколько приложений, использующих одну и ту же базу данных, вам необходимо подключить триггеры изменения данных.

В целом, я бы сказал, что для медленных (и относительно простых) запросов, подобных вашему, используйте Dapper + AutoMapper.Держите EF так, чтобы ваша база данных оставалась синхронизированной с вашими классами, но не полагайтесь на нее для запросов.

Если вы действительно хотите придерживаться ORM, я думаю, вам нужно переключить nHibernate.Сам не пробуй, но из того, что я прочитал, он превосходит почти все возможные варианты, включая производительность и время запуска.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...