Я потратил почти семь часов, чтобы понять это, и не смог найти решение. Итак, вот я, разделяя эту проблему с вами.
Обратите внимание, что следующий пример - это упрощение и подмножество моего первоначального проекта. Я попытался упростить его как можно больше.
Для начала у меня две бизнес-модели:
Следующая диаграмма EDMX выглядит следующим образом:
Я использую MVC 4, и у меня есть простая страница, на которой вы можете ввести имена домашних и гостевых команд соответственно и кнопку сохранения для сохранения этих команд и соответствия:
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>
контроллер
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);
}
}
}
Как вы можете видеть внутри контроллера, имя введенной команды сравнивается с именем в базе данных. Если он существует, он должен быть обновлен; иначе вставлен. Нет проблем со вставками, но когда в текстовое поле вводится существующее имя команды, появляется следующее сообщение об ошибке:
Невозможно вставить значение NULL в столбец "Обновлено", таблица 'TestEF.dbo.Teams'; столбец не допускает нулей. INSERT терпит неудачу. утверждение завершено.
Я получаю эту ошибку, хотя внутри контроллера я явно устанавливаю UpdateDate для записей, которые необходимо обновить, и установите его состояние в Модифицировано. Однако в сообщении об ошибке указано, что поле UpdateDate не установлено. Я отлаживал и убедился, что поля обновлены правильно, но в SQL Profiler UpdateDate не установлен. Я очень смущен.
При необходимости я могу предоставить полный исходный код.
UPDATE Я подозреваю, что это имеет какое-то отношение к Attach/Detach, но я не уверен.
ОБНОВЛЕНИЕ 2 Я упростил код, чтобы узнать, работает ли он, и что он делает. Тогда почему исходный код не работает?
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();