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

Entity framework 如何更改DbContext.SaveChanges()的条目值,entity-framework,asp.net-web-api,db-first,Entity Framework,Asp.net Web Api,Db First,我们正在尝试在基于Web API的应用程序中实现多租户体系结构。我们在SQL Server中使用RLS,Subscription\u Id是提供给每个订阅者的内容。我们已经在SQL Server中为Subscription\u Id设置了默认值,因此当我调用db.SaveChanges()时,我只想忽略从API进入SQL Server的订阅Id。 我尝试在SaveChanges()override方法中设置Subscription\u Id的值,但在这里卡住了 public override i

我们正在尝试在基于Web API的应用程序中实现多租户体系结构。我们在SQL Server中使用RLS,
Subscription\u Id
是提供给每个订阅者的内容。我们已经在SQL Server中为
Subscription\u Id
设置了默认值,因此当我调用
db.SaveChanges()
时,我只想忽略从API进入SQL Server的
订阅Id

我尝试在
SaveChanges()
override方法中设置
Subscription\u 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\u Id
值,打开DB连接时除外

您需要做的是在数据库中的
Subscription\u Id
列中手动设置默认值约束,默认值基于当前会话
Subscription\u 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
约束。可以找到有关更新现有列中默认值的更多信息


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

如果您首先使用EF代码,如何执行此操作:使用添加迁移生成代码迁移后,您可以在代码迁移中手动包含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\u Id
列值的记录(或者向已有记录的表中添加新的
Subscription\u Id
列),则运行此迁移时要小心。空列将填充执行迁移的连接中的
订阅\u Id
的值,这可能是错误的(您可能不希望所有现有记录都与该特定订阅关联)。在这种情况下,您可能希望使用
Sql()
方法在
Up()
方法中包含带有正确
Subscription\u Id
值的显式
UPDATE
说明。大概是这样的:

Sql("UPDATE SomeEntitiesTable SET Subscription_Id= '19bc9b0d-28dd-4510-bd5e-d6b6d445f511' WHERE Id IN (1, 2, 5)");
使用“代码优先”,您还应该从模型类中删除Subscription\u Id属性。如果不能,至少在配置代码中为
Subscription\u Id
列添加显式
Ignore()
指令,您不希望它们出现在EF映射中

注意:我在这里假设您在数据库中创建了一个RLS策略,该策略在
会话\u上下文中使用
用户ID
参数,并且您的应用程序代码在打开数据库连接时通过
DbConnectionInterceptor
或类似的东西设置该值


包含更多信息。

为什么要更改或覆盖订阅Id?我认为这是一个主键/唯一键。否订阅id是针对订阅服务器的,因此与订阅服务器相关的所有数据都与订阅服务器id一起保存为什么要在savechanges()中设置订阅id的值?因为我在sql中使用行级安全性,并且我已经设置了subscriptionid以进行检查,并将subscriptionid的默认值设置为当前上下文值,但实体框架将subscriptionid发送为null,我只想忽略订阅id或发送savechanges上的值,因为所有表都会有该列“这些列不应包含在您的模型中。在实体类中不应该有它们的属性。如果您首先使用数据库,则应确保在从数据库更新模型时排除/忽略这些列。“如何排除Database first EF中的列。我已经设置了订阅id默认值。我对Database first不太熟悉。从注释中可以看出,您可以从edmx中的类模型中删除属性,并且在下次更新模型时不会重新生成这些属性,因为这些列仍然是我的n存储模型,因此EF不认为它们是添加到类模型中的新列。