EF CodeFirst 自引用多对多...在抽象类或派生类上
我正在尝试使用多态表结构在 EF CodeFirst 中对自引用多对多进行建模。我使用的是 2011 年 10 月的 CTP,它支持派生类型的导航属性(在我所做的其他测试中效果很好)。
问题:
当我在基(抽象)表的映射中设置这个特定的多对多关系并尝试获取相关记录时,我得到了一个包含数百 K 联合和连接的 SQL 查询......只是生成所花费的时间SQL 语句的执行时间为 30 秒,而执行它只需几毫秒。但是,它确实返回适当的结果。当我更改两个派生对象之间存在的多对多时,生成的查询是完美的...但是我无法为其他派生对象再次映射相同的相关 M2M 表,而不被告知连接表已“已映射” ”。
细节:
现有的数据库结构有一个基表——Party——它以 1...1 或 0 与 Customer、Vendor、User 和 Department(每个属于 Party 类型)连接。
各方通过现有的联接表 PartyRelationship(ID、InternalPartyID、ExternalPartyID)相互关联。按照约定,InternalPartyID 包含用户的 PartyID,ExternalPartyID 包含与其关联的客户、供应商或部门的 PartyID。
尝试在新项目(WCF DataServices)中使用 EF CodeFirst,我将 Party 类创建为:
public abstract class Party
{
public Party()
{
this.Addresses = new List<Address>();
this.PhoneNumbers = new List<PhoneNumber>();
this.InternalRelatedParties = new List<Party>();
this.ExternalRelatedParties = new List<Party>();
}
public int PartyID { get; set; }
public short Active { get; set; }
//other fields common to Parties
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
public virtual ICollection<Party> InternalRelatedParties { get; set; }
public virtual ICollection<Party> ExternalRelatedParties { get; set; }
}
然后,使用 TPT 继承,Customer、Vendor、Department 和 User 类似于:
public class Customer : Party
{
public string TermsCode { get; set; }
public string DefaultFundsCode { get; set; }
//etc
}
public class User : Party
{
public string EmployeeNumber { get; set; }
public string LoginName { get; set; }
//etc
}
连接表:
public class PartyRelationship
{
public int PartyRelationshipID { get; set; }
public int InternalPartyID { get; set; }
public int ExternalPartyID { get; set; }
//certain other fields specific to the relationship
}
映射:
public class PartyMap : EntityTypeConfiguration<Party>
{
public PartyMap()
{
// Primary Key
this.HasKey(t => t.PartyID);
// Properties
this.ToTable("Party");
this.Property(t => t.PartyID).HasColumnName("PartyID");
this.Property(t => t.Active).HasColumnName("Active");
//etc
// Relationships
this.HasMany(p => p.InternalRelatedParties)
.WithMany(rp => rp.ExternalRelatedParties)
.Map(p => p.ToTable("PartyRelationship")
.MapLeftKey("ExternalPartyID")
.MapRightKey("InternalPartyID"));
}
}
public class PartyRelationshipMap : EntityTypeConfiguration<PartyRelationship>
{
public PartyRelationshipMap()
{
// Primary Key
this.HasKey(t => t.PartyRelationshipID);
// Properties
// Table & Column Mappings
//this.ToTable("PartyRelationship"); // Commented out to prevent double-mapping
this.Property(t => t.PartyRelationshipID).HasColumnName("PartyRelationshipID");
this.Property(t => t.InternalPartyID).HasColumnName("InternalPartyID");
this.Property(t => t.ExternalPartyID).HasColumnName("ExternalPartyID");
this.Property(t => t.CreateTime).HasColumnName("CreateTime");
this.Property(t => t.CreateByID).HasColumnName("CreateByID");
this.Property(t => t.ChangeTime).HasColumnName("ChangeTime");
this.Property(t => t.ChangeByID).HasColumnName("ChangeByID");
}
}
上下文:
public class MyDBContext : DbContext
{
public MyDBContext()
: base("name=MyDBName")
{
Database.SetInitializer<MyDBContext>(null);
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet<Party> Parties { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
modelBuilder.Configurations.Add(new PartyMap());
modelBuilder.Configurations.Add(new PartyRelationshipMap());
}
}
一个 URL,例如作为 http://localhost:29004/Services/MyDataService.svc/Parties(142173)/SAData.Customer/InternalRelatedParties 最终返回正确的 oData,但需要 30 秒才能生成巨大的 SQL 语句(189K )在 600 毫秒内执行。
我还尝试使用双向一对多映射 PartyRelationship 表(两者都作为“一”表映射到 Party),但结果类似。
我是否需要单独的客户-用户、供应商-用户和部门-用户连接表?我是否应该查看垂直表拆分或将 PartyRelationship 分成单独的逻辑实体的数据库视图(以便我可以重新映射同一个表)?在这种情况下,还有其他方法可以配置 EF 模型吗?
I'm trying to model a self-referencing many to many in EF CodeFirst with a polymorphic table structure. I'm using the October 2011 CTP which supports navigation properties on derived types (which works well in other tests I've done).
The problem:
When I set up this particular many to many relationship in the base (abstract) table's mapping and try to get related records, I get a SQL query with hundreds of K of unions and joins...just the time taken to generate the SQL statement is 30 seconds, compared to bare milliseconds to execute it. However, it does return appropriate results. When I change the many to many to exist between two derived objects, the query produced is perfect...but I can't map the same relating M2M table again for other derived objects without being informed that the joining table has "already been mapped".
Specifics:
An existing database structure has a base table--Party--which is joined 1...1 or 0 with Customer, Vendor, User, and Department (each a type of Party).
Parties are related to each other via an existing join table PartyRelationship (ID, InternalPartyID, ExternalPartyID). By convention, InternalPartyID contains a User's PartyID and ExternalPartyID contains the PartyID of the Customer, Vendor, or Department with which they are associated.
Trying to use EF CodeFirst in a new project (WCF DataServices), I have created the Party class as:
public abstract class Party
{
public Party()
{
this.Addresses = new List<Address>();
this.PhoneNumbers = new List<PhoneNumber>();
this.InternalRelatedParties = new List<Party>();
this.ExternalRelatedParties = new List<Party>();
}
public int PartyID { get; set; }
public short Active { get; set; }
//other fields common to Parties
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
public virtual ICollection<Party> InternalRelatedParties { get; set; }
public virtual ICollection<Party> ExternalRelatedParties { get; set; }
}
Then, using TPT inheritance, Customer, Vendor, Department and User are similar to:
public class Customer : Party
{
public string TermsCode { get; set; }
public string DefaultFundsCode { get; set; }
//etc
}
public class User : Party
{
public string EmployeeNumber { get; set; }
public string LoginName { get; set; }
//etc
}
The joining table:
public class PartyRelationship
{
public int PartyRelationshipID { get; set; }
public int InternalPartyID { get; set; }
public int ExternalPartyID { get; set; }
//certain other fields specific to the relationship
}
Mappings:
public class PartyMap : EntityTypeConfiguration<Party>
{
public PartyMap()
{
// Primary Key
this.HasKey(t => t.PartyID);
// Properties
this.ToTable("Party");
this.Property(t => t.PartyID).HasColumnName("PartyID");
this.Property(t => t.Active).HasColumnName("Active");
//etc
// Relationships
this.HasMany(p => p.InternalRelatedParties)
.WithMany(rp => rp.ExternalRelatedParties)
.Map(p => p.ToTable("PartyRelationship")
.MapLeftKey("ExternalPartyID")
.MapRightKey("InternalPartyID"));
}
}
public class PartyRelationshipMap : EntityTypeConfiguration<PartyRelationship>
{
public PartyRelationshipMap()
{
// Primary Key
this.HasKey(t => t.PartyRelationshipID);
// Properties
// Table & Column Mappings
//this.ToTable("PartyRelationship"); // Commented out to prevent double-mapping
this.Property(t => t.PartyRelationshipID).HasColumnName("PartyRelationshipID");
this.Property(t => t.InternalPartyID).HasColumnName("InternalPartyID");
this.Property(t => t.ExternalPartyID).HasColumnName("ExternalPartyID");
this.Property(t => t.CreateTime).HasColumnName("CreateTime");
this.Property(t => t.CreateByID).HasColumnName("CreateByID");
this.Property(t => t.ChangeTime).HasColumnName("ChangeTime");
this.Property(t => t.ChangeByID).HasColumnName("ChangeByID");
}
}
Context:
public class MyDBContext : DbContext
{
public MyDBContext()
: base("name=MyDBName")
{
Database.SetInitializer<MyDBContext>(null);
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet<Party> Parties { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
modelBuilder.Configurations.Add(new PartyMap());
modelBuilder.Configurations.Add(new PartyRelationshipMap());
}
}
A URL such as http://localhost:29004/Services/MyDataService.svc/Parties(142173)/SAData.Customer/InternalRelatedParties eventually returns correct oData but takes 30 seconds to produce an enormous SQL statement (189K) that executes in 600 ms.
I've also tried mapping the PartyRelationship table with a bidirectional one to many (both to Party as the "one" table), but with a similar outcome.
Do I need separate join tables for Customer-User, Vendor-User, and Department-User? Should I look at vertical table splitting or database views that separates PartyRelationship into separate logical entities (so I can remap the same table)? Is there another way the EF model should be configured in this scenario?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论