C# 优化 Linq 查询:用 where 更新
C# Optimizing Linq query: Update with where
我将如何优化以下查询?我们正在审查数据库中的 entity framework 个查询,并努力学习。
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();
}
优化不多,可以减少代码。逻辑有点奇怪
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();
}
没有什么好方法可以简化您在这里所做的事情。 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");
我之前在你的评论中看到了以下内容(已被删除):
it was a job interview question I had few months ago, not even sure
如果我在求职面试中问某人这个问题,我希望他们会告诉我以下内容:
- Age 和 IsTeenager 应该取决于每个单独的实体,而不是通过集合计算。
- 您应该确保保存此人/child 的出生日期而不是年龄。否则,您不知道实体的年龄何时发生变化,因为年龄仅在系统中捕获实体的日期和时间有效。
- IsTeeneger 不应持久化,应根据出生日期值派生。如果它只在 c# 代码中检查过并且您不必对其进行查询,那么将其设为仅模型 getter 字段,即 not 映射到 Sql.如果您需要对其进行查询,请将其作为计算列作为数据库架构。
Age
! 也应该这样做
- Parent 和 Child 可以更好地建模为具有递归 one-to-many 关系 Children 的 Person。这样你也可以做 grand parents who have children who have children.
仅限模型
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
}
(from u in content.Parents
where u.Children.Any(y = y.Age > 13)
select u);
这似乎是一个错误。如果 Parent
有一个 child 是 14 而另一个是 9 两者都会将 IsTeenager
设置为 true。相反,如果他们有一个 13 岁的 child,那么 child 就不会将 Teenager 设置为 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)
取决于数据库的结构。我真的需要通过 parents 吗?
除非我知道 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)
当你想使用 Entity Framework 更新数据时,你必须先将它检索到内存中(通过预加载或延迟加载)然后修改它。
有时它会导致 N + 1 问题。这意味着对数据库服务器进行了 N+1 次调用。如果您使用某些基于云的托管服务,例如 Azure 或 AWS,那么您需要按请求付费。所以这在经济上也是昂贵的。
对于您的情况,我更愿意在 Entity Framework 中执行此操作,但使用存储过程。使用了普通查询,但我不建议您以这种方式进行查询。
使用存储过程将使您无需将数据带入内存,只需调用 1 次数据库即可获得结果。
我将如何优化以下查询?我们正在审查数据库中的 entity framework 个查询,并努力学习。
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();
}
优化不多,可以减少代码。逻辑有点奇怪
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();
}
没有什么好方法可以简化您在这里所做的事情。 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");
我之前在你的评论中看到了以下内容(已被删除):
it was a job interview question I had few months ago, not even sure
如果我在求职面试中问某人这个问题,我希望他们会告诉我以下内容:
- Age 和 IsTeenager 应该取决于每个单独的实体,而不是通过集合计算。
- 您应该确保保存此人/child 的出生日期而不是年龄。否则,您不知道实体的年龄何时发生变化,因为年龄仅在系统中捕获实体的日期和时间有效。
- IsTeeneger 不应持久化,应根据出生日期值派生。如果它只在 c# 代码中检查过并且您不必对其进行查询,那么将其设为仅模型 getter 字段,即 not 映射到 Sql.如果您需要对其进行查询,请将其作为计算列作为数据库架构。
Age
! 也应该这样做
- Parent 和 Child 可以更好地建模为具有递归 one-to-many 关系 Children 的 Person。这样你也可以做 grand parents who have children who have children.
仅限模型
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
}
(from u in content.Parents
where u.Children.Any(y = y.Age > 13)
select u);
这似乎是一个错误。如果 Parent
有一个 child 是 14 而另一个是 9 两者都会将 IsTeenager
设置为 true。相反,如果他们有一个 13 岁的 child,那么 child 就不会将 Teenager 设置为 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)
取决于数据库的结构。我真的需要通过 parents 吗?
除非我知道 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)
当你想使用 Entity Framework 更新数据时,你必须先将它检索到内存中(通过预加载或延迟加载)然后修改它。
有时它会导致 N + 1 问题。这意味着对数据库服务器进行了 N+1 次调用。如果您使用某些基于云的托管服务,例如 Azure 或 AWS,那么您需要按请求付费。所以这在经济上也是昂贵的。
对于您的情况,我更愿意在 Entity Framework 中执行此操作,但使用存储过程。使用了普通查询,但我不建议您以这种方式进行查询。
使用存储过程将使您无需将数据带入内存,只需调用 1 次数据库即可获得结果。