【问题标题】:ASP.NET MVC 5 - Scaffolding for Many to One RelationshipASP.NET MVC 5 - 多对一关系的脚手架
【发布时间】:2014-12-09 14:48:55
【问题描述】:

我正在使用 VS 2013 开发 ASP.NET MVC 5、EF 6、Razor 引擎、VB 语言和数据库优先方法。

现在,在我的数据库中;我有两个表如下:

CREATE TABLE [dbo].[Group]
(
    [Id]    INT          NOT NULL PRIMARY KEY IDENTITY(1, 1), 
    [Name]  VARCHAR(50)  NOT NULL
)

CREATE TABLE [dbo].[Subscriber]
(
    [Id]          INT              NOT NULL  PRIMARY KEY IDENTITY(1, 1),
    [FirstName]   [nvarchar](100)  NOT NULL,
    [MiddleName]  [nvarchar](100)  NULL,
    [LastName]    [nvarchar](100)  NOT NULL, 
    [Email]       [varchar] (200)  NOT NULL  UNIQUE, 
    [GroupId]     INT              NULL      REFERENCES [Group] ON DELETE SET NULL
)

现在,当我使用 Scaffolding 自动生成控制器和视图时;我在“创建订阅者”和“编辑订阅者”视图中获得了一个

但实际上,我希望“创建组”和“编辑组”视图询问我要添加到特定组中的 Subscribers。相同的 HTML 控件可以是 checkbox 列表,所有 Subscriber 项目都作为

我怎样才能自动生成/实现它?

