如何更改 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 包含更多信息。