60

我有简单的实体:

public class Hall
{
    [Key]
    public int Id {get; set;}

    public string Name [get; set;}
}

然后在SeedAddOrUpdate用来填充表格的方法中:

var hall1 = new Hall { Name = "French" };
var hall2 = new Hall { Name = "German" };
var hall3 = new Hall { Name = "Japanese" };

context.Halls.AddOrUpdate(
    h => h.Name,
    hall1,
    hall2,
    hall3
);

然后我在包管理控制台中运行:

Add-Migration Current
Update-Database

一切都很好:我在“Hall”表中有三行。但是如果我再次在包管理控制台中运行,Update-Database我已经有五行了:

Id  Name
1   French
2   Japaneese
3   German
4   French
5   Japanese

为什么?我认为应该是三排,而不是五排。我尝试使用Id属性而不是,Name但它没有任何区别。

更新:

此代码产生相同的结果:

var hall1 = new Hall { Id = 1, Name = "French" };
var hall2 = new Hall { Id = 2, Name = "German" };
var hall3 = new Hall { Id = 3, Name = "Japanese" };

context.Halls.AddOrUpdate(
                h => h.Id,
                hall1);

context.Halls.AddOrUpdate(
                h => h.Id,
                hall2);

context.Halls.AddOrUpdate(
                h => h.Id,
                hall3);

我还通过 nuget 安装了最新的 EntityFramework。

4

13 回答 13

69

好吧,我用这个把脸从键盘上敲了一个小时。如果您的表的 Id 字段是 Identity 字段,那么它将不起作用,因此请为 identifierExpression 使用不同的字段。我使用了 Name 属性,还从new Hall {...}初始化程序中删除了 Id 字段。

对 OPs 代码的这种调整对我有用,所以我希望它对某人有所帮助:

protected override void Seed(HallContext context)
{
    context.Halls.AddOrUpdate(
        h => h.Name,   // Use Name (or some other unique field) instead of Id
        new Hall
        {
            Name = "Hall 1"
        },
        new Hall
        {
            Name = "Hall 2"
        });

    context.SaveChanges();
}
于 2013-03-14T15:51:00.157 回答
13

我知道这是一个老问题,但正确的答案是,如果你自己设置 id # 并且你想使用 AddOrUpdate 那么你需要告诉 EF/SQL 你不希望它生成 ID #。

modelBuilder.Entity<MyClass>().Property(p => p.Id)
    .HasDatabaseGeneratedOption(System.ComponentModel
    .DataAnnotations.Schema.DatabaseGeneratedOption.None); 

不利的一面是,当您插入一个新项目时,您需要设置它的 Id,因此如果这是在运行时动态完成的(而不是从种子数据中),那么您将需要计算出下一个 Id。 Context.MyClasses.Max(c=>c.Id) + 1效果很好。

于 2016-01-22T17:55:52.150 回答
9

此代码有效:

public Configuration()
{
    AutomaticMigrationsEnabled = true;
}

protected override void Seed(HallContext context)
{
    context.Halls.AddOrUpdate(
        h => h.Id,
        new Hall
        {
            Id = 1,
            Name = "Hall 1"
        },
        new Hall
        {
            Id = 2,
            Name = "Hall 2"
        });

    context.SaveChanges();
}
于 2012-11-20T08:06:42.297 回答
3

如果您错误地设置了实体状态,也可能会导致这种情况。当我运行更新数据库时,我不断收到以下错误...“序列包含多个匹配元素。”

例如,我在每个 update-database 命令上创建了重复的行(这当然不应该在播种数据时发生),然后下一个 update-database 命令根本不起作用,因为它找到了多个匹配项(因此序列错误说我有不止一个匹配的行)。那是因为我已经用对 ApplyStateChanges 的方法调用覆盖了我的上下文文件中的 SaveChanges ...

public override int SaveChanges()
{
    this.ApplyStateChanges();
    return base.SaveChanges();
}

