如何更改DbContext.SaveChanges()的条目值

时间:2017-06-30 05:31:25

标签: entity-framework asp.net-web-api db-first

我们正在尝试在基于Web API的应用程序中实现多租户架构。我们在SQL Server中使用RLS,Subscription_Id是给每个订阅者的内容。我们在SQL Server中设置了Subscription_Id的默认值,所以当我调用db.SaveChanges()时,我只想忽略从API进入SQL Server的Subscription_Id
我尝试在Subscription_Id覆盖方法中设置SaveChanges()的值,但卡在此处。

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();
}

1 个答案:

答案 0 :(得分:0)

我的建议是,您不应修改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,如何执行此操作:在使用AlterColumn生成代码迁移后,您可以在代码迁移中手动包含CreateColumn(或Add-Migration)条指令。为每个实体表执行此操作:

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的值,这可能是错误的(您可能不希望所有现有记录与该特定订阅相关联) )。在这种情况下,您可能希望在UPDATE方法中使用Subscription_Id方法,在Up()方法中包含明确的Sql()指令。像这样:

Sql("UPDATE SomeEntitiesTable SET Subscription_Id= '19bc9b0d-28dd-4510-bd5e-d6b6d445f511' WHERE Id IN (1, 2, 5)");

使用Code First,您还应该从模型类中删除Subscription_Id属性。如果您不能在Ignore()列的配置代码中添加明确的Subscription_Id说明,那么您在EF映射中不需要它们。

注意:我在此假设您在数据库中创建了一个使用UserId中的SESSION_CONTEXT参数的RLS策略,并且您的应用程序代码在打开数据库连接时设置了该值,通过DbConnectionInterceptor或类似的东西。

This page包含更多信息。