0

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

楷模:

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 ]

那么如何正确实施呢?

4

1 回答 1

2

您不需要该TagModeltoQuestionModel课程。您可以many-to-many像这样对关系进行建模:

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

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

Question持有对 many 的引用,Tags并且每个Tag持有对 many 的引用Questions

Entity Framework(和其他任何东西一样)的全部意义ORM在于让您不必以某种方式对对象及其关系进行database-like建模,而是让您以纯粹的Object Oriented方式对其进行建模,然后让 ORM 完成中间表的“肮脏工作”,外键等...

于 2013-03-17T13:16:27.687 回答