我想知道使用Entity Framework的DbContext.ExecuteSqlCommand
来保护类似于以下内容的SQL语句的可行性:
DELETE FROM HugeDataTable WHERE Id IN (1, 4, 6, 74, 82)
基本上,我有一个场景,我的表有一个任意长度的二进制列,我需要删除该行。由于我们使用的是实体框架,我通常会这样做:
// Data Structure
Parent: some object
Item: has a foreign key to Parent (ParentId)
Contents: has a foreign key to Item (ItemId)
// This is a little contrived, but it's meant to illustrate the point.
var parentId = 2;
var childItems = _context
.Items
.Include(item => item.Contents)
.Select(item => item.ParentId == parentid)
.ToList();
if (childItems.Any())
{
// I'm using DbContext, not IDbContext, so I have access to RemoveRange()
var contents = childItems.Select(item => item.Contents).ToList();
_context.ItemContents.RemoveRange(contents);
_context.Items.RemoveRange(childItems);
}
这里的问题是Contents
可能很大 - 兆字节或更大。所以我在第一个查询中通过Entity Framework将所有数据提取到内存中,然后在我稍后执行Select
时将其复制到内存中。这里的简单解决方案是删除Include
并执行直接SQL查询以删除内容 - 毕竟,我实际上并不需要需要那个二进制数据才能删除行;我只需要行的外键值。
这就是我们陷入困境的地方。
此时,我有几个选择。我最终选择的那个是:
var childItems = _context
.Items
.Select(item => item.ParentId == parentId)
.ToList();
if (childItems.Any())
{
foreach (var child in childItems)
{
var sql = "DELETE FROM Content WHERE ItemId = @itemId";
_context.Database.ExecuteSqlCommand(
sql,
new SqlParameter("@itemId", child.Id));
}
}
这有效,但我想知道我是否可以做得更好。我可以抛弃foreach
而只是这样做:
if (childItems.Any())
{
var sql = "DELETE FROM Content WHERE ItemId IN (@itemIds)";
var itemIds = childItems.Select(item => item.Id.ToString()).ToArray();
_context.DataBase.ExecuteSqlCommand(
sql,
new SqlParameter("@itemIds", string.Join(",", itemIds));
}
动臂。一个查询,没有foreach
循环。但我想知道它的安全性,或者我是否只是首先将参数化查询的全部用处都吹走了。
另外,我认为这可能最终成为微观优化。是的,更多的查询比较少的查询慢,但我正在寻找一个与数据库在同一物理网络上执行此操作的服务,并且大优化不会通过直接实体执行此操作将所有内容都拉入内存两次框架类。
所以我的最终问题是:是否可以像这样参数化WHERE...IN
查询?或者这首先是否会破坏参数化的目的?