0

我们正在尝试在基于 Web API 的应用程序中实现多租户架构。我们在 SQL Server 中使用 RLS,并且Subscription_Id是给每个订阅者的内容。我们已经在 SQL Server 中设置了默认值Subscription_Id,所以当我调用 时db.SaveChanges(),我只想忽略Subscription_Id从 API 到 SQL Server 的操作。我尝试在覆盖方法中
设置 的值,但被困在这里。Subscription_IdSaveChanges()

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

1 回答 1

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约束。可以在此处找到有关更新现有列中的默认值的更多信息。

这些列不应包含在您的模型中。您的实体类中不应有它们的属性。如果您使用的是 Database First,您应该确保在从数据库更新模型时排除/忽略这些列。

如果您使用的是 EF Code First,如何执行此操作:您可以AlterColumnCreateColumn使用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_IdUp()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参数 in的 RLS 策略SESSION_CONTEXT,并且您的应用程序代码在打开数据库连接时通过 aDbConnectionInterceptor或类似的东西设置该值。

此页面包含更多信息。

于 2017-07-02T13:35:11.010 回答