Rails 在 ASP.NET MVC3 中具有多通等效性

发布于 2024-12-21 09:19:29 字数 626 浏览 2 评论 0原文

在 .NET 实体框架中,拥有带有额外属性(id 除外)的(自定义)联接表和/或通过单独的模型将此联接表与其他联接表关联的最佳方法是什么?在 Ruby on Rails 中,我们可以有一个连接表模型,例如:

Item.rb (model)
:has_many => :buyers, :through=>:invoice
...

Buyers.rb (model)
:has_many => :items, :through=>:invoice
...

Invoice.rb (model)
:belongs_to :item
:belongs_to :buyer
....

然后我们可以使用: Item.first.buyersBuyers.first.itemsBuyer.create(:items=>Item.create(:name=>'random')) 等,就像我们使用没有模型的自动连接表(使用 has_and_belongs_to_many)一样。

在 Visual Studio 2010 的“添加关联”对话框中,如果我们选择多重性为 *(Many),则没有选择连接表(带模型)的选项。有没有办法手动完成?

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 model? In Ruby on Rails we can have a model for the join table, like:

Item.rb (model)
:has_many => :buyers, :through=>:invoice
...

Buyers.rb (model)
:has_many => :items, :through=>:invoice
...

Invoice.rb (model)
:belongs_to :item
:belongs_to :buyer
....

Then we can use: Item.first.buyers, Buyers.first.items and Buyer.create(:items=>Item.create(:name=>'random')) etc. just like when we use automated join table without model (using has_and_belongs_to_many).

In Visual Studio 2010's "Add Association" dialog, if we select multiplicity as *(Many) there is no option to select a join table (with model). Is there a way to do it manually?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

再浓的妆也掩不了殇 2024-12-28 09:19:30

是的,你可以获得非常接近的东西。我不太确定如何在设计器中进行设置,因为我只使用代码优先。

这是一个例子:

学生 -> 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);
        }
    }
}

链接表:

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

}

最后,多对多的另一边:

public class Floor
{
    public int Id { get; set; }
    // ... Other properties.

    public virtual ICollection<StudentFloor> StudentFloors { get; set; }
}

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; }
}
远昼 2024-12-28 09:19:30

更新 Leniency 的答案:

我们还可以使用模型优先方法创建两个一对多关系。无论哪种方式,我们都无法像纯 M2M 关系中那样进行模型绑定(没有负载或纯连接表 - PJT)。

此外,在(脚手架)控制器中,我们可以根据要求使用视图模型进行 CRUD 操作。假设我们有一个具有以下定义的 FloorViewModel:

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

}

控制器中的创建操作将是:

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

编辑操作将类似于:

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

在视图中,我们可以发送 FloorModelView 的对象,如:

@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>

最后, _CreateOrEdit 部分如下所示:

@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>

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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文