【问题标题】:Fluent Api - M:M relationship with two 1:M properties. How?Fluent Api - 具有两个 1:M 属性的 M:M 关系。如何?
【发布时间】:2014-09-23 15:09:39
【问题描述】:

我有这个模型。一名运动员有多项锻炼和多项训练计划。锻炼可以在许多训练计划中。一天可以进行多次锻炼,这就是为什么我需要 M:M 关系中的额外字段 DateToPerform 。我该如何完成它,这是我的模型:

public class Athlete
{
    public int Id {get; set;}
    public string Name {get; set;}
    public virtual List<Workout> Workouts {get; set;}
    public virtual List<TrainingPlan> TrainingPlans {get; set;}
}

public class Workout
{
    public int Id {get; set;}
    public string Name {get; set;}
    public virtual Athlete Athlete {get; set;}  //Athlete owner of the WO.
    public virtual List<TrainingPlan> TrainingPlans {get; set;} //Plans where WO is.
}

public class TrainingPlan
{
    public int Id {get; set;}
    public string Name {get; set;}
    public virtual Athlete Athlete {get; set;}  //Athlete owner of this TP.
    public virtual List<Workout> Workouts {get; set;} //Workouts in this plan.
}

因为我需要额外的字段,所以我在 SO 中读到我应该向 Entity 推广这种关系,这就是我结束这样做的方式:

public class TrainingPlanWorkout
{
        public int Id {get; set;}
        public virtual TrainingPlan TrainingPlan { get; set; }
        public virtual Workout Workout { get; set; }
        public DateTime DateToPerform { get; set; }
}

现在使用流利的 API 我这样定义所有这些:

//I define primary keys.
modelBuilder.Entity<Athlete>().HasKey(x => x.Id);
modelBuilder.Entity<TrainingPlan>().HasKey(x => x.Id);
modelBuilder.Entity<Workout>().HasKey(x => x.Id);
modelBuilder.Entity<TrainingPlanWorkout>().HasKey(x => x.Id);

modelBuilder.Entity<Athlete>().HasMany(a => a.AthleteTrainingPlans)
                              .WithRequired(t=>t.Athlete)
                              .WillCascadeOnDelete(true);

modelBuilder.Entity<Athlete>().HasMany(m => m.AthleteWorkouts)
                              .WithRequired(m=>m.Athlete)
                              .WillCascadeOnDelete(true);


 modelBuilder.Entity<TrainingPlanWorkout>().HasRequired(t => t.TrainingPlan);
 modelBuilder.Entity<TrainingPlanWorkout>().HasRequired(t => t.Workout);

当我运行应用程序时,我收到以下错误:

引入 FOREIGN KEY 约束 'FK_dbo.TrainingPlanWorkouts_dbo.Workouts_Workout_Id' 在桌子上 “TrainingPlanWorkouts”可能会导致循环或多个级联路径。 指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。

我认为我把事情复杂化了,完成这种情况的最佳方法是什么?

谢谢。

【问题讨论】:

  • 我不知道实体框架,也不懂 C#。但是为什么锻炼必须知道,哪个运动员正在使用它?可以在没有训练计划的情况下进行锻炼,还是它们始终是训练计划的一部分?如果可能的话,我会尝试摆脱一些参考。这应该会删除一些外键约束,也许可以解决问题。
  • 该门户将有很多运动员,因此会有很多训练,我需要知道所有者,因为运动员会创建他们的训练。训练包含在训练计划中。
  • 运动员之间是否共享锻炼/训练计划?
  • 如果@Onur 问题的答案是否定的,那么它可能只是一个简单的一对多关系
  • 答案是否定的。但我不明白发生了什么变化

标签: c# asp.net-mvc entity-framework ef-code-first ef-fluent-api


【解决方案1】:

我使用以下代码实现了您的问题,主要区别在于关系实体的模型构建器。

namespace ConsoleApplication1
{
    class Program
    {
        public class Athlete
        {
            public int Id { get; set; }
            public string Name { get; set; }

            private ICollection<Workout> workouts;
            public virtual ICollection<Workout> Workouts
            {
                get { return workouts ?? (workouts = new HashSet<Workout>()); }
                set { workouts = value; }
            }

            private ICollection<TrainingPlan> trainingPlans;
            public virtual ICollection<TrainingPlan> TrainingPlans
            {
                get { return trainingPlans ?? (trainingPlans = new HashSet<TrainingPlan>()); }
                set { trainingPlans = value; }
            }
        }

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

            public DateTime DateToPerform { get; set; }

            public virtual TrainingPlan TrainingPlan { get; set; }
            public virtual Workout Workout { get; set; }
        }

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

            public virtual Athlete Athlete { get; set; }

