如何在SaveChanges之前创建多个实体?

时间:2018-03-20 20:16:01

标签: asp.net-mvc entity-framework

我有一个质量跟踪网络应用程序,我需要用户签署每个解决方案步骤。为了简化这个过程,我建立了我的模型,围绕着每个步骤结束时填写日期的人员。

我简化的失败模型如下所示:

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,但它没有用。我可以预先插入签名,但我希望一次保存所有内容。

此时,我认为我的模型或方法存在问题。

1 个答案:

答案 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,而现有对象将具有正对象。