简体   繁体   中英

Updating related data using MVC 4 and Entity Framework?

So, I have a problem in save data which contains related entities, when I save it a new relation blank is created.

Exemple:

Entities:

public class Project
{
    public int Id { get; set; }
    public int Code{ get; set; }
    public string Description{ get; set; }

    public virtual Client Client { get; set; }
}


public class Client
{
    public int Id { get; set; }
    public int Code { get; set; }
    public string Name { get; set; }
}

The Controller GET:

public ActionResult Create()
{
    PopulateDropDownClienteList(String.Empty); //Returns to ViewBag to create a combobox .in view
    return View();
}

The View:

 @Html.DropDownListFor(m => m.Client.Id, new SelectList(ViewBag.Client_Id, "Id", "Name"), new { Name = "Client.Id" });

The Controller POST:

[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(string command, Project project)
    {
        try
        {
            if (ModelState.IsValid)
            {
                projectRepository = new ProjeRepository();
                Project pro = projectRepository.ReturnByCode(project.Code);

                if (pro == null)
                    projectRepository.Save(project);
                else
                    projectRepository.Update(project);

                PopulateDropDownClienteList(String.Empty);

                Return View();

            }
            else
            {
                return View(project);
            }
        }
        catch (Exception ex)
        {
            return View();
        }
    }

So when I save the data, the client is not associated with the project. just creating a new blank Client.

You've got a number of issues here, so let me break them down.

First and foremost, do not ever catch Exception (at least without throwing it again). There's two very important things about using try...catch blocks: you should only wrap the code where you're expecting an exception (not nearly your entire method as you've done here), and you should only catch the specific exception you're expecting (not the base type Exception ). When you catch Exception , any and every exception that could possibly be generated from your code will be caught, and in this case, simply discarded, which means you really will never know if this code works at all.

Second, you have a fine method that generates a dropdown list of choices, but never store the user's selection anywhere meaningful. To understand why, you need to stop and think about what's happening here. An HTML select element has a string value and a string text or label component. It does not support passing full objects back and forth. I can't see what your PopulateDropDownClienteList method does, but what it should be doing is creating an IEnumerable<SelectListItem> , where each item gets its Text property set to whatever you want displayed and its Value property to the PK of the Client . However, once you have that, you need some property on Project to post back to. Your virtual Client won't work as that needs a full Client instance, which your form will never have. So, you have two choices:

  1. Implement a view model to feed to the view (and accept in the post). In that view model, in addition to all other editable fields, you'll include something like ClientId which will be an int type, and you'll bind this to your drop down list. Once you're in your post method, you map all the posted values to your project instance, and then use the ClientId to look up a client from the database. You then set the resulting client as the value for your Client property and save as usual.

  2. You alter your database a bit. When you just specify a virtual, Entity Framework smartly creates a foreign key and a column to hold that relationship for you behind the scenes. That's great, but in situations like this, where you actually need to access that foreign key column, you're screwed. That way around that is to explicitly define a property to hold that relationship on your model and tell Entity Framework to use that instead of creating its own.

     [ForeignKey("Client")] public int ClientId { get; set; } public virtual Client Client { get; set; } 

    With that, you can now directly use ClientId without worrying about filling in Client . You again bind your drop down list to ClientId , but now, you do not need to look up the client explicitly from the database. Entity Framework will just save the ClientId as it should to the database, and then restore the Client based on that when you look up the project again in the future.

You Project Save code is not updating the entity, it is ADDING a new one all the time.

You should have update logic similar to following grounds -

To Add new FK Entry and associate it with parent record -

var entity = entities.Students.Where(p => p.Id == "2").First();
entity.StudentContact = new StudentContact() { Contact = "xyz", Id = "2" };

entities.Students.Attach(entity);
var entry = entities.Entry(entity);
// other changed properties
entities.SaveChanges();

To update a FK record with new details -

var entity = entities.Students.FirstOrDefault();
entity.StudentContact.Contact = "ABC";

entities.Students.Attach(entity);
var entry = entities.Entry(entity);
entry.Property(e => e.StudentContact.Contact).IsModified = true;
// other changed properties
entities.SaveChanges();

The above code, I have a Student records which has FK relationship with StudentContacts. I updated Contact information of a student and then updated it to database using ATTACH.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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