我使用 ApplyStateChanges 来确保在添加对象图时,Entity Framework 明确知道对象是处于添加状态还是修改状态。关于我如何使用 ApplyStateChanges 的完整说明可以在这里找到。

这很好用(但需要注意!!)...如果您还使用 CodeFirst 迁移为数据库播种,那么上述方法将对 Seed 方法中的 AddOrUpdate() 调用造成严重破坏。因此,在其他任何事情之前,只需检查您的 DBContext 文件并确保您没有以上述方式覆盖 SaveChanges,否则您最终将获得第二次运行 update-database 命令的重复数据,然后根本无法工作第三次,因为每个匹配的项目不止一行。

归根结底,您不需要在 AddOrUpdate() 中配置 Id ......这违背了简单和初始数据库播种的整个目的。它可以通过以下方式正常工作:

context.Students.AddOrUpdate(
    p => p.StudentName,
    new Student { StudentName = "Bill Peters" },
    new Student { StudentName = "Jandra Nancy" },
    new Student { StudentName = "Rowan Miller" },
    new Student { StudentName = "James O'Dalley" },

只要我没有通过调用ApplyStateChanges 覆盖我的上下文文件中的SaveChanges 方法。希望这可以帮助。

于 2013-07-28T21:24:00.880 回答
2

这些步骤对我有用

  1. 删除表中的所有行。
  2. 将增量标识重置为 0。DBCC CHECKIDENT (yourtablename, RESEED, 0)(在 中指定的主键Seed()必须与数据库表中的主键匹配,以免它们重复。)
  3. 在“种子”方法中指定主键。
  4. Seed()多次运行该方法,然后检查它们是否重复。
于 2018-05-23T14:35:41.807 回答
1

我发现这AddOrUpdate适用于不是 ID 的字段。如果这对您有用:context.Halls.AddOrUpdate(h => h.Name, hall1, hall2, hall3)

您可能想要使用“French_test_abc_100”、“German_test_abc_100”等大厅名称。

当你测试你的应用程序时,这会阻止硬编码的测试数据搞砸事情。

于 2012-11-03T20:27:43.303 回答
0

如果 object(hall) 的 id 为 0,则为插入。我认为您需要仔细检查大厅对象的 id 字段

于 2012-04-04T09:03:27.513 回答
0

您的 ID 字段是身份字段吗?我遇到了同样的问题。当我从我的 ID 字段中删除身份状态并将 ID 设置到数据库中时,问题就解决了。

这对我有用,因为这些是查找表,无论如何都不应该是身份字段。

于 2012-05-08T15:54:39.533 回答
0

我认为您可能需要使用类似于Update-Database TargetMigration:0后跟Update-Database.

实际上,您不会删除现有的表或值,而只是添加/更新这些值。这需要发生才能获得您想要的结果。

于 2015-03-03T01:15:19.210 回答
0

我使用 ID 字段作为 Identity/Key 并添加属性而不是由服务器分配 Id。这为我解决了这个问题。

public class Hall
{
    [Key]
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id {get; set;}

    public string Name [get; set;}
 }
于 2016-11-24T11:00:56.873 回答
0

就 Ciaren 的回答而言,以下重置 ModelCreating 上下文的代码帮助我解决了类似的问题。确保将“ApplicationContext”更改为您的 DbContext 名称。

public class ApplicationContext : DbContext, IDbContext
    {
        public ApplicationContext() : base("ApplicationContext")
        {
             
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
            Database.SetInitializer<ApplicationContext>(null);
            base.OnModelCreating(modelBuilder);
        }
     }

于 2017-08-09T11:06:47.920 回答
0

我发现要让它起作用,当种子第一次运行时,身份位置应该是 0。您可以使用以下方法重置它:

DBCC CHECKIDENT (tableName, RESEED, 0)
于 2018-07-17T09:50:51.390 回答
-1

你也可以这样做:

 context.Halls.AddOrUpdate(new Hall[]{hall1,hall2, hall3});
于 2018-03-21T20:43:08.990 回答