1

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.

4
  • What happens in projectRepository.Save? Commented Jan 27, 2014 at 13:01
  • 2
    EF is already abstracted way too much. You dont need a repository pattern over the top of it Commented Jan 27, 2014 at 13:06
  • Project.Save: public void Save(Project project) { try { _dao = new AppContext(); _dao.Project.Add(project); _dao.SaveChanges(); } catch (Exception ex) { throw new Exception(ex.Message); } } Commented Jan 27, 2014 at 13:14
  • Please update your question with the full ProjectRepository code, as the issue obviously occurs in there. @Greg there's no such thing as too much abstraction, read for example Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application for some motivation on this design. Commented Jan 27, 2014 at 13:25

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

The problem is when I create a new project for a client. The entity when I submit the form on view, return: - Project.Id = null - Project.Code = 0001 - Project.Description = "A simple text" - Project.Client.Id = 1 The Client with Id "1" exits. But, when I save it.. is created a new blank client, and no referrer is created to the existent client.
@user3198710, thats what I am telling you. Do not use ADD - _dao.Project.Add(project); instead use Attach as shown above.
If my answer helped you then can you please mark it as answer, that would help others to find out answers pretty easily.
1

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.

1 Comment

Thank you all. unable to resolve. was filling the client with another context, and should be the same always ... Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.