【问题讨论】:

    标签: asp.net-mvc entity-framework one-to-many many-to-one asp.net-mvc-scaffolding


    【解决方案1】:

    不要过度依赖脚手架。关键是它为您提供了工作的基础;这不是你的观点的全部。您可以而且应该修改脚手架以满足您的需求,而且老实说,从头开始往往比尝试撤消脚手架添加的所有不必要的绒毛更容易。

    也就是说,尤其是在一次选择多个相关项目时,您需要一个视图模型。尝试为此使用您的实体将很快失去动力。所以创建一个类:

    public class GroupViewModel
    {
        // `Group` properties you need to edit here
    
        public List<int> SelectedSubscriberIds { get; set; }
    
        public IEnumerable<SelectListItem> SubscriberChoices { get; set; }
    }
    

    然后,在您的控制器中:

    // We'll use this code multiple times so it's factored out into it's own method
    private void PopulateSubscriberChoices(GroupViewModel model)
    {
        model.SubscriberChoices = db.Subscribers.Select(m => new SelectListItem
        {
            Value = m.Id.ToString(),
            Text = m.FirstName + " " + m.LastName
        });
    }
    
    public ActionResult Create()
    {
        var model = new GroupViewModel();
    
        PopulateSubscriberChoices(model);
        return View(model);
    }
    
    [HttpPost]
    public ActionResult Create(GroupViewModel model)
    {
        if (ModelState.IsValid)
        {
            // Map the posted values onto a new `Group` instance. To set `Subscribers`,
            // lookup instances from the database using the list of ids the user chose
            var group = new Group
            {
                Name = model.Name,
                Subscribers = db.Subscribers.Where(m => model.SelectedSubscriberIds.Contains(m.Id))
            };
            db.Groups.Add(group);
            db.SaveChanges()
    
            return RedirectToAction("Index");
        }
    
        PopulateSubscriberChoices(model);
        return View(model);
    }
    
    public ActionResult Edit(int id)
    {
        var group = db.Groups.Find(id);
        if (group == null)
        {
            return new HttpNotFoundResult();
        }
    
        // Map `Group` properties to your view model
        var model = new GroupViewModel
        {
            Name = group.Name,
            SelectedSubscriberIds = group.Subscribers.Select(m => m.Id).ToList()
        };
    
        PopulateSubscriberChoices(model);
        return View(model);
    }
    
    [HttpPost]
    public ActionResult Edit(int id, GroupViewModel model)
    {
        var group = db.Groups.Find(id);
        if (group == null)
        {
            return new HttpNotFoundResult();
        }
    
        if (ModelState.IsValid)
        {
            group.Name = model.Name;
    
            // Little bit trickier here
            // First remove subscribers that are no longer selected
            group.Subscribers.Where(m => !model.SelectedSubscriberIds.Contains(m.Id))
                .ToList().ForEach(m => group.Subscribers.Remove(m));
    
            // Now add newly selected subscribers
            var existingSubscriberIds = group.Subscribers.Select(m => m.Id);
            var newSubscriberIds = model.SelectedSubscriberIds.Except(existingSubscriberIds);
            db.Subscribers.Where(m => newSubscriberIds.Contains(m.Id))
                .ToList().ForEach(m => group.Subscribers.Add(m));
    
            db.Entry(group).State = EntityState.Modified;
            db.SaveChanges()
    
            return RedirectToAction("Index");
        }
    
        PopulateSubscriberChoices(model);
        return View(model);
    }
    

    编辑帖子操作是最困难的。为了不出现关于重复键等的错误,您需要确保不要将重复项添加到集合中。您还需要确保删除该组与用户未选择的任何项目之间的关系。除此之外,它非常简单。

    最后在你的视图中,你只需要一个来渲染选择列表:

    @model Namespace.To.GroupViewModel
    
    ...
    
    @Html.ListBoxFor(m => m.SelectedSubscriberIds, Model.SubscriberChoices)
    

    更新

    添加转换后的 VB 代码。这可能无法 100% 开箱即用。任何有更多 VB 经验的人都可以随意编辑此内容以纠正任何问题。

    查看模型

    Public Class GroupViewModel
        ' Group properties you need to edit here
    
        Public Property SelectedSubscriberIds As List(Of Integer)
        Public Property SubscriberChoices As IEnumerable(Of SelectListItem)
    
    End Class
    

    控制器代码

    ' We'll use this code multiple times so it's factored out into it's own method
    Private Sub PopulateSubscriberChoices(model As GroupViewModel)
        model.SubscriberChoices = db.Subscribers.[Select](Function(m) New SelectListItem With { _
            .Value = m.Id, _
            .Text = m.FirstName & " " & m.LastName _
        })
    End Sub
    
    Public Function Create() As ActionResult
        Dim model as New GroupViewModel
        PopulateSubscriberChoices(model)
        Return View(model)
    End Function
    
    <HttpPost> _
    Public Function Create(model As GroupViewModel) As ActionResult
        If ModelState.IsValid Then
            ' Map the posted values onto a new `Group` instance. To set `Subscribers`,
            ' lookup instances from the database using the list of ids the user chose
            Dim group = New Group With { _
                .Name = model.Name, _
                .Subscribers = db.Subscribers.Where(Function(m) model.SelectedSubscriberIds.Contains(m.Id)) _
            }
            db.Groups.Add(group)
            db.SaveChanges()
    
            Return RedirectToAction("Index")
        End If
    
        PopulateSubscriberChoices(model)
        Return View(model)
    End Function
    
    Public Function Edit(id As Integer) As ActionResult
        Dim group = db.Groups.Find(id)
        If group Is Nothing Then
            Return New HttpNotFoundResult()
        End If
    
        ' Map `Group` properties to your view model
        Dim model = New GroupViewModel With { _
            .Name = group.Name, _
            .SelectedSubscriberIds = group.Subscribers.[Select](Function(m) m.Id).ToList _
        }
    
        PopulateSubscriberChoices(model)
        Return View(model)
    End Function
    
    <HttpPost> _
    Public Function Edit(id As Integer, model As GroupViewModel) As ActionResult
        Dim group = db.Groups.Find(id)
        If group Is Nothing Then
            Return New HttpNotFoundResult()
        End If
    
        If ModelState.IsValid Then
            group.Name = model.Name
    
            ' Little bit trickier here
            ' First remove subscribers that are no longer selected
            group.Subscribers.Where(Function(m) Not model.SelectedSubscriberIds.Contains(m.Id)).ToList().ForEach(Function(m) group.Subscribers.Remove(m))
    
            ' Now add newly selected subscribers
            Dim existingSubscriberIds = group.Subscribers.[Select](Function(m) m.Id)
            Dim newSubscriberIds = model.SelectedSubscriberIds.Except(existingSubscriberIds)
            db.Subscribers.Where(Function(m) newSubscriberIds.Contains(m.Id)).ToList().ForEach(Function(m) group.Subscribers.Add(m))
    
            db.Entry(group).State = EntityState.Modified
            db.SaveChanges()
    
            Return RedirectToAction("Index")
        End If
    
        PopulateSubscriberChoices(model)
        Return View(model)
    End Function
    

    【讨论】:

    • 感谢您的帮助...但必须将其转换为 VB 以检查它是否适用于我的代码。我不是 C# 人... :(
    • 对不起。我完全错过了 VB 部分,不过,我不是 VB 人,所以我的回答不可能那么完整。您应该能够使用类似converter.telerik.com
    • 添加了转换后的 VB 代码。如果不进行修改,它可能无法正常工作,但它至少会让你更接近一点。
    猜你喜欢
    • 1970-01-01
    • 2014-10-01
    • 2014-03-29
    • 1970-01-01
    • 2010-10-04
    • 2015-09-12
    • 1970-01-01
    • 1970-01-01
    • 2015-01-08
    相关资源
    最近更新 更多