我正在创建一个作为其他项目基础的库,可以找到项目的详细信息here,并且可以找到nuget包here
由于这是一个基础库,我想允许用户扩展模型,所以我创建接口而不是具体的库(默认实现显然存在,但这超出了当前问题的范围),但是因为实体框架只能用具体的类来完成导航/外键,我正在通过通用接口的路径,它工作得很好,但我已经到了一个点,我想在同一个表中基于另一个外键限制外键
在搜索答案时,这个问题最符合我的要求
SQL: Foreign key that is a child of another foreign key - Stack Overflow
在那里提供的解决方案说技术上你应该删除父键,因为子表已经有父键可用于关联到父表,我完全同意它,但在我的情况下,这是不可能的,因为它将导致循环依赖地狱,这应该不惜一切代价远离软件项目。
IOrganization接口
public interface IOrganization<TLinkedAddress> where TLinkedAddress : ILinkedAddress
{
long ID { get; set; }
string Name { get; set; }
string Brand { get; set; }
string Genre { get; set; }
string Industry { get; set; }
string Master_Security_Stamp { get; set; }
long? Control_Branch_ID { get; set; }
[ForeignKey("Control_Branch_ID")]
TLinkedAddress Control_Branch { get; set; }
IList<TLinkedAddress> Branches { get; set; }
}
ILinkedAddress接口
public interface ILinkedAddress
{
long ID { get; set; }
string Address { get; set; }
long LocationID { get; set; }
string AddressType { get; set; }
string Contact_Person_Name { get; set; }
string Contact_Person_Number { get; set; }
string Contact_Person_Relation { get; set; }
[ForeignKey("OrganizationID")]
long? OrganizationID { get; set; }
[ForeignKey("UserID")]
long? UserID { get; set; }
}
IEducation接口
public interface IEducation<TUser, TOrganization, TLinkedAddress>
where TUser : IUser
where TOrganization : IOrganization<TLinkedAddress>
where TLinkedAddress : ILinkedAddress
{
long ID { get; set; }
long UserID { get; set; }
long InstituteID { get; set; }
long InstituteLocationID { get; set; }
string Degree { get; set; }
string Major { get; set; }
string Grade { get; set; }
DateTime StartDate { get; set; }
DateTime? EndDate { get; set; }
string Socities { get; set; }
string Description { get; set; }
[ForeignKey(name: "UserID")]
TUser User { get; set; }
[ForeignKey(name: "InstituteID")]
TOrganization Institute { get; set; }
[ForeignKey(name: "InstituteLocationID")]
TLinkedAddress InstituteLocation { get; set; }
}
默认实施
LinkedAddress.cs
[Table(name: "LinkedAddress", Schema = "Arinsys_CRM")]
public class LinkedAddress : DBEntity<LinkedAddress>, ILinkedAddress
{
public long ID { get; set; }
public string Address { get; set; }
public long LocationID { get; set; }
public string AddressType { get; set; }
public string Contact_Person_Name { get; set; }
public string Contact_Person_Number { get; set; }
public string Contact_Person_Relation { get; set; }
[ForeignKey("OrganizationID")]
public long? OrganizationID { get; set; }
[ForeignKey("UserID")]
public long? UserID { get; set; }
}
Organization.cs
[Table(name: "Organization", Schema = "Arinsys_CRM")]
public partial class Organization : DataContext.DBEntity<Organization>,
IOrganization<LinkedAddress>
{
public long ID { get; set; }
public string Name { get; set; }
public string Brand { get; set; }
public string Genre { get; set; }
public string Industry { get; set; }
public string Master_Security_Stamp { get; set; }
public long? Control_Branch_ID { get; set; }
[ForeignKey("Control_Branch_ID")]
public virtual LinkedAddress Control_Branch { get; set; }
public virtual IList<LinkedAddress> Branches { get; set; }
}
Education.cs
[Table(name: "Education", Schema = "Arinsys_CRM")]
public class Education : IEducation<User, Organization, LinkedAddress>
{
public long ID { get; set; }
public long UserID { get; set; }
public long InstituteID { get; set; }
public long InstituteLocationID { get; set; }
public string Degree { get; set; }
public string Major { get; set; }
public string Grade { get; set; }
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public string Socities { get; set; }
public string Description { get; set; }
[ForeignKey(name: "UserID")]
public virtual User User { get; set; }
[ForeignKey(name: "InstituteID")]
public virtual Organization Institute { get; set; }
[ForeignKey(name: "InstituteLocationID")]
public virtual LinkedAddress InstituteLocation { get; set; }
}
现在的问题是,是否可以在InstitueLocationID
接口的IEducation
上设置约束,它必须是由Organization
定义的InstituteID
的分支,否则我会没有其他选择,除了让最终的开发人员自己设置约束
编辑:进一步研究
我开始研究@Ivan Starostin所建议的传递依赖性,并且@Jeff Atwood(联合创始人(Emeritus)和Stack Overflow的共同创建者)发表了关于他们在Stack Overflow中遇到的问题的精彩博客文章。 2008
博客文章的摘要是,规范化不是一个灵丹妙药,有时非规范化数据实际上会带来更好的生产力和性能,尽管该博文没有回答确切的问题,但它帮助我获得了更深刻的理解,所以在这里张贴给未来的读者。
答案 0 :(得分:1)
这是一个理论上的问题。 InstituteID不是Education实体的属性 - 它是LinkedLocation实体的属性。所以你在Education实体中有一个传递依赖,这意味着它不是第三种正常形式:InstituteID依赖于InstituteLocationID。
所以你必须将InstituteID视为没有关系约束的非规范化数据或者从Education实体中删除它。
此外,我认为麻烦来自LinkedAddress实体 - 它是地址实体和附加链接实体的混合,它应该在地址和组织之间存储链接。因此,如果您从LinkedAddress中提取实体地址并使LinkedAddress成为存储AddressID和OrganizationID元组的真实 链接实体 - 这可能会使模型更加清晰,并且存在InstituteLocationID属性将足以确定与编辑实体数据相关的所有内容。
答案 1 :(得分:1)
现在的问题是,是否可以设置约束 IEducation接口中的InstitueLocationID必须是它的一个分支 由InstituteID定义的组织
EF对接口一无所知,但只关心课程。实际上不可能指定暗示接口的配置。 Fluent API和Data Annotation配置仅适用于类。您不能为从IEducation
派生的所有类创建约束,但只能在实现此接口的类上创建约束。
除了离开最终的开发者之外,我别无选择 自己约束
以ASP.Net Identity为例。要使用零配置来处理此API,Microsoft会为我们可能需要的每个类提供默认实现:IdentityUser
,Role
等...这些默认类实现IUser
,{{1}等等......
如果默认实施不能满足您的需求,您有两种选择:
如果选择后一个选项,则EF配置留给您。您必须对所有配置(使用Fluent API或数据注释)进行编码,以使其他类可以与您的新类一起使用。我的意思是实现IRole
,IUSerStore
,IUserPasswordStore
等的类...如果你甚至不想使用EF这个API让你实现所有IUserLoginStore
接口等...
我的答案是创建所有接口的默认实现,并对这些类设置约束。如果开发人员想要扩展您的API,他可以继承您的默认类,或者他可以通过实现API接口创建新类,并且必须创建所需的所有约束,以使他的新类与其余API一起正常工作。