概述
我正在建立一个网络应用程序(C#,MVC,EF,Azure),帮助人们玩电脑游戏(名为" DotA"),允许他们记录他们应该如何玩游戏。 DotA是一款5v5团队游戏,每个玩家控制着100多个独特的英雄"具有各种能力和属性。
在我的应用中,您可以制作两种不同类型的音符:提示和关系。
提示
Tip
是关于如何有效地扮演英雄的提示。
[Table(nameof(Tip))]
public class Tip: UserOwnedEntity
{
/// <summary>
/// The "HeroSubject" of a tip is the hero this tip applies to.
/// </summary>
public Hero HeroSubject { get; set; }
public int? HeroSubjectId { get; set; }
//Tips can be categorized
public TipType Type { get; set; } = TipType.Counter;
[Required]
[DataType(DataType.MultilineText)]
public string Text { get; set; }
/// <summary>
/// As the game changes, tips may go out of date; tracking the
/// applicable patch assists with this, and users can flag a
/// specific tip as out of date ("deprecated").
/// </summary>
[Required]
public string Patch { get; set; }
public bool Deprecated { get; set; } = false;
/// <summary>
/// Where the tip was found, possibly a URL. Can give context to
/// a tip that isn't very intuitive.
/// </summary>
public string Source { get; set; }
}
UserOwnedEntity
您会注意到Tip
继承自UserOwnedEntity
,这是一个简单的类,包含与给定User
相关的实体的共享属性:
public abstract class UserOwnedEntity
{
public int Id { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
关系
A Relationship
对两个不同的英雄进行比较(例如&#34;英雄X与英雄Y和#34协同作用)。 Relationship
是提示的超集;他们有一个主题(英雄X),但他们也有一个对象(英雄Y)。这是代码:
[Table(nameof(Relationship))]
public class Relationship: Tip
{
public int? HeroObjectId { get; set; }
public Hero HeroObject { get; set; }
}
注意:我已从这些类中删除了所有额外的方法,因此更容易理解数据关系。
用户
这给我们留下了User
对象,它将所有这些结合在一起:
public class User
{
public int Id { get; set; }
[Required]
[Index(IsUnique = true)]
public string Username { get; set; }
//Associated objects
public virtual List<Hero> Heroes { get; set; }
public virtual List<Tip> Tips { get; set; }
public virtual List<Relationship> Relationships { get; set; }
}
英雄
编辑:添加Hero
类以获得完整性。
public class Hero: UserOwnedEntity
{
public string Name { get; set; }
[DataType(DataType.MultilineText)]
public string Notes { get; set; }
public virtual List<Tip> Tips { get; set; } = new List<Tip>();
[NotMapped]
public virtual List<Relationship> Relationships { get; set; } = new List<Relationship>();
}
实体框架的SQL解释
实体框架创建一个正常/预期的Tip' table, but the
Relationship`表,其中包含以下字段:
问题1:多个用户外键
Relationship
表格不应包含User_Id
字段;它已在Tip
表中有一个UserId字段。实际上,Relationship
可以与2个User
记录相关联(一次在Tip
表中,一次在Relationship
表中),这是不可能的。它始终为null,因此某些查询在使用此UserId字段而不是继承字段时会失败。为什么要创建第二个外键?
查询失败的具体情况是通过Relationship
访问User
时,如:
var relationship = db.Users.First().Relationships.FirstOrDefault(r => r. Id == id);
问题2:提示与关系无法区分
除了检查匹配的Relation.Id
之外,还无法判断Tip
表中的记录是否实际为Relationship
。
首选解决方案
我想要的是两个不同的表:一个用于Tip
,另一个用于Relationship
。我不确定需要添加哪些属性组合才能实现这一目标。
或者,失败......
如果我可以删除额外的User_Id字段并使其成为提示和关系将加载就像它们在不同的表中一样,我会很高兴。实际上也许更开心。
答案 0 :(得分:2)
问题是您的实体模型被识别为有资格使用EF继承策略(使用此特定配置 - Table per Type (TPT))。
我想要的是两个不同的表格:一个用于
Tips
,另一个用于Relationships
。
虽然可以通过使用流畅的API配置Table per Concrete Type (TPC)来实现这一点,但它无法解决导航属性/关系/ FK的问题。
这是因为只要EF看到ICollection<Tip>
,它就会确保它还包含Relationships
(因为Relationship
是 Tip
)。同时,为了支持ICollection<Relationship>
,它将创建另一个FK。因此,在您的情况下,UserId
将用于将Relationship
映射到User.Tips
和User_Id
(按惯例默认名称) - Relationship
到User.Relationships
。请记住 - 在EF中,每个集合都映射到一个单独的关系。
因此,除了配置TPT继承之外,您还必须删除/忽略所有ICollection<Relationship>
属性。或者,完全摆脱EF继承。以后无法使用数据注释/流畅的API设置,因此您需要提取类似于UserOwnedEntity
的基本非实体类。像这样:
public abstract class TipEntity : UserOwnedEntity
{
/// <summary>
/// The "HeroSubject" of a tip is the hero this tip applies to.
/// </summary>
public Hero HeroSubject { get; set; }
public int? HeroSubjectId { get; set; }
//Tips can be categorized
public TipType Type { get; set; } = TipType.Counter;
[Required]
[DataType(DataType.MultilineText)]
public string Text { get; set; }
/// <summary>
/// As the game changes, tips may go out of date; tracking the
/// applicable patch assists with this, and users can flag a
/// specific tip as out of date ("deprecated").
/// </summary>
[Required]
public string Patch { get; set; }
public bool Deprecated { get; set; } = false;
/// <summary>
/// Where the tip was found, possibly a URL. Can give context to
/// a tip that isn't very intuitive.
/// </summary>
public string Source { get; set; }
}
[Table(nameof(Tip))]
public class Tip : TipEntity
{
}
[Table(nameof(Relationship))]
public class Relationship : TipEntity
{
public int? HeroObjectId { get; set; }
public Hero HeroObject { get; set; }
}
当然,这会影响您应用的其他部分,因为Relationship
不再是Tip
。相反,Tip
和Relationship
现在都是TipEntity
,因此TipEntity
可以在与非EF相关的多态代码中服务与Tip
相同的目的。