当孩子有身份证时,如何使用EF将多个子实体添加到对象?

11 c# sql-server asp.net-mvc entity-framework entity-framework-5

我们使用EF5和SQL Server 2012以下两个类:

public class Question
{
    public Question()
    {
        this.Answers = new List<Answer>();
    }
    public int QuestionId { get; set; }
    public string Title { get; set; }
    public virtual ICollection<Answer> Answers { get; set; }

}
public class Answer
{
    public int AnswerId { get; set; }
    public string Text { get; set; }
    public int QuestionId { get; set; }
    public virtual Question Question { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

映射如下:

public class AnswerMap : EntityTypeConfiguration<Answer>
{
    public AnswerMap()
    {
        // Primary Key
        this.HasKey(t => t.AnswerId);

        // Identity
        this.Property(t => t.AnswerId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Run Code Online (Sandbox Code Playgroud)

数据库DDL

CREATE TABLE Answer (
    [AnswerId] INT IDENTITY (1, 1) NOT NULL,
    [QuestionId] INT NOT NULL,
    [Text] NVARCHAR(1000),
    CONSTRAINT [PK_Answer] PRIMARY KEY CLUSTERED ([AnswerId] ASC)
)";
Run Code Online (Sandbox Code Playgroud)

以下是我尝试过的结果:

这适用于一个孩子:

var a = new Answer{
    Text = "AA",
    QuestionId = 14
};
question.Answers.Add(a);
_uow.Questions.Update(question);
_uow.Commit();
Run Code Online (Sandbox Code Playgroud)

这不适用于多个孩子:

错误:ObjectStateManager中已存在具有相同键的对象.ObjectStateManager无法使用相同的键跟踪多个对象.

var a = new Answer{
    AnswerId = 0,
    Text = "AAA",
    QuestionId = 14
};
var b = new Answer {
    AnswerId = 0,
    Text = "BBB",
    QuestionId = 14
};
question.Answers.Add(a);
question.Answers.Add(b);
_uow.Questions.Update(question);
_uow.Commit();
Run Code Online (Sandbox Code Playgroud)

这不适用于多个孩子:

它创建了AnswerID的1000和1001,但我希望数据库创建新的Id.

var a = new Answer{
    AnswerId = 1000,
    Text = "AAA",
    QuestionId = 14
};
var b = new Answer {
    AnswerId = 1001,
    Text = "BBB",
    QuestionId = 14
};
question.Answers.Add(a);
question.Answers.Add(b);
_uow.Questions.Update(question);
_uow.Commit();
Run Code Online (Sandbox Code Playgroud)

不起作用:

编译错误.无法将null转换为int

var a = new Answer{
    AnswerId = null,
    Text = "AAA",
    QuestionId = 14    
};
var b = new Answer
{
    AnswerId = null,
    Text = "BBB",
    QuestionId = 14
};
question.Answers.Add(a);
question.Answers.Add(b);
_uow.Questions.Update(question);
_uow.Commit();
Run Code Online (Sandbox Code Playgroud)

不起作用:

ObjectStateManager无法使用相同的键跟踪多个对象.

var a = new Answer{
    Text = "AAA",
    QuestionId = 14
};
var b = new Answer
{
    Text = "BBB",
    QuestionId = 14
};
question.Answers.Add(a);
question.Answers.Add(b);
_uow.Questions.Update(question);
_uow.Commit();
Run Code Online (Sandbox Code Playgroud)

在我的应用程序中,我在客户端上生成了一个或多个新的Answer对象,然后将这些对象发送到服务器.上面我模拟了如果不将客户端添加到问题中会发生什么.请注意,将所有Answers添加到Question对象是在客户端上完成的,然后以JSON字符串形式提供给服务器.然后将其反序列化为这样的问题对象:

public HttpResponseMessage PutQuestion(int id, Question question) {
            _uow.Questions.Update(question);
            _uow.Commit();
Run Code Online (Sandbox Code Playgroud)

我想要使​​用a创建每个Answer对象new identity ID,以便将这些对象添加到Question对象中,并以正常方式返回Question对象.

我不知道如何做到这一点.到目前为止我所有的简单测试都不起作用.请注意,这是我们小组成员先前提出的问题的变体,该问题不太明确,我试图关闭.这个问题我希望更清楚.

笔记:

以下是更新编码的方式:

public virtual void Update(T entity)
{
    DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
    if (dbEntityEntry.State == EntityState.Detached)
    {
        DbSet.Attach(entity);
    }  
    dbEntityEntry.State = EntityState.Modified;
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*enz 5

我也遇到了同样的身份"限制".事实证明,如果您添加父项和任何子项,EF可以处理父项和子项都被添加到一起的事实.更新父项并同时插入两个子项时遇到问题.如果您附加父母,EF将自动接收这两个孩子并附加他们,无论您是否愿意.由于我们希望它自动生成Id,因此我们不会设置子项的主键.但是,当父级是更新时,EF无法处理具有相同主键的项目,并且由于两个子级都具有相同的PK 0,因此EF不会爆炸.

我找到的唯一方法是手动将子项的ID设置为不同的数字.我通常将第一个孩子的Id设置为-1,然后将-2设置为第二个孩子,依此类推.这将导致EF保存子项,并且由于在数据库上运行标识,密钥将自动更新,因为-1和-2不是有效的标识值.

但是,如果您达到第3级或更高级别,这将导致巨大的痛苦.您不仅需要为每个孩子更新此PK,而且还必须将其任何子级上的FK更新为此新的-1或-2值.否则,保存将再次失败!

我看到的唯一其他选项实际上只是一次插入一个子节点并调用save,因此上下文不处理具有相同PK的多个对象,但这种情况违背了ORM的目的...


Ami*_*aqi 1

你有没有提到你要添加a两次......?!

question.Answers.Add(a);
question.Answers.Add(a);
Run Code Online (Sandbox Code Playgroud)

通常,要添加 id 为标识的项目,必须跳过设置 id。您还应该将[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]属性添加到这些 ID:

public class Answer
{
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int AnswerId { get; set; }
    public string Text { get; set; }
    public int QuestionId { get; set; }
    public virtual Question Question { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

并添加这样的数据:

var a = new Answer{
    Text = "AAA",
    QuestionId = 14
};

var b = new Answer
{
    Text = "BBB",
    QuestionId = 14
};

dbContext.Answers.Add(a);
dbContext.Answers.Add(b);

dbContext.SaveChanges();

// ...
Run Code Online (Sandbox Code Playgroud)