简体   繁体   English

为什么要添加新项目?

[英]Why is a new item being added?

One teacher can have many subjects and vice versa, one subject can have many teachers.一个老师可以有很多科目,反之亦然,一个科目可以有很多老师。 That is, here is mtm((ef > 5.0). That's exactly what I did. But when a new teacher is added, new subjects are created. How to fix this?也就是说,这里是 mtm((ef > 5.0)。这正是我所做的。但是当添加新教师时,会创建新科目。如何解决这个问题?

Before adding a teacher添加教师之前

  • Chemistry化学
  • PE体育
  • Biology生物学

Then I'm adding a new teacher然后我要添加一位新老师

    // for example subjects = ["Biology", "Chemistry"] 

    await _db.Teachers.AddAsync(new Teacher
    {
          ...
          Subjects = subjects,
    });

After adding a teacher添加老师后

  • Chemistry化学
  • PE体育
  • Biology生物学
  • Chemistry化学
  • Biology生物学

Teacher model老师model

public class Teacher : User
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public IList<Subject> Subjects { get; set; }
}

User model用户 model

public class User
{
    public int Id { get; set; }
    public string Login { get; set; }
}

Subject model主题 model

public class Subject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IEnumerable<Teacher> Teachers { get; set; }
}

This is a common pitfall with web applications and EF when you pass entities around, or cases where you create new references to related entities and assume that EF will relate them.这是 web 应用程序和 EF 在传递实体时的常见缺陷,或者在您创建对相关实体的新引用并假设 EF 将关联它们的情况下。

If you have a collection of Subjects passed into your method something like:如果您将一组主题传递到您的方法中,例如:

public void CreateTeacher(string name, IEnumerable<Subject> subjects)
{
    var teacher = new Teacher
    {
        Name = name,
        Subjects = subjects
    };
    context.Teachers.Add(teacher);
    context.SaveChanges();
}

where the subjects list had Chemistry and Biology.科目列表有化学和生物学。 It looks innocent enough, but between web request calls those Subjects coming in are just deserialized objects.它看起来很无辜,但是在 web 请求调用之间,进来的那些主题只是反序列化的对象。 They are not known to the DbContext, so when they are associated with the new Teacher they are treated as new subjects. DbContext 不知道它们,因此当它们与新教师相关联时,它们被视为新主题。 Be default EF conventions will treat a column named Id as an Identity so this will result in two new subjects added to the Subject table with names Chemistry & Biology.默认情况下,EF 约定会将名为 Id 的列视为身份,因此这将导致两个名为 Chemistry & Biology 的新主题添加到主题表中。

To avoid this behaviour we need to do one of two things: Associate the subjects to the DbContext (after checking to ensure they aren't already associated) or ensure we use only subjects that have been fetched by the DbContext.为了避免这种行为,我们需要做以下两件事之一:将主题与 DbContext 关联(在检查以确保它们尚未关联之后)或确保我们仅使用 DbContext 获取的主题。

Example: Fetching subjects from DbContext示例:从 DbContext 获取主题

public void CreateTeacher(string name, IEnumerable<Subject> subjects)
{
    var subjectIds = subjects.Select(x => x.Id).ToList();
    var dbSubjects = context.Subjects.Where(x => subjectIds.Contains(x.Id)).ToList();
    var teacher = new Teacher
    {
        Name = name,
        Subjects = dbSubjects
    };
    context.Teachers.Add(teacher);
    context.SaveChanges();
}

Typically we would re-factor the method to just pass in Subject Ids and use the DbContext to load them.通常我们会重构该方法以仅传递主题 ID 并使用 DbContext 加载它们。 No need to send all subject fields over the wire.无需通过网络发送所有主题字段。

Example: Associating with the DbContext示例:与 DbContext 关联

public void CreateTeacher(string name, IEnumerable<Subject> subjects)
{
    var subjectIds = subjects.Select(x => x.Id).ToList();
    foreach(var subject in subjects)
    {
        if(!context.Subjects.Local.Any(x => x.Id == subject.Id)
            context.Subjects.Attach(subject);
    }
    var dbSubjects = context.Subjects.Local
        .Where(x => subjectIds.Contains(x => x.Id))
        .ToList();

    var teacher = new Teacher
    {
        Name = name,
        Subjects = dbSubjects
    };
    context.Teachers.Add(teacher);
    context.SaveChanges();
}

This assumes that all Subjects coming in are valid and reference an existing row in the database.这假设所有进入的主题都是有效的并且引用数据库中的现有行。 If you can mix in new subjects then this gets a fair bit more complex.如果您可以混合新主题,那么这将变得更加复杂。 (checking subject IDs for 0 etc.) By going to context.Subjects.Local we don't hit the DB, we just check the DbContext cache for any loaded subjects before attaching the subject. (检查主题 ID 是否为 0 等)通过转到context.Subjects.Local ,我们不会访问数据库,我们只需在附加主题之前检查 DbContext 缓存中是否有任何加载的主题。 Once attached it will be part of the .Local set so we can find our subjects without touching the DB.一旦附加,它将成为.Local集的一部分,因此我们可以在不接触数据库的情况下找到我们的主题。

Generally fetching the data from the DB is safer and simpler.通常从数据库中获取数据更安全、更简单。 Fetching entities by ID is quite fast and helps ensure that the data state supports the passed in references.通过 ID 获取实体非常快,有助于确保数据 state 支持传入的引用。

I think what Sergey is proposing is close to what is required in that I think you need to move your adding of the relationship outside of the adding of the teacher record.我认为 Sergey 提议的内容接近于所需内容,因为我认为您需要将添加关系移至添加教师记录之外。 Just the second from last line changed from his proposal.倒数第二行与他的提议不同。

var teacher=new Teacher
    {
          ...
                   // don' t use this Subjects = subjects,
    });

 _db.Teachers.Add(teacher);
 teacher.Subjects = tvm.Subjects;
 await _db.Teacher.SaveChangesAsync();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM