EntityFramework在SaveChanges上添加重复的相关对象

时间:2015-01-23 02:14:55

标签: c# entity-framework ef-code-first entity-framework-6 duplication

物件

public class Noun
{
    public int Id { get; set; }

    [MaxLength(128)]
    public string Name { get; set; }
    public bool Active { get; set; }

    public virtual Category Category { get; set; }
    public int CategoryId { get; set; }
}

public class Category
{
    public int Id { get; set; }

    [MaxLength(128)]
    public string Name { get; set; }
}

存储库

public Noun CreateNoun(string name, string categoryName)
{
    using (MyContext context = new MyContext())
    {
        Noun noun;
        Category category;

        lock (NounLock)
        {
            // don't create it if it already exists
            noun = context.Nouns
                .Include(t => t.Category)
                .FirstOrDefault(t => t.Name == name && t.Category.Name == categoryName);

            if (noun == null)
            {
                // make the category if it doesn't already exist
                lock (CategoryLock)
                {
                    category = context.Categories.FirstOrDefault(c => c.Name == categoryName);
                    if (category == null)
                    {
                        category = new Category() { Name = categoryName };
                        context.Categories.Add(category);
                        context.SaveChanges();
                    }
                }

                noun = new Noun()
                {
                    Name = name,
                    Category = category,
                    CategoryId = category.Id
                };

                context.Nouns.Add(noun);
            }
            else
            {
                category = noun.Category;
            }

            // make sure the noun is set as active
            noun.Active = true;

            context.Entry(category).State = EntityState.Unchanged;
            context.SaveChanges();

            return noun;
        }
    }
}

上下文

internal class MyContext : DbContext
{ 
    public DbSet<Category> Categories { get; set; }
    public DbSet<Noun> Nouns { get; set; }

    public MyContext()
        : base("DefaultConnection")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Nouns
        modelBuilder.Entity<Noun>()
            .HasRequired(t => t.Category);
    }
}

问题

当调用CreateNoun()时,当具有该类别的名词已经存在时,它应该(基于我认为我在做什么),只需从数据库加载现有的名词并将其标记为活动。但相反,它会插入一个新的名词和一个新的类别。我究竟做错了什么?我知道这可能是一件小事。

PS:锁是静态的,因为这可能被多线程租户使用

示例

Noun newNoun = repo.CreateNoun("name", "cat");
// should load existing noun from db, and set it as active, but instead duplicates it
Noun oldNoun = repo.CreateNoun("name", "cat"); 

2 个答案:

答案 0 :(得分:1)

CreateNoun方法中存在一些问题,我做了一些我稍后要解释的更改:

public Noun CreateNoun(string name, string categoryName)
{
   using (MyContext context = new MyContext())
   {
       Noun noun;
       Category category;

       lock (NounLock)
       {
          // don't create it if it already exists
          noun = context.Nouns
                    .FirstOrDefault(t => t.Name == name && t.Category.Name == categoryName);

          if (noun == null)
          {
             // make the category if it doesn't already exist
             lock (CategoryLock)
             {
               category = context.Categories.FirstOrDefault(c => c.Name == categoryName);
               if (category == null)
               {
                  category = new Category() { Name = categoryName };
                  context.Categories.Add(category);
                }
             }
             noun = new Noun()
             {
               Name = name,
               Category = category,
             };
             context.Nouns.Add(noun);
          }

          // make sure the noun is set as active
          noun.Active = true;

          context.SaveChanges();

          return noun;
       }
   }
}
  • 您不需要致电Include,EF会加载类别 物业给你。这个navegation属性是虚拟的,它将是 lazy loaded
  • 当您找不到Category时,您不需要致电 SaveChanges方法。在回到noum之前,你是在呼唤这个 方法,它将保存您之前所做的所有更改。
  • 您不需要设置外键CategoryId。你不知道 如果category是从DB加载的或最近创建的。只需设置 navegation property Category
  • 您不需要更改Category

另一个推荐:

OnModelCreating方法中,您根据需要配置Category。当您要配置关系时,使用方法HasRequired。在你的情况下,它将是这样的:

 protected override void OnModelCreating(DbModelBuilder modelBuilder)
 {
        modelBuilder.Entity<Noun>()
            .HasRequired(t => t.Category).WithMany().HasForeignKey(n=>n.CategoryId);
 }

更新

正如@Shoe在关于第一点的评论中所说,如果你想使用此方法返回的Noun并查阅其类别,请在搜索{时调用Include方法{1}}正如您之前所做的那样。

答案 1 :(得分:0)

您尚未在模型中标记您的钥匙。两个Id属性都需要标有[Key],而Category类中的Noun属性需要[ForeignKey("CategoryId")]注释。由于t.Category.Name始终为空,因此重复检查始终失败。