【问题标题】:Having difficulty seeing how to work with many-to-many relationships in MVC2/EF4难以理解如何在 MVC2/EF4 中处理多对多关系
【发布时间】:2010-11-11 04:42:58
【问题描述】:

我已经写过几次关于此的文章,但我仍然难以理解如何在 MVC2 和 EF4 中处理多对多关系,特别是在创建和编辑功能方面。我认为我的部分问题是我决定创建我的数据库表,使数据透视表在模型本身中不可见。

我的桌子,再一次:

Games:
   int GameID (primary key, auto-incr)
   string GameTitle
   string ReviewTitle
   int Score
   int ReviewContentID (foreign key from Content - News, Articles, and Game reviews all have similar content requirements)
   int GenreID (foreign key from Genres)

Platforms:
   int PlatformID (primary key, auto-incr)
   string Name

GamePlatform (not visible in model):
   int GameID (foreign key from Games)
   int PlatformID (foreign key from Platforms)

当我创建新评论时,我真的只想将条目添加到 GamePlatform 数据透视表,因为我只是试图将我评论的游戏链接到现有平台。在 OOP 级别处理它让我感到困惑,因为我一直认为我正在添加到平台,而我真正想做的只是将游戏的 id 链接到各种平台 id。

所以,我不想从传入的 HTTP-Post 数据创建新平台。我只是希望能够获取表单数据并创建一个新游戏、新评论内容,并根据选中的复选框将新游戏链接到现有平台。

我了解如何执行前两项任务。这是我似乎无法掌握的链接。

很抱歉继续在这方面喋喋不休,但这确实是阻碍我取得重大进展的一件事。

【问题讨论】:

    标签: asp.net-mvc-2 entity-framework-4 many-to-many


    【解决方案1】:

    假设您已将其正确映射到实体框架中,并且您的 Game 对象有一个 Platforms 集合,那么将现有 Platforms 分配给 Game 应该就像将这些平台的 ID 传递给您的 Game 添加/编辑操作一样简单。

    在您的表单中,您可以使用一系列复选框,其值属性为 PlatformID 和一个通用名称,例如“platformids”。请注意,Html.CheckBox() HtmlHelper 没有“值”参数,因此您必须通过 htmlAttributes 对象指定它。 MVC 的默认模型绑定器会通过向接收操作添加匹配参数,自动将表单中的“platformid”值集合分组到单个类型化的 IEnumerable 中。

    这里有一些代码可以帮助您入门:

    // games controller
    
    public action AddGame(Game newGame, int[] platformIds) {
        Platforms[] platforms;
        if(platFormIds != null && platformIds.Any()) {
            platforms = ObjectContext.Platforms.Where(ExpressionExtensions.BuildOrExpression<Platform, int>(p => p.PlatformID, platformIds)).ToList();
        }
    
        if(ModelState.IsValid()) {
            game.Platforms.AddRange(platforms);
    
            ObjectContext.AddToGames(game);
            ObjectContext.SaveChanges();
        }
    }
    
    // helper class 
    
     public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values) {
        if (valueSelector == null) throw new ArgumentNullException("valueSelector");
        if (values == null) throw new ArgumentNullException("values");
    
        ParameterExpression p = valueSelector.Parameters.Single();
    
        if (!values.Any())
            return e => false;
    
        IEnumerable<Expression> equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
        Expression body = equals.Aggregate(Expression.Or);
    
        return Expression.Lambda<Func<TElement, bool>>(body, p);
    }
    

    注意:上面的 BuildOrExpression() 只是创建 SELECT * FROM TABLE WHERE ID IN(1,2,3,4,5,...) 的 SQL 等效项的好方法。

    【讨论】:

    • 哇,太好了!有点超出我目前的 C#/MVC 技能水平,但我想我知道你在用它去哪里。对于编辑,有没有办法可能删除现有的链接?假设我不小心将游戏标记为 DS 游戏而不是 Wii 游戏,并想纠正这个错误。如有必要,有没有办法删除现有链接?
    • 当然,使用与上述相同的逻辑,当您创建平台列表以从预先检查已分配的平台中进行选择时。当控制器处理表单时,从 Game 对象中清除所有现有平台,并为所有选中的 PlatformID 创建新平台。我通常处理这个问题的方法是使用 while 循环:while(game.Platforms.Any()) { game.Platforms.Remove(game.Platforms.First()); }。根据我的经验,调用EntityCollection.Clear() 会导致集合被清空,但实际上并没有从数据存储中删除关联的实体。
    • 非常感谢!几天来这一直让我发疯,所以突然如此接近一个可行的解决方案是不可思议的。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 2021-01-28
    • 1970-01-01
    • 1970-01-01
    • 2011-09-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多