简体   繁体   English

EF更新关系的子关系

[英]EF update the child relation of a relation

EF tracking confuses me. EF跟踪使我感到困惑。 Here is a scenario that I am trying to achieve: 这是我正在尝试实现的方案:

public class CentralPoint
{
   public Guid ID { get; set; }
   public virtual BIDatabase BIDatabase { get; set; }
}

public class BIDatabase
{
    public Guid ID { get; set; }
    public Guid CentralPointID { get; set; }
    public virtual CentralPoint CentralPoint { get; set; }
    public Guid ConnectionID { get; set; }
    public virtual Connection Connection { get; set; }
}

public class Connection
{
    public Guid ID { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
}

These are my entity models. 这些是我的实体模型。 I am having a one-one relationship between CentralPoint and BIDatabase entities, also a one-one relationship between BIDatabase and Connection entities. 我在CentralPointBIDatabase实体之间BIDatabase一对一关系,在BIDatabaseConnection实体之间也存在一对一关系。 Obviously there is a separate table for each of these entities. 显然,每个实体都有一个单独的表。

Inside the controller I am updating the BIDatabase property of the CentralPoint with a new instance 在控制器内部,我正在使用新实例更新CentralPointBIDatabase属性。

centralPoint.BIDatabase = biDatabase;

Here is the controller part: 这是控制器部分:

public async Task<IActionResult> AddBIDatabaseAsync(Guid cpId, BIDatabase biDatabase)
    {
       // context is available through Dependency Injection (.net core)
        var centralPoint = _context.CentralPoint.Where(cp => cp.ID == cpId)
            .Include(cp => cp.BIDatabase)
            .ThenInclude(biDb => biDb.Connection)
            .FirstOrDefault();

        centralPoint.BIDatabase = biDatabase;
        await _context.SaveChangesAsync();

        // more code ...
    }

After this: 在这之后:

CentralPoint Table -> Remains Unchanged: Normal CentralPoint表->保持不变:正常

BIDatabase Table -> Updated with new IDs: Normal BIDatabase表->更新为新ID:正常

Connection Table -> // A new row is added instead of updating the old one 连接表-> //添加新行,而不是更新旧行

What I wanted is the connection entity to be updated and not added every time for the same database. 我想要的是对同一数据库每次都更新而不是每次添加的连接实体。

I think I see the issue. 我看到了问题。

When you replace centralPoint.BIDatabase = biDatabase; 当您替换centralPoint.BIDatabase = biDatabase; your new BIDatabase object probably doesnt have the Id linking it to Connection 您的新BIDatabase对象可能没有将其链接到Connection的ID

Try mapping the fields you need onto the existing object instead of replacing it (or replace it, but map any Ids and existing fields that you want to be persisted through onto the new object first). 尝试将所需的字段映射到现有对象上,而不是替换它(或替换它,但首先将要持久化的所有Id和现有字段映射到新对象上)。

What code created this new biDatabase? 什么代码创建了这个新的biDatabase? Was it persisted to a dbContext prior to this method? 在此方法之前,它是否一直保留到dbContext?

Your expectation around the BIDatabase and connection table is a bit suspicious. 您对BIDatabase和连接表的期望有点可疑。 Given an existing CentralPoint /w BIDatabase /w Connection, if you "change" the CentralPoint's BIDatabase to a new one, the existing record is not updated, it is replaced. 给定现有的CentralPoint / w BIDatabase / w连接,如果将CentralPoint的BIDatabase“更改”为新的,则现有记录不会更新,它将被替换。 This means that the old record and it's associated connection would be removed, and a new BIDatabase /wa new Connection would be inserted. 这意味着将删除旧记录及其关联的连接,并插入新的BIDatabase / wa new Connection。 Assuming these entities are configured with DB generated keys: 假设使用数据库生成的密钥配置了这些实体:

Example: CentralPoint(id:1) -> BIDatabase(id:1) -> Connection(id:1) 示例:CentralPoint(id:1)-> BIDatabase(id:1)-> Connection(id:1)

Create new BIDatabase (id:x) with new Connection (id:x) then: 使用新的连接(id:x)创建新的BIDatabase(id:x),然后:

CentralPoint.BIDatabase = newBIDatabase

BIDatabase(id:1) & Connection(id:1) are marked for deletion. BIDatabase(id:1)和Connection(id:1)被标记为删除。 new BIDatabase & Connection will be persisted with new IDs when saved. 保存时,新的BIDatabase&Connection将保留新的ID。

CentralPoint(id:1) -> BIDatabase(id:2) -> Connection(id:2) CentralPoint(id:1)-> BIDatabase(id:2)-> Connection(id:2)

If you want to replace the BIDatabase but keep the same connection ID/reference: 如果要替换BIDatabase但要保留相同的连接ID /引用,请执行以下操作:

Create the new BIDatabase(id:x) /w Connection(id:x) 创建新的BIDatabase(id:x)/ w Connection(id:x)

but before saving it to the context: 但在将其保存到上下文之前:

