如何更改 DbContext.SaveChanges() 的条目值
Posted
技术标签:
【中文标题】如何更改 DbContext.SaveChanges() 的条目值【英文标题】:How to change entry values for DbContext.SaveChanges() 【发布时间】:2017-12-03 23:38:17 【问题描述】:我们正在尝试在基于 Web API 的应用程序中实现多租户架构。我们在 SQL Server 中使用 RLS,Subscription_Id
是分配给每个订阅者的内容。我们已经在 SQL Server 中为 Subscription_Id
设置了默认值,所以当我调用 db.SaveChanges()
时,我只想忽略从 API 到 SQL Server 的 Subscription_Id
。
我尝试在SaveChanges()
覆盖方法中设置Subscription_Id
的值,但卡在这里。
public override int SaveChanges()
var objectType = selectedEntity.CurrentValues.ToObject();
Guid value = new Guid("54E720FC-616B-44C6-8485-5F2185FD7B4C");
PropertyInfo propertyInfo =
objectType.GetType().GetProperty("Subscription_Id");
ChangeTracker.Entries().FirstOrDefault()
.CurrentValues.ToObject().GetType()
.GetProperty("Subscription_Id")
.SetValue(objectType, Convert.ChangeType(value, propertyInfo.PropertyType), null);
return base.SaveChanges();
【问题讨论】:
为什么要更改或覆盖 Subscription_Id ?我认为这是一个主键/唯一键。 没有订阅 ID 用于订阅者,因此与订阅者相关的所有数据都与订阅者 ID 一起保存 为什么要在 savechanges() 中设置 subscriptionId 的值?? 因为我在 sql 中使用行级安全性,并且我已设置订阅 ID 以检查那里并将订阅 ID 的默认值设置为当前上下文值,但实体框架将订阅 ID 发送为null ,我只想忽略订阅 id 或在 savechanges 上发送值,因为所有表都将具有该列 【参考方案1】:我的建议是您不应该为此修改您的SaveChanges()
代码。
使用 RLS 的推荐方法是使 TenantId
列对您的 EF 模型和代码透明,因此您无需在实体中定义租户 ID 或导航属性。这样,您无需更改您的 SaveChanges()
代码,或者在打开数据库连接时以外的代码中的任何位置显式管理和设置 Subscription_Id
值。
您需要做的是在数据库的Subscription_Id
列中手动设置默认值约束,默认值基于当前会话Subscription_Id
参数。该值将在插入记录时设置,并隐式用于在数据库级别过滤任何后续查询和命令。
如果是新列:
ALTER TABLE SomeEntityTable ADD Subscription_Id nvarchar(128)
DEFAULT CAST(SESSION_CONTEXT(N'UserId') AS nvarchar(128))
如果是现有列:
ALTER TABLE SomeEntityTable
ADD DEFAULT CAST(SESSION_CONTEXT(N'UserId') AS nvarchar(128)
FOR Subscription_Id
如果该列具有先前不同的 DEFAULT
值,最好也删除其关联的过时 DEFAULT
约束。有关更新现有列中的默认值的更多信息,请参阅 here。
这些列不应包含在您的模型中。您的实体类中不应有它们的属性。如果您使用 Database First,您应该确保在从数据库更新模型时排除/忽略这些列。
如果您使用的是 EF Code First,如何执行此操作:您可以在使用 Add-Migration
生成代码迁移后手动将 AlterColumn
(或 CreateColumn
)指令包含在代码迁移中。对每个实体表执行此操作:
public override void Up()
AlterColumn("dbo.SomeEntityTable", "Subscription_Id",
c => c.String(
nullable: false,
maxLength: 128,
defaultValueSql: "CAST(SESSION_CONTEXT(N'UserId') AS nvarchar(128))"));
(最好添加一个 Down()
方法来删除该列。)
警告:如果您的表中已有记录具有空的 Subscription_Id
列值(或者如果您要向表中添加新的 Subscription_Id
列,则在运行此迁移时要小心已经有记录)。空列将填充正在执行迁移的连接中的Subscription_Id
的值,这可能是错误的(您可能不希望所有现有记录都与该特定订阅相关联)。在这种情况下,您可能希望使用 Sql()
方法在 Up()
方法中包含具有正确 Subscription_Id
值的显式 UPDATE
指令。像这样的:
Sql("UPDATE SomeEntitiesTable SET Subscription_Id= '19bc9b0d-28dd-4510-bd5e-d6b6d445f511' WHERE Id IN (1, 2, 5)");
使用 Code First,您还应该从模型类中删除 Subscription_Id
属性。如果不能,至少在 Subscription_Id
列的配置代码中添加明确的 Ignore()
指令,您不希望它们出现在 EF 映射中。
注意:我在这里假设您在数据库中创建了一个 RLS 策略,该策略使用SESSION_CONTEXT
中的UserId
参数,并且您的应用程序代码在打开数据库连接时通过DbConnectionInterceptor
设置该值或类似的东西。
This page 包含更多信息。
【讨论】:
"这些列不应包含在您的模型中。您的实体类中不应包含它们的属性。如果您使用 Database First,您应该确保在更新您的模型时排除/忽略这些列数据库中的模型。”我如何首先排除数据库中的列 EF 。我已经设置了订阅id默认值 我对 Database First 不太熟悉。从 cmets in this post 看来,您似乎可以从 edmx 中的类模型中删除属性,并且下次更新模型时它们不会重新生成,因为列仍在存储模型中,所以 EF 没有不要将它们视为添加到类模型的新列。以上是关于如何更改 DbContext.SaveChanges() 的条目值的主要内容,如果未能解决你的问题,请参考以下文章