实体框架6 Upsert

时间:2017-07-11 20:24:20

标签: c# entity-framework-6

我正在尝试使用Entity Framework。我有以下代码抛出错误:

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

1 个答案:

答案 0 :(得分:2)

首先我要指出你发布的(非EntityState)示例中的逻辑看起来并不像我期望的那样 - 至少基于我的理解,这可能是错误的:)

*免责声明 - 我已经在文本编辑器中将其破解,请原谅任何错误。

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

  

基本上我想要做的就是更新记录,如果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();
    }
}