Количество результатов в DbSet <TEntity> - PullRequest
0 голосов
/ 30 апреля 2019

Я просматривал пример кода о DBEntities и DbContext.Есть ли ограничение на количество строк, которые DbSet извлекает из базы данных?В следующем примере кода, скажем, есть DbSet<History> history или DbSet<Logs> logs, когда создается dbcontext, dbcontext.logs или dbcontext.history будут ли все журналы присутствовать в базе данных?Если это так, то что, если таблицы имеют миллионы строк.Не влияет ли это на производительность, когда во время linq или каких-либо обновлений и сохранения контекста?

public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Standard> Standards { get; set; }
public virtual DbSet<Student> Students { get; set; }
public virtual DbSet<StudentAddress> StudentAddresses { get; set; }
using (var context = await _contextFactory.CreateContext())
{
     context.History.Add(history);
     context.SaveChanges();
}

Ответы [ 3 ]

1 голос
/ 30 апреля 2019

Как и в вашем примере, он не "взрывается"

Следующая строка в основном только добавляет элемент в пустой трекер изменений:

 context.History.Add(history);

Если вы выполните

context.History.ToList()

Затем запрос выполняется как «select * from History», и вы наверняка столкнетесь с проблемой производительности, если он содержит миллионы строк.

Ключевым моментом является то, что EF «достаточно умен»."не загружать все в память как целое.Вы можете прикрепить профилировщик (или включить ведение журнала EF), чтобы увидеть фактические выполняемые запросы.Возьмите немного с собой, чтобы получить некоторый опыт.

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

Разница в субтитрах находится в пределах различий между IQueryable и другими IEnumerable -подобными интерфейсами.

Пока объект все еще только IQueryable, фактический запрос все еще должен быть выполнен и может быть расширен с помощью фильтров.Как я сказал;как только вы начинаете перечисление, выполняется фактический запрос, и, следовательно, нефильтрованный набор данных возвращает все строки таблицы.


Также обратите внимание на упомянутые методы linq

.Skip

И

. Взять

Есть еще несколько, например, группа, объединение, где и т. Д.

1 голос
/ 30 апреля 2019

Платформе сущностей не нужно тянуть какие-либо строки, чтобы выполнить вставку, что делает метод Add () и SaveChanged (). Он должен делать то же, что и в SQL, чтобы добавить строку в таблицу.

0 голосов
/ 30 апреля 2019

Вы должны понимать, что DbSet<Student> не представляет вашу коллекцию Students, она представляет таблицу Students в вашей базе данных. Это означает, что вы можете запрашивать последовательности свойств Students.

При желании вы можете запросить полную последовательность, но это приведет к проблемам с производительностью, если не к проблемам с памятью.

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

Пример: база данных с Schools и Students, с отношением один-ко-многим, каждый School имеет ноль или более Students, каждый Student посещает ровно один School:

class School
{
     public int Id {get; set;}
     public string Name {get; set;}
     ...

     // every School has zero or more Students (one-to-many)
     public virtual ICollection<Student> Students {get; set;}
}

class Student
{
     public int Id {get; set;}
     public string Name {get; set;}
     ...

     // Every Student attends exactly one School, using foreign key:
     public int SchoolId {get; set;}
     public virtual School School {get; set;}
}

В структуре сущностей столбцы таблиц представлены не виртуальными свойствами. Виртуальные свойства представляют отношения между таблицами (один ко многим, многие ко многим, ...)

Не делайте следующее!

public IEnumerable<School> GetSchoolByLocation(string city)
{
    return mySchoolWithItsStudents = dbContext.Schools
        .Where(school => school.City == city)
        .Include(school => school.Students)
        .ToList();
}

Почему бы и нет? Это похоже на идеальный код, не так ли?

Ну, может быть, вы получаете больше данных, чем будет использовать ваш абонент:

var mySchoolId = GetSchoolByLocation("Oxford")
    .Where(school => schoolStreet == "Main Street")
    .Select(school => school.Id)
    .FirstOrDefault();

Какая трата, сначала забрать все оксфордские школы, а потом оставить только эту!

Кроме того: вы получаете школу со всеми ее учениками и все, что вы используете, если идентификатор школы?

Попробуйте вернуть IQueryable<...> как можно дольше, и пусть вызывающая сторона решит, что делать с возвращенными данными.

Может быть, он хочет сделать ToList, или Count, или FirstOrDefault. Может быть, он хочет только Id и Name. Пока вы этого не знаете, не принимайте решение за него, это только делает ваш код менее пригодным для повторного использования.

Всегда используйте Select для выбора свойств и выбирайте только те данные, которые вы действительно планируете использовать. Используйте Include только если вы планируете обновить включенные данные.

var schools = dbContext.Schools.Where(school => ...)
    // Keep only the Schools that you actually plan to use:
    .Select(school => new
    {
        // only select the properties that you plan to use
        Id = school.Id,
        Name = school.Name,
        ...

        // Only the Students you plan to use:
        Students = school.Students.Where(student => ...)
            .Select(student => new
            {
                // Again, only the properties you plan to use
                Id = student.Id,
                Name = student.Name,

                // no need for the foreign key: you already know the value
                // SchoolId = student.SchoolId,
            }),
     });

Наконец, если вам нужен доступ ко всем Students, чтобы показать их, но вы не хотите получать все миллионы студентов одновременно, подумайте о том, чтобы выбрать их по странице. Запомните первичный ключ последнего элемента последней выбранной страницы и используйте `.Where (item => item.Id> lastFetchedPrimaryKey) .Take (pageSize), чтобы получить следующую страницу, пока страниц больше не будет.

Таким образом, вы можете запросить 50 учеников, в то время как вы отобразите только 25 из них, но, по крайней мере, у вас не все миллионы учеников в памяти. Выборка следующей страницы выполняется довольно быстро, поскольку индекс первичного ключа уже существует, а извлеченные элементы упорядочены по первичному ключу.

...