Ef 6.0多对多插入如此多的编码......为什么?

时间:2015-12-22 09:38:45

标签: c# sql-server entity-framework

我的问题更多的是为什么ef 6.0不支持忽略重复?

让我们举个例子:

我有两张桌子

  1. 应用{id,AppName}
  2. AdGroup {id,GroupName}
  3. 应用程序与ADGroups有很多对。现在,每次我添加一个包含广告组的应用程序时,我都需要先执行以下操作以避免重复的密钥插入:

    1. 检查应用程序是否已存在
    2. 检查指定的ADGroup是否已存在
    3. 找出差异
    4. 更新数据库
    5. 这似乎只是为了插入血腥的关系而做了很多工作。可以编写sql查询来执行insert中的select以确保数据不在那里然后执行插入。如果行已经存在,它将返回0或1

      假设n到n表将被称为Applications_AdGroup

      然后插入关系的查询将是这样的(未经测试):

      Insert Into Applications (AppName)
         select 'CRM'
            where NOT EXISTS(
               select 1
               from Applications
               where AppName = 'CRM'
            )
      
      Insert Into AdGroup (GroupName)
         select 'CRM_ADMIN'
            where NOT EXISTS(
               select 1
               from Applications
               where AppName = 'CRM_ADMIN'
            )
      
      Insert Into Applications_AdGroup (id_adgroup, id_applications)
          select
             adgroup.id,
             applications.id
          from
             adgroup,
             applications
          where
             adgroup.GroupName = 'CRM_ADMIN' AND
             applications.AppName = 'CRM' AND
      
             WHERE NOT EXISTS (
                select 1
                from
                   Applications_Adgroup
                where
                   Applications_AdGroup.id_adgroup = adgroup.id AND
                   Applications_AdGroup.id_applications = applications.id
             )
      

      所以基本上即使应用程序,广告组或关系已经存在,它也不会插入任何内容......

      一天结束时,将对db.Applications.Add(obj)进行简单调用,然后保存更改。所有检查都在后台进行,一切都很顺利?

      对此有何看法?我只是做错了吗?

      修改的 我想可能会出现一些并发问题。在上述4个步骤中的任何一个步骤中,另一个进程可能会插入广告组,无论如何您都会遇到重复的密钥错误

      修改的 这里有映射:

      //Relation between Application and the Parent Group
                  modelBuilder.Entity<Applications>()
                      .HasMany<AdGroup>(s => s.Groups)
                      .WithMany(a => a.Applications)
                      .Map(cs =>
                          {
                              cs.MapLeftKey("Application");
                              cs.MapRightKey("AdGroup");
                              cs.ToTable("Application_Groups");
                          });
      

      修改的 这是我用来插入关系的代码示例:

      使用(var db = new ARContext()) {

      var val = new Applications
      {
          Application = "CRM",
          Groups = new List<AdGroup> {
              new AdGroup { Group = "CRM_ADMIN" },
              new AdGroup { Group = "CRM_Boutique1" },
              new AdGroup { Group = "CRM_Boutique2" },
          }
      };
      
      var dbArr = db.Applications.Where(a => a.Application == val.Application).FirstOrDefault();
      
      if (dbArr == null)
      {
          db.Applications.Add(new Applications() { Application = val.Application });
          db.SaveChanges();
      }
      
      dbArr = db.Applications.Where(a => a.Application == val.Application).FirstOrDefault();
      
      if (dbArr.Groups == null)
      {
          dbArr.Groups = new List<AdGroup>();
      }
      
      foreach (var gr in val.Groups)
      {
          var dbGrp = db.Groups.Where(g => g.Group == gr.Group).FirstOrDefault();
      
          if (dbGrp == null)
          {
              dbArr.Groups.Add(gr);
          }
          else
          {
              dbArr.Groups.Add(dbGrp);
          }
      }
      
      db.SaveChanges();
      

      }

      上面使用的名称和代码示例存在一些差异,但要点就是......

      首先,我需要保存新的应用程序,然后通过各个组来查看它们是否存在并相应地添加到上下文中。除了编写存储过程之外,这是除此之外的唯一方法吗?

      由于

1 个答案:

答案 0 :(得分:1)

这样的事情也应该做到这一点:

using(var db = new ARContext())
{
  var groupNames = new[] {"CRM_ADMIN","CRM_Boutique1","CRM_Boutique2"};

  //Fetch application (AdGroups eager loaded) if it exists, otherwise create a new one
  var application = db.Applications.Include(a => a.AdGroups)
                                   .FirstOrDefault(a => a.Application == "CRM") 
                                   ?? db.Add(new Application { Application = "CRM" });

  //If the application didn't exist yet, initialize the AdGroup collection
  //Better to do this in the constructor of your application model though
  if(db.Entry(application).State == EntityState.Added)
     application.AdGroups = new List<AdGroup>();

  //Iterate over groupNames that are not part of application.Adgroup's collection           
  foreach(var name in groupNames.Where(g => !application.AdGroups.Any(ag => ag.GroupName == g)))
  {
      AdGroup group = db.AdGroups.FirstOrDefault(ag => ag.GroupName == name) 
                      ?? db.Add(new AdGroup { GroupName = name });
      application.AdGroups.Add(group);
  }

  db.SaveChanges();     
}

您的示例有点不寻常,因为您对ApplicationAdGroup或它们之间的关系一无所知,这会导致代码中的大量检查。首先,我从数据库中获取应用程序(如果它存在(包括AdGroupsEager Loading),否则我创建一个新的应用程序。如果我必须创建一个新集合,则应初始化AdGroup集合(除非您在模型的构造函数中执行此操作)。最后,迭代集合Application.AdGroups中不包含的组名。如果该组存在,则获取它,如果它不存在,则创建它并将其添加到Application.AdGroups