很多关系 - 我做得对吗?

时间:2013-03-17 12:08:19

标签: c# asp.net-mvc entity-framework asp.net-mvc-4 ef-code-first

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

型号:

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();
}

这很好用,虽然我不确定这是否是正确的方法(事实上我自己实现了多对多的逻辑)。是否有更聪明的方法让EF为我做这份工作?我挖了一堆教程,但没有一个能为我工作。

另外我觉得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 ]

那么如何正确实现呢?

1 个答案:

答案 0 :(得分:2)

您不需要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做中间表,外键等的“脏工作”......