我有一个质量跟踪网络应用程序,我需要用户签署每个解决方案步骤。为了简化这个过程,我建立了我的模型,围绕着每个步骤结束时填写日期的人员。
我简化的失败模型如下所示:
public class failure {
[key]
public string FailureId { get; set; }
...
public int? OpenUserId { get; set; }
public int? DiagUserId { get; set; }
public int? CloseUserId { get; set; }
...
[ForeignKey("OpenUserId")]
public virtual signature OpenUser { get; set; }
[ForeignKey("DiagUserId")]
public virtual signature DiagUser { get; set; }
[ForeignKey("CloseUserId")]
public virtual signature CloseUser { get; set; }
}
和我的签名型号:
public class signature {
[key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int SignatureId { get; set; }
public string UserName { get; set; }
[Column(TypeName:="Date")]
[DataType(DataType.Date)]
public date DateSign { get; set; }
}
此模型的目标是最小化表中的签名数。因此,如果用户在一天内签署了多个失败,则代码应该只需要创建一个签名并重用相同的Id。
当用户在一次保存中填写多个步骤时,会出现问题。创建了两个或多个签名(这本身可能是一个问题,但它现在不是焦点)并且会引发错误
多个添加的实体可能具有相同的主键。
因为,在SaveChanges之前,所有ID都为0,代码无法区分它们。
这是POST:
async Task<ActionResult> Edit( FailureVM failure ) {
if (ModelState.IsValid) {
...
failure.OpenUserId = Await TryUpdateSignature(...);
failure.DiagUserId = Await TryUpdateSignature(...);
failure.CloseUserId = Await TryUpdateSignature(...);
...
await db.SaveChangesAsync;
}
}
和我的职能:
public static async Task<int?> TryUpdateSignature(MyDbContext db, Signature oldSignUser, Date? newDate, string userName)
{
int? SignatureID = null; //Returns null if no date
//Validate if there is a new date
if ((IsNothing(oldSignUser) && newDate != null) || (oldSignUser != null && oldSignUser.DateSign != newDate))
{
Signature recSignature = Await db.Signature.FirstOrDefaultAsync(s => s.UserID == userName && s.DateSign == newDate);
if (IsNothing(recSignature))
{
recSignature = new Signature;
recSignature.UserID = userName;
recSignature.DateSign = newDate;
db.Signature.Add(recSignature);
}
SignatureID = recSignature.SignatureID;
}
else if (oldSignUser != null && newDate != null)
{ //If no change, keep old signature
SignatureID = oldSignUser.SignatureID;
}
return SignatureID;
}
我尝试使用该对象而不是ID,但它没有用。我可以预先插入签名,但我希望一次保存所有内容。
此时,我认为我的模型或方法存在问题。
答案 0 :(得分:0)
在完成构造函数后,所有对象的Id都为0,因为0是默认的int值。因为PK必须识别数据库中的对象,所以EF甚至不允许您插入具有相同id值的多个对象,更不用说尝试将这些对象保存回数据库,因为它不知道如何识别每个元组(这甚至与EF有关,不仅仅是出于一致性原因的DBMS,因为EF使用PK值作为默认的并发令牌。)
解决此问题的方法是为所有对象提供不同的PK值。你是怎么做的取决于你 - 在我的项目中,我们让所有要从一个公共基类保存到数据库的对象,让我们称之为BOBase:
public abstract class BOBase
{
public long Id { get; set; }
public BOBase()
{
Id = DbTempId.Next();
}
}
internal static class DbTempId
{
private static long _next = -1;
internal static long Next()
{
return _next--;
}
}
一旦你让所有BO从这个基类继承(并且不隐藏继承的属性),新创建的对象的Id将是-1,-2,依此类推。我们甚至使用它来区分新对象和要更新的对象 - 新对象将具有负Id,而现有对象将具有正对象。