【问题标题】:Many to Many Relation - Entity Framework多对多关系 - 实体框架
【发布时间】:2018-02-05 02:11:34
【问题描述】:

基本上,我正在处理多对多关系:

+-------------+ +-----------------+ +---------------------------+
|    Redes    | |    Objetivos    | |       RedesObjetivos      |
+-------------+ +-----------------+ +---------------------------+
|   | RedesID | |   | ObjetivosID | |   | RedesID | ObjetivosID |
+---+---------+ +-----------------+ +---------------------------+
| 1 |         | | 1 |             | | 1 |         |             |
+---+---------+ +-----------------+ +---------------------------+

Table Objetivos 是一组先前注册的选项。当我的用户尝试注册新的 Redes 时,他会在 Objetivos 列表中进行选择并保存。

此时 Objetivos 表有 8 个选项

+------------------+
|     Objetivos    |
+------------------+
|    | ObjetivosID |
+----+-------------+
| 1  |      1      |
+----+-------------+
| 2  |      2      |
+----+-------------+
| 3  |      3      |
+----+-------------+
| 4  |      4      |
+----+-------------+
| 5  |      5      |
+----+-------------+
| 6  |      6      |
+----+-------------+
| 7  |      7      |
+----+-------------+
| 8  |      8      |
+----+-------------+

假设我包含一个新的 Redes 并选择 Objetivos 5 到 8 目前的最终结果是:

+-------------+ +-------------------+ +---------------------------+
|    Redes    | |     Objetivos     | |       RedesObjetivos      |
+-------------+ +-------------------+ +---------------------------+
|   | RedesID | |   | ObjetivosID   | |   | RedesID | ObjetivosID |
+---+---------+ +---+---------------+ +---------------------------+
| 1 |    1    | | 1 |      1        | | 1 |    1    |      9      |
+---+---------+ +---+---------------+ +---------------------------+
                | 2 |      2        | | 2 |    1    |      10     |
                +---+---------------+ +---------------------------+
                | 3 |      3        | | 3 |    1    |      11     |
                +---+---------------+ +---------------------------+
                | 4 |      4        | | 4 |    1    |      12     |
                +---+---------------+ +---------------------------+
                | 5 |      5        |
                +---+---------------+
                | 6 |      6        |
                +---+---------------+
                | 7 |      7        |
                +---+---------------+
                | 8 |      8        |
                +---+---------------+
                | 9 |      9        |
                +---+---------------+
                | 10|      10       |
                +---+---------------+
                | 11|      11       |
                +---+---------------+
                | 12|      12       |
                +---+---------------+

我想要的结果是:

+-------------+ +-------------------+ +---------------------------+
|    Redes    | |     Objetivos     | |       RedesObjetivos      |
+-------------+ +-------------------+ +---------------------------+
|   | RedesID | |   |  ObjetivosID  | |   | RedesID | ObjetivosID |
+---+---------+ +---+---------------+ +---------------------------+
| 1 |    1    | | 1 |       1       | | 1 |    1    |      5      |
+---+---------+ +---+---------------+ +---------------------------+
                | 2 |       2       | | 2 |    1    |      6      |
                +---+---------------+ +---------------------------+
                | 3 |       3       | | 3 |    1    |      7      |
                +---+---------------+ +---------------------------+
                | 4 |       4       | | 4 |    1    |      8      |
                +---+---------------+ +---------------------------+
                | 5 |       5       |
                +---+---------------+
                | 6 |       6       |
                +---+---------------+
                | 7 |       7       |
                +---+---------------+
                | 8 |       8       |
                +---+---------------+   

基本上,EF 并没有识别出 Objetivos 已经存在于数据库中,而是在创建新行。如何正确地向 EF 表明那些 Objetivos 已经存在?

我进行了很多关于如何使用 EF 与多对多合作的搜索,我知道有些问题与我的相似,但由于我无法解决我的问题,我决定发布我的全部场景以获得帮助。

这是我目前拥有的:

//DAL Layer

//Interface where a set the basic database methods
public interface IDao<T> where T : class
{
    //Save
    int Salva(T Modelo);
    //Update
    int Atualiza(T model);
    //Delete
    void Exclui(T model);
    //GetAll
    IEnumerable<T> ObtemTodos();
    //GetByID
    T ObtemPorId(object id);
    IEnumerable<T> Where(Expression<Func<T, bool>> expression);
    IEnumerable<T> OrderBy(Expression<System.Func<T, bool>> expression);
}

