Rails has-many-through equivalent in ASP.NET MVC3

前端 未结 2 1474
借酒劲吻你
借酒劲吻你 2020-12-18 00:31

In .NET Entity Framework, what is the best way to have a (custom) join table with extra attributes (other than ids) and/or associate this join table with others via separate

相关标签:
2条回答
  • 2020-12-18 01:20

    Yes, you can get something pretty close. I'm not quite sure how to set this up in the designer since I only work with codefirst.

    Here's an example:

    Student -> StudentFloor <- Floor

    public class Student
    {
        public int Id { get; set; }
        // ... properties ...
    
        // Navigation property to your link table
        public virtual ICollection<StudentFloor> StudentFloors { get; set; }
    
        // If you wanted to have a property direct to the floors, just add this:
        public IEnumerable<Floor> Floors
        {
            get
            {
                return StudentFloors.Select(ft => ft.Floor);
            }
        }
    }
    

    The linking table:

    public class StudentFloor
    {
        #region Composite Keys
    
        // Be sure to set the column order and key attributes.
        // Convention will link them to the navigation properties
        // below.  The database table will be created with a
        // compound key.
    
        [Key, Column(Order = 0)]
        public int StudentId { get; set; }
    
        [Key, Column(Order = 1)]
        public int FloorId { get; set; }
    
        #endregion
    
        // Here's the custom data stored in the link table
    
        [Required, StringLength(30)]
        public string Room { get; set; }
    
        [Required]
        public DateTime Checkin { get; set; }
    
        // Navigation properties to the outer tables
        [Required]
        public virtual Student Student { get; set; }
    
        [Required]
        public virtual Floor Floor { get; set; }
    
    }
    

    Finally, the other side of the many-to-many:

    public class Floor
    {
        public int Id { get; set; }
        // ... Other properties.
    
        public virtual ICollection<StudentFloor> StudentFloors { get; set; }
    }
    
    0 讨论(0)
  • 2020-12-18 01:31

    UPDATE to Leniency's answer:

    We can also create two one-to-many relationships with Model first approach. Either way we cannot have model binding as happen in pure M2M relationship (without payload or pure join tables - PJT).

    Also, in (scaffold) controller, we can use a view model for the CRUD operations as per the requirement. Supposedly, we have a FloorViewModel with the following definition:

    public class FloorViewModel
    {
        private Model2Container context = new Model2Container();
    
        [Display(Name = "Student List")]
        [Required(ErrorMessage = "Please select atleast one student from the list.")]
        public int[] MyStudents { get; set; }
    
        public Floor MyFloor { get; set; }
    
        public MultiSelectList StudentsList { get; set; }
    
        public StudentFloorJoin Join { get; set; }
    
    }
    

    The create action in controller would be:

    //
    // GET: /Floor/Create
    
    public ActionResult Create()
    {
        var model = new FloorViewModel() { StudentsList = new MultiSelectList(context.Students, "Id", "Name") };
        return View(model);
    }
    
    //
    // POST: /Floor/Create
    
    [HttpPost]
    public ActionResult Create(FloorViewModel floor)
    {
        if (ModelState.IsValid)
        {
            context.Floors.Add(floor.MyFloor);
            context.SaveChanges();
        }
        foreach (var student in floor.MyStudents)
        {
            context.StudentFloorJoins.Add(new StudentFloorJoin() { Student = context.Students.Find(student), Floor = floor.MyFloor, Room = floor.Join.Room });
        }
        if (ModelState.IsValid)
        {
            context.SaveChanges();
            return RedirectToAction("Index");
        }
        context.Floors.Remove(floor.MyFloor);
        floor.StudentsList = new MultiSelectList(context.Students, "Id", "Name", floor.MyStudents);
        return View(floor);
    }
    

    and the Edit action would be something like:

    //
    // GET: /Floor/Edit
    
    public ActionResult Edit(int id)
    {
        Floor floor = context.Floors.Single(x => x.Id == id);
        int[] ids = (from i in floor.StudentFloorJoins select i.Student.Id).ToArray();
        var model = new FloorViewModel() { StudentsList = new MultiSelectList(context.Students, "Id", "Name", ids), MyFloor = floor, Join = new StudentFloorJoin() { Room = floor.StudentFloorJoins.Count == 0 ? "" : floor.StudentFloorJoins.First().Room } };
        return View(model);
    }
    
    //
    // POST: /Floor/Edit
    
    [HttpPost]
    public ActionResult Edit(FloorViewModel floor)
    {
        if (ModelState.IsValid)
        {
            var item = floor.MyFloor;
            var itemEntry1 = context.Entry<Floor>(item);
            itemEntry1.State = EntityState.Modified;
            var query = (from i in context.StudentFloorJoins where i.Floor.Id == item.Id select i.Id);
            foreach (var studentfloor in query)
            {
                context.StudentFloorJoins.Remove(context.StudentFloorJoins.Find(studentfloor));
            }
            context.SaveChanges();
    
            foreach (var student in floor.MyStudents)
            {
                context.StudentFloorJoins.Add(new StudentFloorJoin() { Student = context.Students.Find(student), Floor = floor.MyFloor, Room = floor.Join.Room });
            }
            context.SaveChanges();
            return RedirectToAction("Index");
        }
        floor.StudentsList = new MultiSelectList(context.Students, "Id", "Name", floor.MyStudents);
        return View(floor);
    }
    

    In View, we can send the FloorModelView's object like:

    @model ManyToManyAutoGen.Models.FloorViewModel
    
    @{
        ViewBag.Title = "Create";
    }
    
    <h2>Create</h2>
    
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
    
    @using (Html.BeginForm()) {
        @Html.ValidationSummary(true)
        <fieldset>
            <legend>Floor</legend>
    
            @Html.Partial("_CreateOrEdit", Model)
    
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>
    }
    
    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>
    

    and finally, _CreateOrEdit partial looks like:

    @model ManyToManyAutoGen.Models.FloorViewModel
    
    @* This partial view defines form fields that will appear when creating and editing entities *@
    
    <div class="editor-label">
        @Html.LabelFor(model => model.MyFloor.FloorName)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.MyFloor.FloorName)
        @Html.ValidationMessageFor(model => model.MyFloor.FloorName)
    </div>
    
    <div class="editor-label">
        @Html.LabelFor(model => model.MyStudents)
    </div>
    <div class="editor-field">
        @Html.ListBoxFor(model => model.MyStudents, Model.StudentsList) 
        @Html.ValidationMessageFor(model => model.MyStudents)
    </div>
    
    <div class="editor-label">
        @Html.LabelFor(model => model.Join.First().Room)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Join.First().Room)
        @Html.ValidationMessageFor(model => model.Join)
    </div>
    
    0 讨论(0)
提交回复
热议问题