流利的Api - M:M与两个1:M属性的关系。怎么样?

时间:2014-07-31 15:22:14

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

我有这个模型。运动员有多项训练和多项训练计划。锻炼可以在许多培训计划中进行。锻炼可以在一天内进行多次,这就是为什么我需要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; }
}

现在使用fluen't 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约束。

我认为我过于复杂化了,完成这种情况的最佳方式是什么?

感谢。

2 个答案:

答案 0 :(得分:1)

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

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);

        } 
    }
}

答案 1 :(得分:0)

我能想到的另一件事是将所有关系转移到一个新的班级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;}
}