避免多次进入导航属性和多对多导航属性

时间:2018-01-15 10:54:34

标签: c# entity-framework

我目前正在开发一个产品管理系统,我有一个文章版本页面,这里是我文章的简短定义:

Article : Id, name, List<Keywords>
Keyword : Name, List<Article>

这意味着当我生成上下文时,它会创建一个新表KeywordArticle。这里没什么奇怪的,现在我的问题是我有一个带有网格的版本页面,其中包含链接到文章的关键字,我可以编辑它以添加新的,删除一个等等。

为了解决这个问题,我目前有一个包含最终网格的ViewModel,并且我将它传递给我的控制器。

我的第一个想法就是将ViewModel替换为关键字实体的上下文实体关键字属性(entity.Keywords)。但是当联络人已经存在时会产生错误(逻辑,不能创建具有相同ID的新行)。

我的同事想要修复它是删除所有内容并每次重新添加实体,但它似乎非常沉重,可能不是最好的主意。像这样:

foreach (var keyword in existingArticle.Keywords)
{
    existingArticle.Keywords.Remove(keyword);
}

existingArticle.Keywords = article.Keywords;

(其中article.Keywords是我的viewmodel属性,映射到新的关键字实体的集合,而现有的文章是从上下文中检索的实体)

我怎么能处理这个?有没有任何魔术方法,或者我应该遍历我的列表来检索已经存在的联络人,添加它们并删除其余的?这些方面的东西:

var newKeywordsList = new List<Keyword>();

foreach (var keyword in article.Keywords)
{
    if (existingArticle.Keywords.Any(m => m.Id == keyword.Id))
    {
        newKeywordsList.Add(existingArticle.Keywords.First(m => m.Id == keyword.Id));
    }
    else
    {
        newKeywordsList.Add(keyword);
    }
}

existingArticle.Keywords = newKeywordsList;

老实说,两种解决方案似乎都很糟糕,但我不知道如何正确处理它。也许通过重新处理所有内容,只列出要在列表中执行的操作列表。

1 个答案:

答案 0 :(得分:1)

  

为了解决这个问题,我目前有一个包含最终网格的ViewModel,并且我将它传递给我的控制器。

糟糕的主意。仅将ViewModel传递给控制器​​,其中没有任何UI类(元素)。

没有任何魔术方法。做手动。

有3组变化。

  1. 新关键字。你可以找到它们:

    var newKeywords = article.Keywords.Except(existingArticle.Keywords, new KeywordComparer()).ToList();
    
  2. 您必须在dbContext

    中添加这些新实体
    1. &#34;可能修改&#34;关键字。你可以找到它们:

      var possibleModified = article.Keywords.Intersect(existingArticle.Keywords, new KeywordComparer()).ToList();
      
    2. 需要检查这些实体是否有任何更新,如果是,则在dbContext中更新

      1. 已删除的关键字。你可以找到它们:

        var deletedKeywords = existingArticle.Keywords.Except(article.Keywords, new KeywordComparer()).ToList();
        
      2. 必须从dbContext中删除这些实体。

        <强> UPD

        感谢grek40对Equals方法的建议。

        对于ExceptIntersect的正确工作,还需要Keyword类的比较。简单的是:

            class KeywordComparer : IEqualityComparer<Keyword>
            {
                /// <summary>
                /// Determines whether the specified objects are equal.
                /// </summary>
                /// <returns>
                /// true if the specified objects are equal; otherwise, false.
                /// </returns>
                /// <param name="x">The first object of type <paramref name="T"/> to compare.</param><param name="y">The second object of type <paramref name="T"/> to compare.</param>
                public bool Equals(Keyword x, Keyword y)
                {
                    return x != null && y != null && x.Id == y.Id;
                }
        
                /// <summary>
                /// Returns a hash code for the specified object.
                /// </summary>
                /// <returns>
                /// A hash code for the specified object.
                /// </returns>
                /// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param><exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.</exception>
                public int GetHashCode(Keyword obj)
                {
                    return obj.Id.GetHashCode();
                }
            }