//Generic context, where i implement the methods in the interface
public partial class dbContext<T> : DbContext where T : class
    {
        public DbSet<T> DbSet
        {
            get;
            set;
        }

        public dbContext()
            : base("name=dbContext")
        {
        }

        public virtual void ChangeObjectState(object model, EntityState state)
        {
            ((IObjectContextAdapter)this)
                            .ObjectContext
                            .ObjectStateManager
                            .ChangeObjectState(model, state);
        }

        //Save
        public virtual int Salva(T model)
        {
            this.DbSet.Add(model);
            return this.SaveChanges();
        }
        //Update
        public virtual int Atualiza(T model)
        {
            var entry = this.Entry(model);

            if (entry.State == EntityState.Detached)
                this.DbSet.Attach(model);

            this.ChangeObjectState(model, EntityState.Modified);
            return this.SaveChanges();
        }
        //Delete
        public virtual void Exclui(T model)
        {
            var entry = this.Entry(model);

            if (entry.State == EntityState.Detached)
                this.DbSet.Attach(model);

            this.ChangeObjectState(model, EntityState.Deleted);
            this.SaveChanges();
        }
        //GetAll
        public virtual IEnumerable<T> ObtemTodos()
        {
            return this.DbSet.ToList();
        }
        //GetByID
        public virtual T ObtemPorId(object id)
        {
            return this.DbSet.Find(id);
        }

        public virtual IEnumerable<T> Where(Expression<Func<T, bool>> expression)
        {
            return this.DbSet.Where(expression);
        }

        public IEnumerable<T> OrderBy(Expression<Func<T, bool>> expression)
        {
            return this.DbSet.OrderBy(expression);
        }
    }

//Then the relevant DAO classes where I implement specific database methods if necessary
public class RedesDAO : dbContext<Redes>, IDao<Redes>
    {
        //Save
        public override int Salva(Redes Model)
        {
            ObjetivosDAO ObjetivosDAO = new ObjetivosDAO();
            Model.RedeObjetivos = new List<Objetivos>();

            //ObjetivosSelecionados is a List<int> with Ids that relates to user's selected options on my view
            //each option relates to an especific Objetivos row that already exist in the database
            foreach (int ObjetivoID in Model.ObjetivosSelecionados)
            {
                //So, with each ID I obtain the whole information for that entity
                //by using the ObtemPorId = GetByID method and adding it to models' collection
                Objetivos Objetivo = ObjetivosDAO.ObtemPorId(ObjetivoID);
                Model.RedeObjetivos.Add(Objetivo);                
            }

            this.DbSet.Add(Model);
            return this.SaveChanges();
        }        

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Redes>().
              HasMany(c => c.RedeObjetivos).
              WithMany(p => p.Redes).
              Map(
               m =>
               {
                   m.MapLeftKey("RedeID");
                   m.MapRightKey("ObjetivoID");
                   m.ToTable("RedesObjetivos");
               });
        }
    }

public class ObjetivosDAO : dbContext<Objetivos>, IDao<Objetivos>
    {
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Objetivos>().
              HasMany(c => c.Redes).
              WithMany(p => p.RedeObjetivos).
              Map(
               m =>
               {
                   m.MapLeftKey("ObjetivoID");
                   m.MapRightKey("RedeID");
                   m.ToTable("RedesObjetivos");
               });
        }
    }   

//And my models/tables
public partial class Redes
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int RedeID { get; set; }
        [Required]
        public ICollection<Objetivos> RedeObjetivos { get; set; }
        [NotMapped]
        public List<int> ObjetivosSelecionados { get; set; }        
    }

public partial class Objetivos
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int ObjetivoID { get; set; }
        [Required]
        public ICollection<Redes> Redes { get; set; }
    }

请注意,我为保存新的 Redes 执行的方法是 RedesDAO 类上的覆盖。

//Model = Information that comes from my view
public override int Salva(Redes Model)

【问题讨论】:

  • 难以阅读,但我认为dbContext&lt;T&gt; 意味着您有多个上下文和更改跟踪器,这会导致现有对象被视为新对象。您的存储库模式实现已损坏。

标签: c# entity-framework entity-framework-6


【解决方案1】:

您指定了 2 个不同的 DbContext,RedesDAOObjetivosDAO。 EntityFramework 的上下文对于两个对象是不同的,这意味着您的第二个上下文看不到该对象是从数据库中获取的。

要修复,您可以替换以下行:

Objetivos Objetivo = ObjetivosDAO.ObtemPorId(ObjetivoID);

与:

Objetivos Objetivo = base.Set<Objetivos>().Find(ObjetivoID);

这应该可以防止对象被误认为是新的。


作为旁注,除非您有特定的理由将它们分开,否则最好使用单个 DbContext 类与您的数据库进行通信。

【讨论】:

  • 感谢旁注。我想我误解了一些关于 EF 的事情。我将重新评估我的数据结构以避免这些情况。
猜你喜欢
  • 2017-01-28
  • 2011-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-13
  • 1970-01-01
相关资源
最近更新 更多