我花了将近七个小时来解决这个问题,但无法提出解决方案。我就是这样,与你分享这个问题。
请注意,以下示例是我原始项目的简化和子集。我尽可能地为你简化它。
首先,我有两种商业模式:
以下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插入列'UpdatedDate',表中 'TestEF.dbo.Teams';列不允许空值。 INSERT失败。该 声明已被终止。
即使在控制器内部,我也会收到此错误,我明确地为需要更新的记录设置UpdateDate并将其状态设置为Modified。但是,错误消息显示如果未设置UpdateDate字段。我调试并确保字段正确更新但在SQL Profiler中没有设置UpdateDate。我很困惑。
如果需要,我可以分享完整的源代码。
更新我怀疑它与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();
答案 0 :(得分:0)
UpdatedDate
不允许空值。使其成为数据库中可以为空的列。
在你的EDMX中也作为评论中提到的scheien。
答案 1 :(得分:0)
您在EF中的架构表示在添加/插入或更新时不允许输入Null值。
确保传递正确的不可为空的值。 您还可以更改表的架构并更新模型,以便输入null。
答案 2 :(得分:0)
在此处设置断点:awayTeam.UpdatedDate = DateTime.Now;
然后当你运行它时,你可以判断它是否指向现有团队。
我很确定问题出在您尝试进行更新时。在这种情况下,您没有分离原始对象,而是尝试重新分配。尝试分离您的existingAwayTeam,然后附加您的matchData.AwayTeam,将其标记为已修改,并尝试保存它。