简体   繁体   English

如何在ASP.NET MVC 5中通过视图创建/编辑模型

[英]How to create/edit models via view in asp.net mvc 5

I got one question related to my model you can see in the picture below. 我有一个与我的模型有关的问题,您可以在下图中看到。

在此处输入图片说明

As you can see I got 3 entities and 1:n and m:n relations between them. 如您所见,我得到了3个实体以及它们之间的1:n和m:n关系。

I want that I can edit these models through a web interface. 我希望我可以通过Web界面编辑这些模型。 Therefore I scaffold (add controller with entity framework) these three models and got edit/delete/create/ views and of course one controller for each entity. 因此,我搭建了这三个模型(在实体框架中添加了控制器),并获得了edit/delete/create/视图,当然每个实体都有一个控制器。

But there is no input/fields created for the relations automatically by VS. 但是,VS没有为关系自动创建任何输入/字段。 So I thought to implement them manually. 所以我想手动实现它们。 Before I want to do that is there an simpler way to implement/scaffold this model, so I can even edit the relations(Checkboxes or (multi)select would be the best)? 在执行此操作之前,有一种更简单的方法来实现/构建此模型,因此,我什至可以编辑关系(复选框或(multi)select最好)? Thanks in advance! 提前致谢!

对于很多人,您可以在Partner View中使用DropDownList作为Tip的提示(请参阅Scott Allen的解决方案 。许多人可以由ViewModels和JavaScript框架(如Knockout)处理。

No, the scaffolds are intentionally unopinionated here, as there's many different ways you could handle this. 不,在这里,脚手架是有意进行的,因为您可以通过多种方法来处理。 Perhaps you just want to choose from a select list? 也许您只想从选择列表中进行选择? Maybe you want checkboxes, instead? 也许您想要复选框? Or, maybe you want to actually add/edit related items inline? 或者,也许您想实际内联添加/编辑相关项目? And with that last one, would you like to post all at once or use AJAX? 最后,您想一次发布所有内容还是使用AJAX?

So, instead of picking for you, the framework rightly leaves the decision up to you, since only you know how your application should be built. 因此,由于只有您知道应如何构建应用程序,因此该框架不会由您来决定,而是由您自行决定。 Regardless, relying on the scaffolds is going to bite you more often than not. 无论如何,依靠脚手架会经常咬你。 They only work in the most basic and ideal scenarios, and when have application requirements ever been either basic or ideal? 它们仅在最基本和理想的情况下工作,并且应用程序需求何时达到基本或理想状态? I don't even bother with them at this point, preferring to just create my controllers/views manually. 在这一点上,我什至都不用理会它们,而是只想手动创建我的控制器/视图。 It ends up being quicker than dealing with the scaffold and undoing all the things that aren't applicable. 它最终比处理脚手架和撤消所有不适用的事情更快。

So, since you're looking for select boxes (either single-select or multi-select), first, I'd recommend creating view models for your entities. 因此,由于您要查找选择框(单选或多选),因此,我建议为您的实体创建视图模型。 For example, with Tip : 例如,使用Tip

public class TipViewModel
{
    [Required]
    public string Name { get; set; }

    [Required]
    [DataType(DataType.MultilineText)]
    public string Description { get; set; }

    [Required]
    public int? SelectedPartnerId { get; set; }
    public IEnumerable<SelectListItem> PartnerChoices { get; set;}

    [Required]
    public int? SelectedBookId { get; set; }
    public IEnumerable<SelectListItem> BookChoices { get; set; }
}

Here, I've added nullable int (using a nullable allows them to be initially unselected, instead of just set to the first option) properties to track the id of the selected Book / Partner because it doesn't appear you have explicit properties on your entities for the foreign keys. 在这里,我添加了可为null的int属性(使用可为null的属性可使它们最初不被选择,而不仅仅是将其设置为第一个选项)属性来跟踪所选Book / Partner Book的ID,因为它似乎没有显示您具有您的实体的外键。 That's fine, but it doesn't make it slightly more complicated to save the relationship, as you'll see in a bit. 很好,但是保存关系并没有变得稍微复杂,正如您将看到的那样。 If you did have explicit foreign key properties, then you should mirror those in your view models instead. 如果确实具有显式外键属性,则应在视图模型中镜像那些外键属性。

Now in the GET version of your action, you'll need to do something like the following: 现在,在操作的GET版本中,您需要执行以下操作:

public ActionResult Create()
{
    var model = new TipViewModel();

    PopulateChoices(model);

    return View(model);
}

...

protected void PopulateChoices(TipViewModel model)
{
    model.PartnerChoices = db.Partners.Select(m => new SelectListItem
    {
        Value = m.Id.ToString(),
        Text = m.Name
    });
    model.BookChoices = db.Books.Select(m => new SelectListItem
    {
        Value = m.Id.ToString(),
        Text = string.Format("{0} by {1}", m.Name, m.Author)
    });
}

I've abstracted out the code for populating these select lists because the code will be used multiple times throughout your controller. 我已经提取了用于填充这些选择列表的代码,因为该代码将在整个控制器中多次使用。 Also, I used string.Format on the Text value for the books just to show that you can do whatever you want with the text for the select list item. 另外,我在书籍的“ Text值上使用了string.Format ,只是为了表明您可以对选择列表项的文本执行任何操作。 Also, the code above would be for a create action, obviously. 同样,显然,以上代码将用于create动作。 Doing an edit would be similar but slightly different: 进行编辑将类似但略有不同:

public ActionResult Edit(int id)
{
    var tip = db.Tips.Find(id);
    if (tip == null)
    {
        return new HttpNotFoundResult();
    }

    var model = new TipViewModel
    {
        Name = tip.Name,
        Description = tip.Description,
        SelectedPartnerId = tip.Partner != null ? tip.Partner.Id : new int?(),
        SelectedBookId = tip.Book != null ? tip.Book.Id : new int?()
    }

    PopulateChoices(model);

    return View(model);
}

The main difference is that you're obviously dealing with an existing instance so you need to pull it from the database. 主要区别在于您显然正在处理现有实例,因此需要从数据库中将其提取。 Then, you just need to map the data from your entity onto your view model. 然后,您只需要将数据从实体映射到视图模型即可。 Since, again, you don't have explicit foreign key properties, you have to do a little extra leg work to get the currently chosen Partner / Book values, otherwise you could just copy the values for the foreign key properties over directly. 同样,由于您没有显式的外键属性,因此您需要做一些额外的工作才能获得当前选择的“ Partner / Book值,否则您可以直接复制外键属性的值。 Also, here, I'm just doing a manual mapping, but there's third-party libraries to make this task easier (see: AutoMapper ). 另外,在这里,我只是在进行手动映射,但是有第三方库使此任务更容易(请参阅: AutoMapper )。

With that, you can implement your views. 这样,您可以实现您的视图。 Everything will work the same as it did when you were using the entity directly, you just need to make a couple of modifications. 一切都将与您直接使用实体时的工作方式相同,您只需要进行一些修改即可。 First, you'll need to change your view's model declaration: 首先,您需要更改视图的模型声明:

@model Namespace.To.TipViewModel

Then, add the select lists for your two related properties: 然后,为您的两个相关属性添加选择列表:

@Html.DropDownListFor(m => m.SelectedPartnerId, Model.PartnerChoices)

...

@Html.DropDownListFor(m => m.SelectedBookId, Model.BookChoices)

The fun happens in the POST version of your actions. 有趣的事情发生在您的操作的POST版本中。 Most of the code will stay the same from the GET version, but now you'll have an if (ModelState.IsValid) block: 大部分代码与GET版本中的代码相同,但是现在您有了if (ModelState.IsValid)块:

[HttpPost]
public ActionResult Create(TipViewModel model)
{
    if (ModelState.IsValid)
    {
        // map the data from model to your entity
        var tip = new Tip
        {
            Name = model.Name,
            Description = model.Description,
            Partner = db.Partners.Find(model.SelectedPartnerId),
            Book = db.Books.Find(model.SelectedBookId)
        }

        db.Tips.Add(tip);
        db.SaveChanges();

        return RedirectToAction("Index");
    }

    // Form has errors, repopulate choices and redisplay form

    PopulateChoices(model);

    return View(model);
}

The edit version, again, is similar, except you're going to map onto you existing instance, for example: 再次,编辑版本是相似的,除了要映射到现有实例上,例如:

tip.Name = model.Name;
tip.Description = model.Description;
tip.Partner = db.Partners.Find(model.SelectedPartnerId);
tip.Book = db.Books.Find(model.SelectedBookId);

That's all there is to it for reference properties. 这就是参考属性的全部内容。 You don't actually have any thing that's M2M or even one-to-many on your entities in your question. 您在问题中的实体上实际上没有M2M甚至一对多的东西。 Everything is one-to-one, but if you did have a collection property, you'd need to handle it slightly differently. 一切都是一对一的,但是如果您确实具有collection属性,则需要稍微不同地处理它。 You still need a property on your view model to hold the selected values and the available choices: 在视图模型上仍然需要一个属性来保存选定的值和可用的选择:

public List<int> SelectedFooIds { get; set; }
public IEnumerable<SelectListItem> FooChoices { get; set; }

Populating the choices would also be the same. 填充选择也将是相同的。 The options are the options; 选项是选项; it doesn't matter if you're select just one or many as far as that is concerned. 无论您选择的是一个还是多个,都没有关系。

Mapping onto your entity in your create action would be different though, as you'd need to select all of the chosen items from the database and set your collection property on your entity to that: 但是,在您的create动作中映射到您的实体会有所不同,因为您需要从数据库中选择所有选定的项目,并将您实体上的collection属性设置为:

var tip = new Tip
{
    ...
    Foos = db.Foos.Where(m => model.SelectedFooIds.Contains(m.Id)),
}

And, you'd need to make changes to both the GET and POST versions of your edit action. 并且,您需要同时更改编辑操作的GET和POST版本。 For the GET, you need to condense your collection property down to a list of ids: 对于GET,您需要将您的collection属性压缩为一个id列表:

var model = new TipViewModel
{
    ...
    SelectedFooIds = tip.Foos.Select(m => m.Id).ToList(),
}

And in the edit version, you set new selected items: 在编辑版本中,您可以设置新的选定项目:

tip.Foos = db.Foos.Where(m => model.SelectedFooIds.Contains(m.Id);

Finally, in your views, you'd use ListBoxFor instead of DropDownListFor to enable the multiselect: 最后,在您的视图中,将使用ListBoxFor而不是DropDownListFor来启用多选:

@Html.ListBoxFor(m => m.SelectedFooIds, Model.FooChoices)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM