无法在源实体集’团队’的实体类型’Data.Team’上自动绑定导航属性’Players’

我正在使用EF6 Code First。

我有以下课程:

public class Player { [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [Required, MinLength(2, ErrorMessage = "Player name must be at least 2 characters length")] public string Name { get; set; } [Required] public int TeamClubId { get; set; } [ForeignKey("TeamClubId")] public virtual Team Club { get; set; } [Required] public int TeamNationalId { get; set; } [ForeignKey("TeamNationalId")] public virtual Team National { get; set; } } public class Team { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [Required, MinLength(2, ErrorMessage = "Team name must be at least 2 characters length")] public string Name { get; set; } [Required] public TeamType Type { get; set; } public virtual ICollection Players { get; set; } public virtual ICollection Matches { get; set; } } public class Tournament { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [Required, MinLength(2, ErrorMessage = "Tournament name must be at least 2 characters length")] public string Name { get; set; } public int Year { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public ICollection Matches { get; set; } } 

在DBContext类中,我有:

 protected override void OnModelCreating(DbModelBuilder modelBuilder) { //player - national team relations modelBuilder.Entity() .HasRequired(p => p.National) .WithMany() .WillCascadeOnDelete(false); //player - club team relations modelBuilder.Entity() .HasRequired(p => p.Club) .WithMany() .WillCascadeOnDelete(false); //match - home team relations modelBuilder.Entity() .HasRequired(p => p.HomeTeam) .WithMany() .WillCascadeOnDelete(false); //match - away team relations modelBuilder.Entity() .HasRequired(p => p.AwayTeam) .WithMany() .WillCascadeOnDelete(false); base.OnModelCreating(modelBuilder); } 

尝试通过OData连接时出现以下错误:

无法在源实体集’团队’的实体类型’Data.Team’上自动绑定导航属性’Players’,因为有两个或更多匹配的目标实体集。 匹配的实体集是:玩家,锦标赛。

[NotSupportedException:无法自动将实体类型’Data.Team’上的导航属性’Players’绑定到源实体集’Teams’,因为有两个或更多匹配的目标实体集。 匹配的实体集是:玩家,锦标赛。]
System.Web.Http.OData.Builder.EntitySetConfiguration.FindBinding(NavigationPropertyConfiguration navigationConfiguration,Boolean autoCreate)+736
System.Web.Http.OData.Builder.EdmModelHelperMethods.AddNavigationBindings(EntitySetConfiguration配置,EdmEntitySet entitySet,EntitySetLinkBuilderAnnotation linkBuilder,ODataModelBuilder构建器,Dictionary 2 edmTypeMap, Dictionary 2 edmEntitySetMap)+391
System.Web.Http.OData.Builder.EdmModelHelperMethods.AddEntitySets(EdmModel model,ODataModelBuilder builder,EdmEntityContainer container,Dictionary 2 edmTypeMap) +635
System.Web.Http.OData.Builder.EdmModelHelperMethods.BuildEdmModel(ODataModelBuilder builder) +200
System.Web.Http.OData.Builder.ODataConventionModelBuilder.GetEdmModel() +664 ODataAPI.WebApiConfig.Register(HttpConfiguration config) +221 System.Web.Http.GlobalConfiguration.Configure(Action
2 edmTypeMap) +635
System.Web.Http.OData.Builder.EdmModelHelperMethods.BuildEdmModel(ODataModelBuilder builder) +200
System.Web.Http.OData.Builder.ODataConventionModelBuilder.GetEdmModel() +664 ODataAPI.WebApiConfig.Register(HttpConfiguration config) +221 System.Web.Http.GlobalConfiguration.Configure(Action
2 edmTypeMap) +635
System.Web.Http.OData.Builder.EdmModelHelperMethods.BuildEdmModel(ODataModelBuilder builder) +200
System.Web.Http.OData.Builder.ODataConventionModelBuilder.GetEdmModel() +664 ODataAPI.WebApiConfig.Register(HttpConfiguration config) +221 System.Web.Http.GlobalConfiguration.Configure(Action
1 configurationCallback)+65

[HttpException(0x80004005):无法在源实体集’团队’的实体类型’Data.Team’上自动绑定导航属性’Players’,因为有两个或更多匹配的目标实体集。 匹配的实体集是:玩家,锦标赛。]
System.Web.HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(HttpContext context,HttpApplication app)+12582201
System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext,HttpContext context,MethodInfo [] handlers)+175
System.Web.HttpApplication.InitSpecial(HttpApplicationState状态,MethodInfo []处理程序,IntPtr appContext,HttpContext上下文)+304
System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext,HttpContext context)+404
System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext)+475

[HttpException(0x80004005):无法在源实体集’团队’的实体类型’Data.Team’上自动绑定导航属性’Players’,因为有两个或更多匹配的目标实体集。 匹配的实体集是:玩家,锦标赛。]
System.Web.HttpRuntime.FirstRequestInit(HttpContext context)+12599232 System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context)+159 System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr,HttpContext context)+12438981

我的问题是什么?

谢谢

您在Player中有两个导航属性指向Team,National和Club,然后您还拥有属于Team的属性Players,这是EF无法解析的,因为它无法猜测此集合是否指向National或Team属性所以您有为了帮助他,一种方法是使用InverseProperty属性装饰属性:

 public class Team { [InverseProperty("Club")] public virtual ICollection ClubPlayers { get; set; } [InverseProperty("National")] public virtual ICollection NationalPlayers { get; set; } } 

当然,我认为你也可以使用流畅的映射做同样的事情。

我在这里遇到了同样的问题,但我的问题是WebApiConfig.cs OData contiguration上的输入错误:

  builder.EntitySet("Users"); builder.EntitySet("Issues"); 

代替

  builder.EntitySet("Users"); builder.EntitySet("Issues"); 

你实际描述的是一个很多:玩家和团队之间的许多关系。 即使您想指定1:2关系(玩家:团队),EntityFramework也可以使用1:1-0,1:1,1:多。 2需要很多才能与EF一起正常工作,如果需要,可以使用业务逻辑将其约束为两个。

为什么这是一个问题? EF要求对于每个导航属性,在Principal Entity上都有一个Collection Property …这样它就可以支持在将实体添加到集合时自动设置ForeignKey属性的约定。

在您预期的模型中,假设您想使用以下代码将球员添加到球队,EF将如何知道在球员记录中设置哪个球队ID?

 Player newPlayer = new Player(); Team team = db.Teams.FirstOrDefault(t => t.Id == 1); // Just illustrating that team is an existing data entity; team.Players.Add(newPlayer); // should this set TeamNationalId = 1 // or TeamClubId = 1 

仅仅因为您可能不想使用上面的代码将球员添加到球队并不意味着EF会忽略惯例。

你有什么选择:

  1. 将团队分成2个表,NationalTeam和ClubTeam
  2. 将导航属性拆分为两个属性,NationalPlayers,ClubPlayers
  3. 添加中间表TeamPlayer

我不希望你考虑选项1,我不认为你也想要,所以我会跳过那个…

然后还有另外一个问题……你和Team和Match有类似的关系,一个Match有一个HomeTeam和一个AwayTeam,只有在Match中它可能更像是一个团队有一个HomeMatches和AwayMatches的集合,但它是具有不同数据类型的情况基本相同。

在上面的3个选项中,这里是选项2的一些代码:(感谢E-Bat对Inverse Properties的属性表示法的回答 )

 public class Team { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [Required, MinLength(2, ErrorMessage = "Team name must be at least 2 characters length")] public string Name { get; set; } [Required] public TeamType Type { get; set; } //public virtual ICollection Players { get; set; } [InverseProperty("Club")] public virtual ICollection ClubPlayers { get; set; } [InverseProperty("National")] public virtual ICollection NationalPlayers { get; set; } //public virtual ICollection Matches { get; set; } [InverseProperty("HomeTeam")] public virtual ICollection HomeMatches { get; set; } [InverseProperty("AwayTeam")] public virtual ICollection AwayMatches { get; set; } } 

现在,如果这对于您的某些业务逻辑代码来说是个问题,那么通过向Team添加Players和Matches属性可能会有所帮助,它可能会简化处理,请注意您还必须在构造函数中初始化集合以避免错误,但无论如何,您都会为导航属性执行此操作以避免序列化期间出现问题:

 public class Team { public Team() { ClubPlayers = new HashSet(); NationalPlayers = new HashSet(); } [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [Required, MinLength(2, ErrorMessage = "Team name must be at least 2 characters length")] public string Name { get; set; } [Required] public TeamType Type { get; set; } [InverseProperty("Club")] public virtual ICollection ClubPlayers { get; set; } [InverseProperty("National")] public virtual ICollection NationalPlayers { get; set; } public virtual IEnumerable Players { get { return ClubPlayers.Union(NationalPlayers); } } [InverseProperty("HomeTeam")] public virtual ICollection HomeMatches { get; set; } [InverseProperty("AwayTeam")] public virtual ICollection AwayMatches { get; set; } public virtual IEnumerable Matches { get { return HomeMatches.Union(AwayMatches); } } } 

让我们考虑选项3. TeamPlayers的中间表。 这将允许您添加有关关系的更多元数据,例如签名/来自日期和结束日期。 也许他们是临时转会或贷款人?

请注意,上述原因并不真正适用于匹配关系上下文中的选项3,匹配将始终只有2个团队,并且这些团队不会随着时间的推移而改变。 选项2在IMO中是匹配方案的最佳选择。

您已经在比赛中拥有年份,这意味着您希望在多个赛季中跟踪和保存数据,很有可能多年来球员将更换球队,您当前的模型将不支持这一点,例如运行报告5年前的总决赛中的球员将根据球员的磨损率显示太多或不足的球员……这只是一个想法。 选项3为未来的function和报告开辟了一些新的可能性。