如何更改 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() 的条目值的主要内容,如果未能解决你的问题,请参考以下文章

更改模式时如何更改导航阴影颜色?

如何更改分区ID为AF?

更改标签文本时如何更改标签颜色

如何更改环境的字体大小?

如何使用 sqitch postgresql 验证更改列数据类型更改?

如何更改列表框项目的文本?