如何更改 DbContext.SaveChanges() 的条目值
How to change entry values for DbContext.SaveChanges()
我们正在尝试在基于 Web API 的应用程序中实施多租户架构。我们在 SQL 服务器中使用 RLS,而 Subscription_Id
是提供给每个订阅者的内容。我们已经在 SQL 服务器中为 Subscription_Id
设置了默认值,所以当我调用 db.SaveChanges()
时,我只想忽略 Subscription_Id
转到 SQL来自 API.
的服务器
我尝试在 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();
}
我的建议是您不应为此修改 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。
这些列不应包含在您的模型中。您的实体 类 中不应包含它们的属性。如果您使用的是数据库优先,则应确保在从数据库更新模型时 exclude/ignore 这些列。
如果您使用的是 EF Code First,该怎么做:在使用 Add-Migration
生成代码迁移后,您可以在代码迁移中手动包含 AlterColumn
(或 CreateColumn
)指令。对每个实体都这样做 table:
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()
方法来删除列。)
警告:如果您在 table 中已有包含空 Subscription_Id
列的记录,则在 运行 此迁移时要小心值(或者如果您要向已有记录的 table 添加新的 Subscription_Id
列)。空列将填充执行迁移的连接中 Subscription_Id
的值,这可能是错误的(您可能不希望所有现有记录都与该特定订阅相关联)。在这种情况下,您可能希望在 Up()
方法和 Sql()
方法中包含具有正确 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 映射中。
注意:我在这里假设您在数据库中创建了一个使用 SESSION_CONTEXT
中的 UserId
参数的 RLS 策略,并且您的应用程序代码在打开数据库连接时设置了该值,通过 DbConnectionInterceptor
或类似的东西。
This page 包含更多信息。
我们正在尝试在基于 Web API 的应用程序中实施多租户架构。我们在 SQL 服务器中使用 RLS,而 Subscription_Id
是提供给每个订阅者的内容。我们已经在 SQL 服务器中为 Subscription_Id
设置了默认值,所以当我调用 db.SaveChanges()
时,我只想忽略 Subscription_Id
转到 SQL来自 API.
的服务器
我尝试在 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();
}
我的建议是您不应为此修改 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。
这些列不应包含在您的模型中。您的实体 类 中不应包含它们的属性。如果您使用的是数据库优先,则应确保在从数据库更新模型时 exclude/ignore 这些列。
如果您使用的是 EF Code First,该怎么做:在使用 Add-Migration
生成代码迁移后,您可以在代码迁移中手动包含 AlterColumn
(或 CreateColumn
)指令。对每个实体都这样做 table:
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()
方法来删除列。)
警告:如果您在 table 中已有包含空 Subscription_Id
列的记录,则在 运行 此迁移时要小心值(或者如果您要向已有记录的 table 添加新的 Subscription_Id
列)。空列将填充执行迁移的连接中 Subscription_Id
的值,这可能是错误的(您可能不希望所有现有记录都与该特定订阅相关联)。在这种情况下,您可能希望在 Up()
方法和 Sql()
方法中包含具有正确 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 映射中。
注意:我在这里假设您在数据库中创建了一个使用 SESSION_CONTEXT
中的 UserId
参数的 RLS 策略,并且您的应用程序代码在打开数据库连接时设置了该值,通过 DbConnectionInterceptor
或类似的东西。
This page 包含更多信息。