简而言之;
当您调用SaveChanges
时,是否有办法在EF Core中将查询过滤器应用于生成的UPDATE
和DELETE
语句的WHERE
子句?(与仅适用于SELECT
场景的全局查询过滤器非常相似。)
全局查询过滤器对于SELECT
场景非常有效,通过向生成的SELECT
语句的WHERE
子句添加附加条件来实现,但是当您调用DbContext的SaveChanges
方法时,它们不会对UPDATE
和DELETE
语句执行相同的操作,我想知道在调用SaveChanges
时是否可能使用EF Core将查询过滤器应用于生成的UPDATE
和DELETE
语句的WHERE
子句? 就像HasQueryFilter
对于SELECT
语句一样。
我将在小型应用程序的多租户场景中使用此功能,并且我能够在其他层中处理此要求,或通过覆盖SaveChanges并检查已删除或修改的实体是否属于正确的租户来处理此要求; 但是,由于性能和安全考虑,我更喜欢查询过滤器方法。
更多上下文
查询过滤器的常见应用场景之一是多租户。一个租户的数据应该受到其他租户读取和修改的保护:
- 被其他租户读取:使用
HasQueryFilter
注册全局查询过滤器可以很好地解决。 - 被其他租户修改:如何在EF DbContext中实现?
您可以查看Microsoft Docs上的查询过滤器或者他们在GitHub上的示例代码。
为什么我要寻找UPDATE和DELETE的查询过滤器?
主要原因如下:
- 性能考虑
- 安全考虑
毕竟,我们有SELECT语句的查询过滤器,为什么不为UPDATE和DELETE提供呢?
性能考虑
我能够处理这个需求在其他层,比如API、DAL、BLL或是通过重写SaveChanges
并检查被删除或修改的实体是否属于正确租户来完成;我不想在BLL或SaveChanges中做这个是因为性能问题。一个WHERE子句将在数据库服务器端进行评估,这是安全和高性能的,相当类似于全局查询过滤器。例如,在删除场景下,一个明显的答案可能是像这样的:
var entity = db.BlogPosts.Find(id);
if(entity!=null)
db.Entry(entity).State = EntityState.Deleted;
db.SaveChanges();
基本上是执行一个
SELECT
然后再执行一个 DELETE
语句。
但是我更倾向于像这样直接执行一个 DELETE
语句:
db.Entry(new BlogPost() { Id = id }).State = EntityState.Deleted;
db.SaveChanges();
安全考虑
首先,这些实体在 DbContext.Entries
中不一定具有TenantId
。另一方面,即使它们为其TenantId
指定了一个值,该值也是不可靠的,因为它可能已更改。例如 BlogPost.Id = 2
可能属于租户 2,而租户 1 正试图对其进行修改或删除。
HasQueryFilter
的方法可用(或者可以轻松地创建)用于 DbContext,在 UPDATE 和 DELETE 语句中添加条件来应用过滤器到SaveChanges
。 - Reza Aghaei