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 ] 

那么如何正确地执行呢?

+0

您的问题似乎更适合http://codereview.stackexchange.com StackOverflow是一个编程相关的问答网站,你应该只问问题,你可能会遇到问题遇到一段特定的代码。 – 2013-03-17 12:18:33

+0

@DarinDimitrov部分你是对的。我可能会问'如何实现一个多对多的关系',因为这是我的真正意义,但是我包含了更具体的代码。 – gisek 2013-03-17 12:23:42

+0

好的,那么你遇到这个代码有什么特别的问题?什么不起作用?你收到了什么错误信息? StackOverflow是一个问答网站,用户可以询问关于他们遇到问题的特定代码的具体问题。那么你呢?你遇到了什么问题,这些代码有什么不工作?如果您无法回答这些问题,只是要求进行代码审查或是否有更好的方法来实现某些内容,那么,正如我在第一条评论中所述,您的问题更适合http://codereview.stackexchange。com/ – 2013-03-17 12:24:19

回答

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模型饶你的“肮脏的工作'的中间表,外键等...

+0

这适用于创建操作。崩溃(myQuestion.Tags = someTags)它在SaveChanges()中报告关键字(它试图在myQuestion.Tags中重新添加标签而不是更新它们) – gisek 2013-03-18 09:47:47

+0

请将完整的异常和“更新”代码一起发布 – haim770 2013-03-18 10:30:36

+0

另外,我并不是说按照我推荐的方式改变你的实体应该立即使它工作,我只是简单介绍了它应该完成的方式。 在你的情况中,在你改变你的类之后,你是否改变了数据库相应地,您是否让实体框架为您重新创建了它? – haim770 2013-03-18 10:49:06