我正在使用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<Player> Players { get; set; }
public virtual ICollection<Match> 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<Match> Matches { get; set; }
}
在DBContext类中,我有:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//player - national team relations
modelBuilder.Entity<Player>()
.HasRequired<Team>(p => p.National)
.WithMany()
.WillCascadeOnDelete(false);
//player - club team relations
modelBuilder.Entity<Player>()
.HasRequired<Team>(p => p.Club)
.WithMany()
.WillCascadeOnDelete(false);
//match - home team relations
modelBuilder.Entity<Match>()
.HasRequired<Team>(p => p.HomeTeam)
.WithMany()
.WillCascadeOnDelete(false);
//match - away team relations
modelBuilder.Entity<Match>()
.HasRequired<Team>(p => p.AwayTeam)
.WithMany()
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
尝试通过OData连接时出现以下错误:
无法在源实体集'团队'的实体类型'Data.Team'上自动绑定导航属性'Players',因为有两个或更多匹配的目标实体集。匹配的实体集包括:玩家,锦标赛。
[NotSupportedException:无法自动绑定导航 属性'Players'在实体类型'Data.Team'上为源实体 设置'团队',因为有两个或更多匹配的目标实体集。 匹配的实体集是:玩家,锦标赛。]
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构建器,字典2 edmTypeMap, Dictionary
2 edmEntitySetMap)+391
System.Web.Http.OData.Builder.EdmModelHelperMethods.AddEntitySets(EdmModel model,ODataModelBuilder构建器,EdmEntityContainer容器, 字典2 edmTypeMap) +635
1 configurationCallback)+65
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[HttpException(0x80004005):无法自动绑定导航 属性'Players'在实体类型'Data.Team'上为源实体 设置'团队',因为有两个或更多匹配的目标实体集。 匹配的实体集是:玩家,锦标赛。]
System.Web.HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(HttpContext的 上下文,HttpApplication app)+12582201
System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr的 appContext,HttpContext上下文,MethodInfo []处理程序)+175
System.Web.HttpApplication.InitSpecial(HttpApplicationState状态, MethodInfo [] handlers,IntPtr appContext,HttpContext context)+304
System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr的 appContext,HttpContext context)+404
System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr的 appContext)+475[HttpException(0x80004005):无法自动绑定导航 属性'Players'在实体类型'Data.Team'上为源实体 设置'团队',因为有两个或更多匹配的目标实体集。 匹配的实体集是:玩家,锦标赛。]
System.Web.HttpRuntime.FirstRequestInit(HttpContext context)+12599232 System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +159 System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest) wr,HttpContext context)+12438981
我的问题是什么?
由于
答案 0 :(得分:1)
您在Player中有两个导航属性指向Team,National和Club,然后您还拥有属于Team的属性Players,这是EF无法解析的属性,因为它无法猜测此集合是否指向National或Team属性所以你必须帮助他,一种方法是用InverseProperty属性装饰属性:
public class Team
{
[InverseProperty("Club")]
public virtual ICollection<Player> ClubPlayers { get; set; }
[InverseProperty("National")]
public virtual ICollection<Player> NationalPlayers { get; set; }
}
当然,我认为你也可以使用流畅的映射做同样的事情。
答案 1 :(得分:1)
我在这里遇到了同样的问题,但我的问题是WebApiConfig.cs上的输入错误OData contiguration:
builder.EntitySet<User>("Users");
builder.EntitySet<User>("Issues");
而不是
builder.EntitySet<User>("Users");
builder.EntitySet<Issue>("Issues");
答案 2 :(得分:0)
你实际描述的是很多:玩家和团队之间的许多关系。即使你想指定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,我不认为你也想要,所以我会跳过那个......
然后还有另一个问题......你和Team和Match有类似的关系,一个Match有一个HomeTeam和一个AwayTeam,只有在Match中它可能更像是一个团队有一个HomeMatches和AwayMatches的集合的现实但它与不同数据类型的情况基本相同。
上面的3个选项中,以下是选项2的一些代码:(感谢E-Bat's answer对于反向属性的属性表示法)
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<Player> Players { get; set; }
[InverseProperty("Club")]
public virtual ICollection<Player> ClubPlayers { get; set; }
[InverseProperty("National")]
public virtual ICollection<Player> NationalPlayers { get; set; }
//public virtual ICollection<Match> Matches { get; set; }
[InverseProperty("HomeTeam")]
public virtual ICollection<Match> HomeMatches { get; set; }
[InverseProperty("AwayTeam")]
public virtual ICollection<Match> AwayMatches { get; set; }
}
现在,如果这对于您的某些业务逻辑代码来说是个问题,那么通过向Team添加Players和Matches属性可能会有所帮助,它可能会简化处理,请注意您还必须在构造函数中初始化集合为避免错误,但无论如何都要对导航属性执行此操作以避免序列化期间出现问题:
public class Team
{
public Team()
{
ClubPlayers = new HashSet<Player>();
NationalPlayers = new HashSet<Player>();
}
[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<Player> ClubPlayers { get; set; }
[InverseProperty("National")]
public virtual ICollection<Player> NationalPlayers { get; set; }
public virtual IEnumerable<Player> Players
{
get
{
return ClubPlayers.Union(NationalPlayers);
}
}
[InverseProperty("HomeTeam")]
public virtual ICollection<Match> HomeMatches { get; set; }
[InverseProperty("AwayTeam")]
public virtual ICollection<Match> AwayMatches { get; set; }
public virtual IEnumerable<Match> Matches
{
get
{
return HomeMatches.Union(AwayMatches);
}
}
}
让我们考虑选项3. TeamPlayers的中介表。这将允许您添加有关关系的更多元数据,例如签名/从日期和结束日期。也许他们是临时转会或贷款人?
请注意,上述原因并不真正适用于匹配关系上下文中的选项3,匹配将始终只有2个团队,并且这些团队不会随着时间的推移而改变。选项2在IMO中是比赛场景中最好的选项。
您已经在比赛中拥有年份,这意味着您希望在多个赛季中跟踪和保存数据,很有可能多年来球员将更换球队,您当前的模型将不支持这一点,例如运行5年前总决赛中球员的报告将根据球员的损耗率显示太多或不足的球员......这只是一个想法。选项3为未来的功能和报告开辟了一些新的可能性。