如何保存包含现有数据和新数据的模型?

10

我目前拥有一个模型,其中包含现有数据和新数据。

以下是我的模型示例:

 public class NameDetails
    {
        public int Id { get; set; }
        public string Name { get; set; }

    }

这是它当前拥有的模拟数据

  List<NameDetails> Names = new List<NameDetails>{

                new  NameDetails{Id = 1, Name = "Name 1"},
                new NameDetails{Id = 2 , Name = "Name 2"},
            };

现在假设我需要将这个保存到数据库中...我已经在表中有id = 1,所以应该是更新操作,而id = 2应该是添加操作... 我该怎么办?

之前,当我使用存储库编写保存时,我要么进行添加操作,要么进行编辑操作 添加操作如下所示,

 context.NameDetails.Add(NameDetails); 
    context.SaveChanges(); 

或者像这样进行编辑:

var recordToUpdate = context.NameDetails.FirstOrDefault(x => x.Id== 1);
recordToUpdate.Name = "New name";
context.SaveChanges();

那么这是否意味着我必须循环遍历我的列表并找出什么是新的,什么不是新的……还是有其他方法?


1
你只有两个选择:-查询数据库以确定对象是否已经存在。或者-让对象持有某些属性以确定它是新的(例如,Id=0)。 - Reda
2
不确定是否真正相关,但是您从哪里获取新记录的Id值为2? - lc.
@user2266486 或者创建一个存储过程来执行upsert操作,但我想知道这个问题/难题是否还有更多的内容。 - lc.
4个回答

2
您可以使用一些适用于Entity Framework的约定。
例如,如果您在数据库中使用IDENTITY(1,1)(因此它会自动生成插入行的ID),则实体属性应该将StoreGeneratedPattern设置为Identity(模型优先方法的情况),而Id属性的值为0表示尚未添加到数据库。 然后,您可以轻松地决定要添加什么以及要更新什么。这是一些伪代码(未经测试):
foreach (var entity in entities)
{
    if (entity.Id == 0)
    {
        // Adds to the context for a DB insert
        context.Entities.Add(entity);
    }
    else
    {
        // Updates existing entity (property by property in this example, you could update
        // all properties in a single shot if you want)
        var dbEntity = context.Entities.Single(z => z.Id == entity.Id);
        dbEntity.Prop1 = entity.Prop1;
        // etc...
    }
}

context.SaveChanges();

1

仅限EF5及以上版本:

如果您有一种方法来检测一个项目是否为新的(例如Id == 0),您可以使用以下方法避免需要映射:

foreach(var entity in entities)
{
    context.NameDetails.Attach(entity);
    context.Entry(entity).State = IsNewEntity(entity) 
                                  ? EntityState.Added
                                  : EntityState.Modified;
}

这将告诉EntityFramework为新实体创建INSERT,为旧实体创建UPDATE。

0

试试这个:

var nameIds = Names.Select(n => n.Id);

var recordsToUpdate = context.NameDetails.Where(x => nameIds.Contains(x.Id));

0

Entity Framework 在这种情况下表现不佳。以下代码应该能让你接近你想要的结果:

public class NameDetails
{
    public int Id {get;set;}
    public string Name {get;set;}
}

List<NameDetails> Names = new List<NameDetails>
    {
        new  NameDetails{Id = 1, Name = "Name 1"},
        new NameDetails{Id = 2 , Name = "Name 2"},
    };


var toFetch = Names.Select(n => n.Id).ToArray();
var association = context.NameDetails
                         .Where(n => toFetch.Contains(n.Id))
                         .ToDictionary(n => n.Id);

foreach(var name in Names)
{
    NameDetails existing;
    if(association.TryGetValue(name.Id, out existing))
    {
        // It exists, map the properties in name to existing
    }
    else
    {
        // It's new, perform some logic and add it to the context
        context.NameDetails.Add(name);
    }
}

context.SaveChanges()

或者,您可以尝试劫持实体框架迁移的{{link1:AddOrUpdate}},该方法执行upsert操作:

// You need a namespace import of  System.Data.Entity.Migrations
context.NameDetails.AddOrUpdate(names.ToArray());
context.SaveChanges();

我没有尝试过这个,但它应该可以工作,除非在AddOrUpdate中有我不理解的其他逻辑。


我快速查看了AddOrUpdate的实现,虽然它构建表达式的方式有点令人费解,但它似乎并没有创建任何神奇的upsert SQL。我相当确定它会查询尝试获取您传入的实体,并在找到该实体时更新该实体,如果没有这样的实体,则添加它。当您正在填充数据库并且所有内容已经在内存中时,这很好,但是当您意外地向数据库发出大量TOP(1)查询时,可能不太好。 - Steve Ruble

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接