【问题标题】:Entity Framework 6 Upsert实体框架 6 Upsert
【发布时间】:2017-07-14 01:03:59
【问题描述】:

我正在尝试让 upsert 与实体框架一起工作。我有以下引发错误的代码:

using (var db = new Entities.DB.DConn())
{
    //...
    foreach (Account account in accounts)
    {
        Entities.DB.Account dlAccount = new Entities.DB.Account();
        dlAccount.GId = dlG.Id;
        dlAccount.AccountName = account.NameAtFI;
        dlAccount.AccountNumber = account.AcctNumber;
        dlAccount.AcctType = account.AcctType;
        dlAccount.AsOfDate = account.DateCreated;
        dlAccount.IsDeleted = false;
        dlAccount.DateModified = DateTime.UtcNow.ToUniversalTime();

        db.Entry(dlAccount).State = ((dlAccount.GId == dlG.Id) ? EntityState.Modified : EntityState.Added);

        db.SaveChanges();
    }
}

例外:

存储更新、插入或删除语句影响了意外数量的行 (0)。自加载实体后,实体可能已被修改或删除。

基本上我要做的就是更新记录if dlAccount.GId == dlG.Id,或者如果它不存在则插入它。以下代码在不使用 EntityState 的情况下实现了我想要的:

using (var db = new Entities.DB.DConn())
{
    //...
    foreach (Account account in accounts)
    {
        bool isNewRecord = false;
        Entities.DB.Account dlAccount = new Entities.DB.Account();
        Entities.DB.Account exisitngAcct = db.Accounts.Where(x => x.GId == dlG.Id).FirstOrDefault(); //x.GId is NOT ad primary key
        if (exisitngAcct != null)
        {
            dlAccount = exisitngAcct;
            isNewRecord = true;
        }

        dlAccount.GId = dlG.Id;
        dlAccount.AccountName = account.NameAtFI;
        dlAccount.AccountNumber = account.AcctNumber;
        dlAccount.AcctType = account.AcctType;
        dlAccount.AsOfDate = account.DateCreated;
        dlAccount.IsDeleted = false;
        dlAccount.DateModified = DateTime.UtcNow.ToUniversalTime();

        if (isNewRecord)
        {
            dldb.Accounts.Add(dlAccount);
        }

        db.SaveChanges();
    }
}

谁能看到我在这里做错了什么?我真的很想让这个工作,避免使用像上面那样臃肿的代码。

TIA

【问题讨论】:

  • 您在共享代码的开头附近将它们设置为彼此相等,因此它们具有相同的值,那么为什么还要麻烦检查呢? (dlAccount.GId = dlG.Id;)
  • 您是否将实体添加到上下文中以便对其进行跟踪...?
  • @Igor 在插入的情况下需要设置这些值。我想我对如何评估记录是否存在感到困惑。
  • @JoePhillips 不,你所看到的就是我所拥有的。我错过了什么吗?

标签: c# entity-framework-6


【解决方案1】:

首先我应该指出,您发布的(非实体状态)示例中的逻辑看起来不像我期望的那样 - 至少根据我的理解,这可能是错误的 :)

*免责声明 - 我已经在文本编辑器中解决了这个问题,请原谅任何错误。

如果我们将此作为您的要求:

基本上我要做的就是在 dlAccount.GId == dlG.Id 时更新记录,如果不存在则插入它。

那么我希望非 EntityState 版本看起来像这样:

using (var db = new Entities.DB.DConn())
{
    //...
    foreach (Account account in accounts)
    {
        Entities.DB.Account dlAccount = null;

        Entities.DB.Account exisitngAcct = db.Accounts.Where(x => x.GId == dlG.Id).FirstOrDefault(); //x.GId is NOT ad primary key
        if (exisitngAcct != null)
        {
            //If there is an EXISTING account, it will already be tracked by EF so no need to attach it.
            dlAccount = exisitngAcct;                
        }
        else
        {
            //No account exists, so we need to create one, and ADD it to our EF context as a new Entity 
            dlAccount = new Entities.DB.Account();
            db.Accounts.Add(dlAccount);
        }

        dlAccount.GId = dlG.Id;
        dlAccount.AccountName = account.NameAtFI;
        dlAccount.AccountNumber = account.AcctNumber;
        dlAccount.AcctType = account.AcctType;
        dlAccount.AsOfDate = account.DateCreated;
        dlAccount.IsDeleted = false;
        dlAccount.DateModified = DateTime.UtcNow.ToUniversalTime();

        db.SaveChanges();
    }
}

假设以上是您所需要的,并且假设我们有充分的理由不只使用上面的 EF 跟踪,那么手动处理 EF 状态将如下所示:

using (var db = new Entities.DB.DConn())
{
    //...
    foreach (Account account in accounts)
    {
        Entities.DB.Account exisitngAcct = db.Accounts.FirstOrDefault(x => x.GId == dlG.Id).FirstOrDefault(); //x.GId is NOT ad primary key

        //NB. Since we're already pulling up the record with EF, there is *probably* no measurable advantage in not just using EF tracking at this point (unless this is a HUUUGE list of objects)
        //    in which case we should use the .AsNoTracking() modifier when we load the records (and they should be loaded in batches/all at once, to reduce DB hits)

        Entities.DB.Account dlAccount = new Entities.DB.Account();
        if(exisitngAcct == null)
        {
            db.Entry(dlAccount).State = EntityState.Added;
        }
        else
        {
            dlAccount.Id = exisitngAcct.Id; //We have to set the PK, so that EF knows which object to update
            db.Entry(dlAccount).State = EntityState.Modified;
        }

        dlAccount.GId = dlG.Id;
        dlAccount.AccountName = account.NameAtFI;
        dlAccount.AccountNumber = account.AcctNumber;
        dlAccount.AcctType = account.AcctType;
        dlAccount.AsOfDate = account.DateCreated;
        dlAccount.IsDeleted = false;
        dlAccount.DateModified = DateTime.UtcNow.ToUniversalTime();

        db.SaveChanges();
    }
}

【讨论】:

  • 您好史蒂夫,感谢您的回复。在插入场景中,显然这些是我需要设置的值,以便将它们插入数据库。在更新场景中,我需要确定是否存在已设置这些值的任何记录,如果存在,则使用设置值更新记录。你在检查什么 (dlAccount.GId > 0) ?您是否假设 dlAccount.GId 是主键?
  • 是的,我是 - 假设如果键 == 0 那么它是一个新记录。如果 GId 不是 PK 然后用它代替 GId...
  • GId 不是主键,而只是我在尝试确定记录是否存在时需要关闭的值。确切地说,如果我必须这样做: Entities.DB.Account exisitngAcct = db.Accounts.Where(x => x.GId == dlG.Id).FirstOrDefault(); if (exisitngAcct != null) { // 更新记录 }else{//插入记录}
  • 差不多@Rodney - 但实际上它的“diy,MSSQL 没有更新”;)
  • 点了,@Steve Land。我只是假设最初创建 EF 是为了让 MSSQL 成为它不是的样子,只是抱怨他们没有继续分层处理昂贵的“助手”来节省我的几次击键......我无疑会几年后在代码优化期间恢复;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-21
  • 2015-12-26
  • 2015-01-18
  • 2014-11-08
  • 2014-04-04
  • 2015-10-10
相关资源
最近更新 更多