Cannot update entity framework model
Asked Answered
D

3

0

I have spent nearly seven hours to figure this out and couldn't come up with a solution. So here am I, sharing this problem with you.

Please note that the following example is a simplification and subset of my original project. I tried to simplify it as much as possible for you.

To start, I have two business models:

enter image description here

The following EDMX diagram is as follows:

EDMX Diagram

I am using MVC 4 and I have a simple page where you can enter home and away team names respectively and a save button to save these teams and the match:

New match entry page

CSHTML

@model TestEF.Data.Match
@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>NewMatch</title>
</head>
<body>
    <div>
        Status: @ViewBag.Status
    </div>
    <div id="NewMatchFormContainer">
        @using (Ajax.BeginForm(new AjaxOptions() { Url = "/Match/NewMatch", UpdateTargetId = "NewMatchFormContainer" }))
        {
            @Html.ValidationSummary(false)

            @Html.TextBox("HomeTeamName", "", new { Name = "HomeTeam.TeamName" });
            @Html.TextBox("AwayTeamName", "", new { Name = "AwayTeam.TeamName" });

            <input type="submit" value="Save" />
        }
    </div>
</body>
</html>

Controller

public class MatchController : Controller
{
    TestEFEntities _dbContext = new TestEFEntities();

    public ActionResult Index()
    {
        return View();
    }

    public ActionResult NewMatch()
    {
        return View();
    }

    [HttpPost]
    public ActionResult NewMatch(Match matchData)
    {
        try
        {
            if (ModelState.IsValid)
            {
                using (TransactionScope ts = new TransactionScope())
                {
                    string homeTeamName = matchData.HomeTeam.TeamName;
                    Team existingHomeTeam = _dbContext.Teams.SingleOrDefault(i => i.TeamName == homeTeamName);
                    Team homeTeam = existingHomeTeam ?? matchData.HomeTeam;
                    homeTeam.UpdatedDate = DateTime.Now;

                    if (existingHomeTeam == null)
                    {
                        _dbContext.AddToTeams(homeTeam);
                    }
                    else
                    {
                        _dbContext.ObjectStateManager.ChangeObjectState(homeTeam, System.Data.EntityState.Modified);
                    }

                    string awayTeamName = matchData.AwayTeam.TeamName;
                    Team existingAwayTeam = _dbContext.Teams.SingleOrDefault(i => i.TeamName == awayTeamName);
                    Team awayTeam = existingAwayTeam ?? matchData.AwayTeam;
                    awayTeam.UpdatedDate = DateTime.Now;

                    if (existingAwayTeam == null)
                    {
                        _dbContext.AddToTeams(awayTeam);
                    }
                    else
                    {
                        _dbContext.ObjectStateManager.ChangeObjectState(awayTeam, System.Data.EntityState.Modified);
                    }

                    matchData.HomeTeam = homeTeam;
                    matchData.AwayTeam = awayTeam;

                    _dbContext.AddToMatches(matchData);
                    _dbContext.SaveChanges();

                    ts.Complete();
                }

                ViewBag.Status = "Success";

                return PartialView(matchData);
            }
            else
            {
                ViewBag.Status = "Invalid input.";

                return PartialView(matchData);
            }
        }
        catch (Exception ex)
        {
            ViewBag.Status = "Error: " + (ex.InnerException != null ? ex.InnerException.Message : ex.Message);

            return PartialView(matchData);
        }
    }
}

As you can see inside the controller, the entered team name is compared to those in the database. If one exists, it is to be updated; else inserted. There are no problems with inserts but when an existing team name is entered inside a textbox, I get the following error message:

Cannot insert the value NULL into column 'UpdatedDate', table 'TestEF.dbo.Teams'; column does not allow nulls. INSERT fails. The statement has been terminated.

I get this error even though inside the controller, I explicitly set the UpdateDate for records that need to be updated and set its state to Modified. However the error message says as if UpdateDate field was not set. I debugged and made sure the fields are updated correctly but in SQL Profiler UpdateDate is not set. I am very confused.

I can share the full source code if needed.

UPDATE I suspect it has something to do with Attach/Detach but I am not sure.

UPDATE 2 I have simplified the code to see whether it works and it does. Then why does the original code not work?

Team homeTeam = new Team() { TeamId = 1 };
Team awayTeam = new Team() { TeamId = 2 };

_dbContext.Teams.Attach(homeTeam);
homeTeam.UpdatedDate = DateTime.Now;

_dbContext.Teams.Attach(awayTeam);
awayTeam.UpdatedDate = DateTime.Now;

Match newMatch = new Match()
{
    HomeTeam = homeTeam,
    AwayTeam = awayTeam,
    UpdateDate = DateTime.Now
};

_dbContext.AddToMatches(newMatch);
_dbContext.SaveChanges();
Desmoid answered 13/1, 2014 at 10:1 Comment(5)
Have you tried setting the xxx.UpdatedDate = DateTime.Now; AFTER doing the _dbContext.ObjectStateManager.ChangeObjectState... statement inside the else? So, re-set it if updating? Try that and see if it solves your problem. If it works let me know and I will post answer.Hughey
Can I have a look at your Model's ?Placido
@Belogix, let me try that, I will respond asap.Desmoid
@Reddy, I have provided the model diagram. Do you need something specifically?Desmoid
@Belogix, unfortunately nothing changed.Desmoid
A
0

UpdatedDate does not allow nulls. Make it a nullable column in your database.

And also in your EDMX as scheien mentioned in the comment.

Ayannaaycock answered 13/1, 2014 at 10:6 Comment(4)
He should also make it nullable in the model, to reflect the database.Nigritude
UpdateDate is not a nullable field on purpose. It is set for both inserted and updated records.Desmoid
You edited the code. Your previous version would err if it isn't nullable. Now it should work fine.Ayannaaycock
@AmarDuplantier, I forgot to mention that. Previously it was as it is now, then I changed it to check if it will work and forgot to change to original version back. Sorry for misleading. It still doesn't work this way.Desmoid
S
0

Your schema in EF indicates that Null value is not allowed to be entered while adding/Inserting or Updating.

Make sure you are passing the correct non nullable value. Also you can change the schema of the table and update the model, so that null can be entered.

Stalinism answered 13/1, 2014 at 10:12 Comment(1)
I am passing DateTime.Now which is a non nullable value.Desmoid
T
0

Set a breakpoint here: awayTeam.UpdatedDate = DateTime.Now;

Then when you run it you can tell if it's pointing to the existing team or not.

I'm pretty certain that the issue is when you are trying to do an update. In that case you haven't detached your original object, instead you're trying to reassign. Give it a try to detach your existingAwayTeam, and then attach your matchData.AwayTeam, mark it as modified, and try saving it.

Tengdin answered 15/1, 2014 at 19:21 Comment(1)
Yes, this error occurs only when trying to update and it is very frustrating. I applied your solution for both away and home teams and I get this error now: "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key." Even though I detached existing entities. For further investigation please check out the question's UPDATE 2 part.Desmoid

© 2022 - 2024 — McMap. All rights reserved.