   var centralPoint = _context.CentralPoint.Where(cp => cp.ID == cpId)
        .Include(cp => cp.BIDatabase)
        .ThenInclude(biDb => biDb.Connection)
        .FirstOrDefault();

   biDatabase.Connection = (centralPoint?.BIDatabase?.Connection) ?? biDatabase.Connection;

    centralPoint.BIDatabase = biDatabase;
    await _context.SaveChangesAsync();

What that extra statement does is look to preserve an existing connection already on the existing CentralPoint.BIDatabase by copying the reference over to the new BIDatabase. 该额外语句的作用是通过将引用复制到新的BIDatabase来保留现有CentralPoint.BIDatabase上已有的现有连接。 If the existing BIDatabase didn't have a connection (or the existing CP didn't have a BIDatabase) then the new connection created would be used. 如果现有的BIDatabase没有连接(或现有的CP没有BIDatabase),则将使用创建的新连接。 Provided you haven't saved the new BIDatabase to a dbContext prior to this code, the "new" Connection (id:x) will never get inserted where an existing one is substituted. 如果没有在此代码之前将新的BIDatabase保存到dbContext,则“新”连接(id:x)将永远不会插入到替换现有BIDatabase的位置。

There were a couple of mistakes I was doing. 我在犯一些错误。 I am detailing them so it might be helpful to others. 我正在详细说明它们,因此可能会对其他人有所帮助。

First mistake (pointed out by @Steveland83) 第一个错误 (由@ Steveland83指出)

centralPoint.BIDatabase = biDatabase; centralPoint.BIDatabase = biDatabase;

Since the CentralPoint entity is being tracked by the context (DbContext) we cannot simply replace a property by another. 由于上下文(DbContext)跟踪了CentralPoint实体,因此我们不能简单地用另一个属性来替换。 We need to modify the property by copying values. 我们需要通过复制值来修改属性。 So the following should be done instead 因此,应改为执行以下操作

_context.Entry(centralPoint.BIDatabase).CurrentValues.SetVal‌​ues(biDatabase); _context.Entry(centralPoint.BIDatabase).CurrentValues.SetVal的UE(biDatabase);

Second mistake With the fix above, I was expecting the context to track all property changes in the centralPoint.BIDatabase and update those as well by itself (In my case the Connection property eg. centralPoint.BIDatabase.Connection) 第二个错误通过上面的修复,我希望上下文能够跟踪centralPoint.BIDatabase中的所有属性更改,并自行进行更新(在我的情况下为Connection属性,例如centralPoint.BIDatabase.Connection)。

However unfortunately this does not happen for non primitive types. 但是,不幸的是,这对于非原始类型不会发生。 You have to tell the context explicitly which of the non-primitive properties changed. 您必须明确地告诉上下文哪些非原始属性已更改。 Here is the second fix 这是第二个解决方法

_context.Entry(centralPoint.BIDatabase).CurrentValues.SetVal‌​ues(biDatabase); _context.Entry(centralPoint.BIDatabase).CurrentValues.SetVal的UE(biDatabase);

_context.Entry(centralPoint.BIDatabase.Connection).CurrentValues.SetVal‌​ues(biDatabase.Connection); _context.Entry(centralPoint.BIDatabase.Connection).CurrentValues.SetVal的UE(biDatabase.Connection);

This Updates the BIDatabase and the Connection tables with the changes. 这将使用更改来更新BIDatabase和Connection表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM