首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >跟踪体育统计

跟踪体育统计
EN

Code Review用户
提问于 2014-03-08 02:03:58
回答 2查看 3.2K关注 0票数 12

我正在设计一个新的应用程序来跟踪体育统计代码优先迁移。我有最起码的POCO设置。问题是,我不满意的设计,特别是球员,球队和比赛是如何相互关联。

我很感谢大家的意见和建议。

PlayerAccount类:

代码语言:javascript
复制
/// <summary>
/// Player account is a separate entity from the actual user account.
/// Player account allows a user to join teams and participate in a tournament.
/// </summary>
public class PlayerAccount
{
    [Key]
    public int Id { get; set; }
    [Required]
    public string FirstName { get; set; }
    [Required]
    public string LastName { get; set; }

    /// <summary>
    /// TournamentPlayerKeys tells us which team this player is competing with 
    /// in a certain tournament.
    /// </summary>
    public virtual ICollection<PlayerTeamKey> PlayerTeamKeys { get; set; }
    public virtual ICollection<PlayerMatchStat> PlayerMatchStats { get; set; }
}

团队类:

代码语言:javascript
复制
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

/// <summary>
/// Team can be formed by a single user or a group of user. You'll need a team to join a tournament.
/// A single user can form his own team. 
/// </summary>
public class Team
{
    [Key]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }

    public virtual ICollection<PlayerTeamKey> PlayerTeamKeys { get; set; }
    public virtual ICollection<TeamTournamentKey> TeamTournamentKeys { get; set; }
}

锦标赛类:

代码语言:javascript
复制
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

/// <summary>
/// Tournaments are basically a collection of games in one event. 
/// For example, Summer 2014 Basketball League.
/// </summary>
public class Tournament
{
    [Key]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }

    public virtual ICollection<TeamTournamentKey> TeamTournamentKeys { get; set; }
    public virtual ICollection<TournamentMatch> TournamentMatches { get; set; }
}

在一次比赛中,有多个比赛实例(游戏):

代码语言:javascript
复制
/// <summary>
/// Match represents one occurence of a game in a league.
/// </summary>
public class TournamentMatch
{
    [Key]
    public int Id { get; set; }
    [Required]
    public int Tournament_Id { get; set; }

    [ForeignKey("Tournament_Id")]
    public virtual Tournament Tournament { get; set; }
    public virtual ICollection<TournamentMatchResult> TournamentMatchResults { get; set; }
}

当然,在一场比赛中,我们有多个匹配结果的实例。Score属性决定谁是赢家。

代码语言:javascript
复制
/// <summary>
/// This is the result of a league match.
/// </summary>
public class TournamentMatchResult
{
    [Key]
    public int Id { get; set; }
    [Required]
    public int Match_Id { get; set; }
    [Required]
    public int Team_Id { get; set; }
    [Required]
    public double Score { get; set; }

    [ForeignKey("Match_Id")]
    public virtual TournamentMatch TournamentMatch { get; set; }
    [ForeignKey("Team_Id")]
    public virtual Team Team { get; set; }
    public virtual ICollection<PlayerMatchStat> PlayerMatchStat { get; set; }

}

我有一个多部分的键来创建球员和团队之间的关系:

代码语言:javascript
复制
/// <summary>
/// This is a multipart key class that defines a player as part of a team.
/// Mind the naming convension, from smaller entity to a bigger entity; Player > Team.
/// </summary>
public class PlayerTeamKey
{

    [Required]
    public int PlayerAccount_Id { get; set; }
    [Required]
    public int Team_Id { get; set; }

    [ForeignKey("PlayerAccount_Id")]
    public virtual PlayerAccount Player { get; set; }
    [ForeignKey("Team_Id")]
    public virtual Team Team { get; set; }

}

我在球队和锦标赛上也有这样的表现:

代码语言:javascript
复制
/// <summary>
/// This is a multipart key class that is a combination of a team id and a tournament id.
/// This basically tells us that the team with the given id is going to participating on a tournament with the given id.
/// </summary>
public class TeamTournamentKey
{
    [Required]
    public int Team_Id { get; set; }
    [Required]
    public int Tournament_Id { get; set; }

    [ForeignKey("Team_Id")]
    public virtual Team Team { get; set; }
    [ForeignKey("Tournament_Id")]
    public virtual Tournament Tournament { get; set; }

}
EN

回答 2

Code Review用户

发布于 2014-07-12 18:16:50

我举了一个小小的例子告诉你,你所有的忧虑都过去了。不过,在您自己实现这一点之前,请记住Mat的备注:我将其作为一个快速草图,并将映射与上下文分离,命名约定等对于代码的清晰性非常重要。

尽管如此,这个小设置向您展示了如何更改它:

代码语言:javascript
复制
public class Player
{
    public int Id { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }

    public virtual ICollection<Team> Teams { get; set; }
}

public class Team
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Player> Players { get; set; }
}

就这样。你完蛋了!我们现在需要做的就是配置我们的映射,这样它就会生成中间表本身:

代码语言:javascript
复制
public class TournamentContext : DbContext
{
    public DbSet<Player> Players { get; set; }
    public DbSet<Team> Teams { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Player>()
            .HasKey(x => x.Id)
            .HasMany(x => x.Teams);

        modelBuilder.Entity<Team>()
            .HasKey(x => x.Id)
            .HasMany(x => x.Players);
    } 
}

如果您想自己测试它,可以使用以下示例代码:

代码语言:javascript
复制
static void Main(string[] args)
{
  using (var context = new TournamentContext())
  {
      context.Database.ExecuteSqlCommand("delete from Players");
      context.Database.ExecuteSqlCommand("delete from Teams");

      var team1 = context.Teams.Add(new Team {Name = "Power rangers"});
      var team2 = context.Teams.Add(new Team { Name = "Ninja turtles" });

      context.Players.Add(new Player
      {
          Firstname = "John",
          Lastname = "Johnson",
          Teams = new List<Team> {team1, team2}
      });

      context.Players.Add(new Player
      {
          Firstname = "Jack",
          Lastname = "Jackson",
          Teams = new List<Team>{team1}
      });

      context.SaveChanges();

      foreach (var player in context.Players.ToList())
      {
          Console.WriteLine("Player: " + player.Id);
          Console.WriteLine("Teams: " + string.Join(", ", player.Teams.Select(x => x.Name).ToArray()));
      }
  }

  Console.Read();
}

通过服务器资源管理器查看我们的数据库,我们现在可以看到以下表:

有了这个输出:

您会发现您可以大量地配置它,并在生成的表中添加限制,但是我太懒了,无法自己查找它们;映射不是我最好的一面。这应该给您一个关于如何摆脱那些不那么漂亮的中间表的想法。

票数 9
EN

Code Review用户

发布于 2014-07-12 18:31:36

我建议对主键和外键使用相同的名称,遵循一些一致的模式:

它可以是_id或Id,因此在PlayerAccount表中有主键PlayerAccountId (而不仅仅是Id),在PlayerTeamKey中也有作为外键的PlayerAccountId。

在非小项目中,字段和类的命名非常重要。

我相信,不管您遵循的是代码优先还是模型优先,还是其他方法,您的问题都是推动类的设计和命名。我个人非常喜欢使用PowerToys的第二个模式--您可以在像Enterprise 2这样的对象设计工具(如Enterprise 2)中使用Server这样的对象设计工具来完成数据图1。然后将此设计导出为代码--首先使用PowerToys。

您必须组织使用的术语,并将它们转换为有意义的类设计,这并不容易。

下面是您提供的类的图表:

您可以很容易地在Visual中创建这个类图。显然有缺少比赛类。(Match_Id -外键)(好的,它没有丢失,这是一个链接到非常可疑的TournamentMatch类,我明白)

您的基本类是Player (最初的PlayerAccount)、Team、Match (最初的TournamentMatchResult)、锦标赛。我把课程改名了--这样就更容易理解了。其他类是用于创建M:N关系的空关联类。这些M:N关系真的有必要吗?

没有答案的问题太多了

基本上,您的主要问题是在您的设计中有太多的无属性关联类(PlayerTeamKey、TournamentMatch .我看到了其中的4个),这很可能是类设计中的错误。

您应该首先回答我在第二张图表中所写的问题,然后才开始编写您的代码。否则你的设计就会失败。

编辑:

为了举例说明如何通过回答分析性问题来进行适当的设计,我创建了简化版本。正如我所相信的,根据你回答分析性问题的方式,你可以用4个类来创建你的模型。这个模型可以很容易地首先转换为C#代码,但是第一次确认这是否是您所要寻找的模型。

还有一个提示:如果你在比赛中使用单词匹配,不要把它叫做游戏,尽量少用一些绝对必要的术语。

票数 6
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/43762

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档