            private ICollection<TrainingToPerform> trainingsToPerform;
            public virtual ICollection<TrainingToPerform> TrainingsToPerform
            {
                get { return trainingsToPerform ?? (trainingsToPerform = new HashSet<TrainingToPerform>()); }
                set { trainingsToPerform = value; }
            }
        }

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

            public virtual Athlete Athlete { get; set; }

            private ICollection<TrainingToPerform> trainingsToPerform;
            public virtual ICollection<TrainingToPerform> TrainingsToPerform
            {
                get { return trainingsToPerform ?? (trainingsToPerform = new HashSet<TrainingToPerform>()); }
                set { trainingsToPerform = value; }
            }
        }


        public class Db3 : DbContext
        {
            public Db3()
            {

            }

            public DbSet<Athlete> Athletes { get; set; }
            public DbSet<Workout> Workouts { get; set; }
            public DbSet<TrainingPlan> TrainingPlans { get; set; }
            public DbSet<TrainingToPerform> TrainingsToPerform { get; set; }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);

                modelBuilder.Entity<TrainingPlan>()
                    .HasKey(x => x.Id);

                modelBuilder.Entity<Workout>()
                    .HasKey(x => x.Id);

                modelBuilder.Entity<Athlete>()
                    .HasKey(x => x.Id);

                modelBuilder.Entity<Athlete>()
                    .HasMany(a => a.TrainingPlans)
                    .WithRequired(t => t.Athlete)
                    .WillCascadeOnDelete(true)
                    ;

                modelBuilder.Entity<Athlete>()
                    .HasMany(a => a.Workouts)
                    .WithRequired(t => t.Athlete)
                    .WillCascadeOnDelete(true)
                    ;

                modelBuilder.Entity<TrainingToPerform>()
                    .HasKey(x => x.Id);

                modelBuilder.Entity<TrainingToPerform>()
                    .HasRequired(t => t.Workout)
                    .WithOptional()
                    .WillCascadeOnDelete(false)
                    ;

                modelBuilder.Entity<TrainingToPerform>()
                    .HasRequired(t => t.TrainingPlan)
                    .WithOptional()
                    .WillCascadeOnDelete(false)
                    ;         
            }
        }

        static void Main(string[] args)
        {

            var db = new Db3();

            var a = new Athlete { Name = "a6"};
            var w = new Workout { Name = "w6", Athlete = a };
            var t = new TrainingPlan { Name = "t6", Athlete = a };
            db.Athletes.Add(a);
            db.Workouts.Add(w);
            db.TrainingPlans.Add(t);
            db.SaveChanges();

            var wtp = new TrainingToPerform { TrainingPlan = t, Workout = w, DateToPerform = DateTime.Now };

            w.TrainingsToPerform.Add(wtp);
            t.TrainingsToPerform.Add(wtp);


            db.TrainingsToPerform.Add(wtp);

            db.SaveChanges();

            Console.WriteLine(db.TrainingsToPerform.First().Workout.Name);

        } 
    }
}

【讨论】:

  • 谢谢!让我试试,我会告诉你的:D
  • 我尝试了你的方法,但是当我看到创建的表格 TrainingToPerforms 时,我看不到任何对锻炼和训练计划的 FK 的引用......没有列可以保存这些值。跨度>
  • 我离开了这个,现在工作正常:modelBuilder.Entity() .HasRequired(t => t.Workout); modelBuilder.Entity() .HasRequired(t => t.TrainingPlan);
【解决方案2】:

我能想到的另一件事是将所有关系转移到一个新类ATrainingAthlete 中,该类仅将运动员与其锻炼和训练计划结合在一起。

public class Athlete
{
    public int Id {get; set;}
    public string Name {get; set;}
    //public virtual List<Workout> Workouts {get; set;}
    //public virtual List<TrainingPlan> TrainingPlans {get; set;}
}

public class Workout
{
    public int Id {get; set;}
    public string Name {get; set;}
    //public virtual Athlete Athlete {get; set;}  //Athlete owner of the WO.
    //public virtual List<TrainingPlan> TrainingPlans {get; set;} //Plans where WO is.
}

public class TrainingPlan
{
    public int Id {get; set;}
    public string Name {get; set;}
    //public virtual Athlete Athlete {get; set;}  //Athlete owner of this TP.
    //public virtual List<Workout> Workouts {get; set;} //Workouts in this plan.
}

public class ATrainingAthlete
{
    public int Id {get; set;} 
    // connect the athlete
    public virtual Athlete Athlete {get; set;}
    // with its workouts
    public virtual List<Workout> Workouts {get; set;}
    // and training plans
    public virtual List<TrainingPlan> TrainingPlans {get; set;}
}

【讨论】:

  • 我会等待更多的答案,因为你提到你只知道 C# 而不会 Fluet api / EF。这个问题是关于那个的。
  • 当然。但在许多情况下,通过更改模型而不是简单地尝试使其运行,可以更好地解决问题......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-04
  • 1970-01-01
  • 2017-07-22
  • 1970-01-01
  • 2013-11-20
  • 2016-09-06
  • 1970-01-01
相关资源
最近更新 更多