多对多关系,我做对了吗?

本文关键字:关系 | 更新日期: 2023-09-27 18:04:13

这个想法很简单。我有一个标签列表。当我创建一个问题时,我想给它添加一些标签。

模型:

public class QuestionModel
{
    public int Id { get; set; }
    public String Content { get; set; }
    public ICollection<TagModeltoQuestionModel> Tags { get; set; }
    [NotMapped]
    public ICollection<TagModel> AssignedTags { get { return Tags.Select(x => x.Tag).ToList(); } }
    public int UserId { get; set; }
}
public class QuestionViewModel // helper - not in database
{
    public QuestionModel Model { get; set; }
    public ICollection<TagModel> Tags { get; set; }
}
public class TagModel
{
    public int Id { get; set; }
    public String Name { get; set; }
    public ICollection<TagModeltoQuestionModel> Questions { get; set; }
    [NotMapped]
    public bool Assigned { get; set; }
    [NotMapped]
    public ICollection<QuestionModel> AssignedQuestions { get { return Questions.Select(x => x.Question).ToList(); } }
}
public class TagModeltoQuestionModel // many to many
{
    [Key, Column(Order = 0)]
    public int TagId { get; set; }
    [Key, Column(Order = 1)]
    public int QuestionId { get; set; }
    public virtual QuestionModel Question { get; set; }
    public virtual TagModel Tag { get; set; }
}

控制器:

[HttpPost]
public ActionResult Edit(QuestionViewModel questionViewModel)
{
    if (ModelState.IsValid)
    {
        _repo.Update(questionViewModel.Model, questionViewModel.Tags); // see repo code below
        return RedirectToAction("Index");
    }
    return View(questionViewModel.Model);
}
回购:

public void Update(QuestionModel entity, ICollection<TagModel> tags)
{
    AssignTags(entity, tags);
    Db.Attach(entity);
    Db.SaveChanges();
}
private void AssignTags(QuestionModel entity, ICollection<TagModel> tags)
{
    tags = tags.Where(x => x.Assigned).ToArray(); // remove unassigned comming form View --> Controller
    var linkedTags =
        Db.TagsToQuestions.Where(x => x.QuestionId == entity.Id);
    var linkedTagsIds = linkedTags.Select(x => x.TagId);
    var selectedTagsIds = tags.Select(x => x.Id);
    var oldTags = linkedTags.Where(x => !selectedTagsIds.Contains(x.TagId));
    var newTags = tags.Where(x => !linkedTagsIds.Contains(x.Id)).Select(x=> new TagModeltoQuestionModel{QuestionId=entity.Id,TagId=x.Id});
    foreach (var t in oldTags)
        Db.Delete(t);
    foreach (var t in newTags)
        Db.Add(t);
    Db.SaveChanges();
}

这工作得很好,虽然我不确定这是否是正确的方法(事实上,我自己实现了整个多对多逻辑)。有没有更聪明的方法让英孚教育为我做这件事?我翻了一堆教程,但没有一本适合我。

另外,我觉得AssignTags方法可以用更好的方式编写,所以任何有关的评论也很感谢。

编辑

根据haim770的回答,我按他建议的方法简化了模型。

我的控制器现在看起来是这样的:

public void Update(QuestionModel entity, ICollection<TagModel> tags)
{
    Db.Attach(entity);
    //these lines give the same result
    //var ids = tags.Select(y => y.Id).ToArray();
    //entity.Tags = Db.Tags.Where(x => ids.Contains(x.Id)).ToArray();
    tags.ForEach(x => Db.Attach(x));
    entity.Tags = tags;
    Db.SaveChanges();
}

SaveChanges导致错误:

An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
inner:
{"A duplicate value cannot be inserted into a unique index. [ Table name = TagModelQuestionModels,Constraint name = PK_TagModelQuestionModels ]

那么如何正确地实现它呢?

多对多关系,我做对了吗?

您不需要TagModeltoQuestionModel类。您可以像这样对many-to-many关系建模:

public class QuestionModel
{
    //....
    public ICollection<TagModel> Tags { get; set; }
}
public class TagModel
{
    //....
    public ICollection<QuestionModel> Questions { get; set; }
}

Question持有一个对多个Tags的引用,每个Tag持有一个对多个Questions的引用。

Entity Framework(像任何其他ORM一样)的重点是让您不必以database-like的方式建模对象及其关系,而是让您以纯Object Oriented的方式建模,然后让ORM做中间表,外键等"脏工作"。