C # Оптимизация запроса Linq: обновление с помощью где - PullRequest
0 голосов
/ 18 февраля 2019

Как бы я оптимизировал следующий запрос?Мы просматриваем запросы к структуре сущностей в базе данных и пытаемся их изучить.

        using (var context = new DataDbContext())
        {
            var query = (from u in content.Parents
                         where u.Children.Any(y = y.Age > 13)
                         select u);

            foreach (var parent in query.ToList())
            {
                foreach (var children in owner.Children)
                {
                    children.IsTeenager= true;
                }
            }
            context.SaveChanges();
        }

Ответы [ 5 ]

0 голосов
/ 19 февраля 2019

Когда вы хотите обновить данные с помощью Entity Framework, вы должны сначала извлечь их в память (либо путем быстрой загрузки, либо с отложенной загрузкой), а затем изменить их.
Иногда это вызывает проблему N + 1.Это означает, что N + 1 обращается к серверу базы данных.А если вы используете какой-либо облачный хостинг, например Azure или AWS, то вы платите за каждый запрос.Так что это будет дорого в финансовом отношении.

В вашем случае, я бы предпочел сделать это в Да Entity Framework, но с использованием хранимой процедуры.Используются простые запросы, но я не рекомендую вам делать это таким образом.

Использование хранимых процедур избавит вас от внесения данных в память и даст результат с помощью 1 вызова базы данных.

0 голосов
/ 18 февраля 2019
(from u in content.Parents
  where u.Children.Any(y = y.Age > 13)
  select u);

Это похоже на ошибку.Если бы у Parent был ребенок, которому было 14 лет, а у другого, которому было 9 лет, для IsTeenager было бы установлено значение true.И наоборот, если бы у них был один ребенок, которому было 13 лет, у этого ребенка не было бы подростка, установленного в значение true.

Если возможно, я бы сделал IsTeenager вычисляемым свойством, а не хранимым значением

public bool IsTeenager => Age >= 13 && Age <= 19

Тогда мне никогда не пришлось бы вызывать запрос вообще.

Если бы я не изменил запрос на

content.Parents.SelectMany(o => o.Children).Where(o => o.Age >= 13 && o.Age <= 19)

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

content.Children.Where(o => o.Age >= 13 && o.Age <= 19)

В зависимости от структуры базы данных.Мне действительно нужно обращаться к родителям?

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

content.Parents.SelectMany(o => o.Children).Where(o => !o.IsTeenager && o.Age >= 13 && o.Age <= 19)

или

content.Children.Where(o => !o.IsTeenager && o.Age >= 13 && o.Age <= 19)
0 голосов
/ 18 февраля 2019

Нет хорошего способа упростить то, что вы здесь делаете.EF стремится протолкнуть вас вниз по пути чтения группы данных из хранилища данных, локально обновляя сущности и затем записывая эти изменения обратно.Это, очевидно, довольно медленно.Однако, то, что вы пытаетесь сделать здесь, это получить всех потомков и установить свойство IsTeenager, так что вам даже не нужен объект Parent, вы можете просто сделать это:

var children = context.Children.Where(c => c.Age < 13);

foreach(var child in children)
{
    child.IsTeenager = true;
}

context.SaveChanges();

Конечно, есть гораздо более простой способ сделать это, используя сырой SQL.Например:

context.Database.ExecuteSqlCommand("UPDATE Children SET IsTeenager = 1 WHERE Age < 13");
0 голосов
/ 18 февраля 2019

Ранее я видел в ваших комментариях следующее (с тех пор было удалено):

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

Если бы я задал кому-то этот вопрос на собеседовании, я бы надеялся, что они сообщат мне следующее:


  1. Возраст и IsTeenager должны зависеть от каждой отдельной сущности, а неВычислено для коллекции.
  2. Вы должны убедиться, что дата рождения человека / ребенка сохраняется, а не возраст.В противном случае вы понятия не имеете, когда возраст изменяется для сущности, поскольку возраст действителен только на дату и время, когда сущность захвачена в системе.
  3. IsTeeneger не должен сохраняться, его следует выводить на основеЗначение даты рождения.Если это когда-либо проверяется только в коде c # и вам не нужно запрашивать его, сделайте его полем только для модели, который не сопоставлен в Sql.Если вам нужно выполнить запрос к нему, сделайте вычисляемый столбец схемой базы данных.То же самое должно быть сделано для Age!
  4. Родитель и ребенок могут быть лучше смоделированы как Человек с рекурсивными отношениями «один ко многим», «Дети».Таким образом, вы также можете делать бабушек и дедушек, у которых есть дети.

Только модель

public class PersonModel
{
    public DateTime BirthDate { get; set; }
    public int Age
    {
        get
        {
            var today = DateTime.Today;
            // Calculate the age.
            var age = today.Year - BirthDate.Year;
            // Go back to the year the person was born in case of a leap year
            if (BirthDate > today.AddYears(-age)) age--;
            return age;
        }
    }
    public bool IsTeenager
    {
        get
        {
            return Age >= 13 && Age < 20;
        }
    }
}

Отображается в вычисляемый столбец

public class PersonModel
{
    public DateTime BirthDate { get; set; }
    public int Age { get; set; } // should be computed and mapped from sql
    public bool IsTeenager { get; set; } // should be computed and mapped from sql
}
0 голосов
/ 18 февраля 2019

Оптимизировать особо нечего, код можно уменьшить.Логика странная

using (var context = new DataDbContext())
{
    foreach (var child in content.Parents.Where(o => o.Children.Any(x => x.Age > 13)).SelectMany(o => o.Children))
    {
        children.IsTeenager= true;
    }

    context.SaveChanges();
}

или

using (var context = new DataDbContext())
{
    var parents = content.Parents.Where(o => o.Children.Any(x => x.Age > 13));
    foreach (var child in parents.SelectMany(o => o.Children))
    {
        children.IsTeenager= true;
    }

    context.SaveChanges();
}
...