MVC Entity Framework modifying child entities

2019-06-13 18:24发布

问题:

I'm quite new to using MVC3 and EF4 and I'm trying to implement the CRUD functions of a parent-child entity set, however I haven't found an example of some specific requirements that I need and therefore would like a bit of help.

The situation I have is that I have a Product entity that has child Category entities. I have all of the CRUD functionality working fine for the Product entity and the detail functionality working with the Category entity within the Product details view. However I need to implement the addition and removal of child Categories from a given Product.

Normally this wouldn't be much of an issue, but in this case when a user adds a Category to a Product I need to only allow the user to be able to select from a list of all available Categories from the database. The user will also be able to remove any of the existing child Categories from a Product.

I expect implementing a DropDownList with all of the unused 'Categories' would work well, however I don't know how to use one to allow the user to add and remove 'Categories' and then save the changes to the database via EF.

Has anyone got any suggestions/examples on how to accomplish this?

If any extra information is required, please ask.

Thanks very much.

回答1:

I have done similar with Authors and Books with many-to-many relation. The basic idea is to create a JSON object from view which contains all authors inside it and submit to controller. I also used jQuery UI TagIt to allow user to add/remove authors associated with the book. When user clicks on 'Save Book' button, the script builds a JSON object which mimics the Book object.

Below is the code. Please make sure you have added "json2.js" and "tagit.js" in the project before you try this code.

View Models:

 public class BookViewModel
    {
        public string Title { get; set; }
        public int BookId { get; set; }
        public int IsAvail { get; set; }
        public string CallNumber { get; set; }
        //Assiged authors
        public List<AuthorViewModel> Authors { get; set; }
        //available authors
        public List<AuthorViewModel> AuthorOptions { get; set; }
     }

    public class AuthorViewModel
    {
        public int AuthorId { get; set; }
        public string FirstName { get; set; }
    }

Code for Book/Edit.chtml:

@using System.Web.Script.Serialization
@model eLibrary.Models.BookViewModel

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</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>

@*This is for JSON*@
<script src="../../Scripts/json2.js" type="text/javascript"></script>
<script src="../../Scripts/tagit.js" type="text/javascript"></script>
 @*These are for styling Control*@
<link href="../../Content/themes/base/jquery.ui.all.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">

    //This function is used for sending data(JSON Data) to BookController
    function BookSave() {
        // Step 1: Read View Data and Create JSON Object

        var author = { "AuthorId": "", "FirstName": "" };
        // Creating book Json Object
        var book = { "BookId": "", "Title": "", "IsAvail": "", "CallNumber":"", "authors": []};

        // Set Boook  Value
        book.BookId = $("#BookId").val();
        book.Title = $("#Title").val();
        book.IsAvail = $("#IsAvail").val();
        book.CallNumber = $("#CallNumber").val()  ;

        var tags = $('#authors').tagit('tags');

        for (var i in tags) {

            author.AuthorId = tags[i].value;
            author.FirstName = tags[i].label;

            book.authors.push(author );

            author = { "AuthorId": "", "FirstName": "" };

        }

        // Step 1: Ends Here
        // Set 2: Ajax Post
        // Here i have used ajax post for saving/updating information
        $.ajax({
            url: '/Book/Edit',
            data: JSON.stringify(book),
            type: 'POST',
            contentType: 'application/json;',
            dataType: 'json',
            success: function (result) {

                if (result.Success == "1") {
                    window.location.href = "/Book/Edit";
                }
                else {
                    alert(result.ex);
                }
            }
        });

    }

    </script>

@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Book Details</legend>

        @Html.HiddenFor(model => model.BookId)

        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.IsAvail)
        </div>
            @Html.EditorFor(model => model.IsAvail)
            @Html.ValidationMessageFor(model => model.IsAvail)

        @Html.EditorFor(model => model.CallNumber);
    </fieldset>

@Html.Partial("AuthorsByBook", Model.Authors, new ViewDataDictionary { { "mode", "EDIT" } })        

    <input type="button" value="Book Save" onclick="BookSave()" />
}

Code for Book/AuthorsByBook.chtml

@model IEnumerable< eLibrary.Models.AuthorViewModel>
<link href="../../Content/tagit-awesome-blue.css" rel="stylesheet" type="text/css" />
     <div class="box">
       <ul id="authors" name="authors">

    </ul>
    </div>
<script type="text/javascript">


//Load authors in the javascript variable
    $(function () {
      var initialAuthorList=[];

      @if(ViewData["mode"]=="EDIT")
{      

   foreach (var category in Model)
   {

        <text>

        initialAuthorList.push({label: "@category.FirstName", value: @category.AuthorId });
        </text>

 }
 }

  $('#authors').tagit({tagSource: function (request, response) {

                    $.ajax({

                        url: "/Author/SearchAuthor", type: "POST", dataType: "json",

                        data: { searchText: request.term, maxResults: 10 },
                        success: function (data) {
                            response($.map(data, function (item) {

                                return { label: item.FirstName, value: item.AuthorId }
                            }))
                        }
                    })
                },

    initialTags:initialAuthorList,minLength:3,allowNewTags:false,sortable:true,delay:400});

    });
    </script>

Code for BookController.cs

public ActionResult Edit(int id)
        {
            //To DO: use repository to fetch data
            Book  book = db.Books.Single(a => a.BookId  == id);
            Mapper.CreateMap<Book, BookViewModel>();
            Mapper.CreateMap<Author, AuthorViewModel>();
            BookViewModel bookVm = Mapper.Map<Book, BookViewModel>(book);
            List<AuthorViewModel> Authors = Mapper.Map<List<Author>,List<AuthorViewModel>>( db.Authors.ToList());
            bookVm.AuthorOptions = Authors;
            return View(bookVm);
        }



[HttpPost]
        public ActionResult Edit(BookViewModel bookv)
        {
            //create maps
            Mapper.CreateMap<AuthorViewModel, Author>();
            Mapper.CreateMap<BookViewModel, Book>();

            //convert view objects to model objects
            Book book = Mapper.Map<BookViewModel, Book>(bookv);
            List<Author> authors = Mapper.Map<List<AuthorViewModel>, List<Author>>(bookv.Authors.ToList());

            //this has to be executed before db.Books.Attach
            //clear authors
            book.Authors.Clear();

            db.Books.Attach(book);

            //assign authors to book
            foreach (Author a in authors) { db.Authors.Attach(a); }

            book.Authors.Clear();

            foreach (Author a in authors) { book.Authors.Add(a); }


        db.ObjectStateManager.ChangeObjectState(book, EntityState.Modified);
        db.SaveChanges();
        return RedirectToAction("Index");

    }

Code for SearchAuthor method in AuthorController.cs

public JsonResult SearchAuthor(string searchText, int? maxResults)
        {
            IEnumerable<Author> query = db.Authors;
            searchText = searchText.ToLower();
            query = query.Where(c => c.FirstName.ToLower().Contains(searchText));

            if ((maxResults ?? 0) == 0)
            {
                return Json(query.ToList());
            }
            else
            {
                return Json(query.Take((int)maxResults).ToList());
            }
        }