实体框架6:没有导航属性的多对多关系

时间:2014-01-20 11:41:11

标签: c# entity-framework many-to-many code-first navigation-properties

(注意:我不同意上述链接,声称这是this的副本)

我正在使用Visual Studio 2010 SP1的实体框架6代码优先(但与Visual Studio 2013的行为相同),针对SQL Server 2012(在Windows 2008 R2上)。

案例1:在多对多关系中有两个实体EntityA和EntityB。连接表使用类JoinAB显式建模。

以下代码有效:

public class EntityA
{
    [Key]
    public Int64 PK { get; set; }
    public string Description { get; set; }

    public List<JoinAB> TheBEntities { get; set; }

    public EntityA()
    {
        TheBEntities = new List<JoinAB>();
    }
}

public class EntityB
{
    [Key]
    public Int64 PK { get; set; }
    public string SomeData { get; set; }

    public List<JoinAB> TheAEntities { get; set; }

    public EntityB()
    {
        TheAEntities = new List<JoinAB>();
    }
}

public class JoinAB
{
    public Int64 JKey { get; set; }
    public Int64 KKey { get; set; }

    public EntityA EntityA { get; set; }
    public EntityB EntityB { get; set; }
}

public class EntityAConfiguration : EntityTypeConfiguration<EntityA>
{
    public EntityAConfiguration()
    {
        HasMany(x => x.TheBEntities).WithRequired(y => y.EntityA).HasForeignKey(y => y.JKey).WillCascadeOnDelete(true);
    }
}

public class EntityBConfiguration : EntityTypeConfiguration<EntityB>
{
    public EntityBConfiguration()
    {
        HasMany(x => x.TheAEntities).WithRequired(y => y.EntityB).HasForeignKey(y => y.KKey).WillCascadeOnDelete(false);

    }
}

public class JoinABConfiguration : EntityTypeConfiguration<JoinAB>
{
    public JoinABConfiguration()
    {
        HasKey(x => new { x.JKey, x.KKey });
    }
}


public class Number1Context : DbContext
{
    public DbSet<EntityA> EntityAs { get; set; }
    public DbSet<EntityB> EntityBs { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new JoinABConfiguration());
        modelBuilder.Configurations.Add(new EntityAConfiguration());
        modelBuilder.Configurations.Add(new EntityBConfiguration());
    }
}

情况2:两个实体EntityC和EntityD仍处于多对多关系中,但连接表未使用类建模,并且EntityD没有EntityC的导航属性。这种情况类似于this postthis other post

以下代码有效,唯一的问题是我无法为EntityD和连接表之间的关系禁用“on delete cascade”选项。有没有办法实现这一点,还是我被迫向EntityD添加导航属性?

public class EntityC
{
    [Key]
    public Int64 PK { get; set; }
    public string Description { get; set; }

    public List<EntityD> TheDEntities { get; set; }

    public EntityC()
    {
        TheDEntities = new List<EntityD>();
    }
}

public class EntityD 
{
    [Key]
    public Int64 PK { get; set; }
    public string SomeData { get; set; }
}


public class EntityCConfiguration : EntityTypeConfiguration<EntityC>
{
    public EntityCConfiguration()
    {
        HasMany(x => x.TheDEntities).WithMany().Map(map => 
                {
                    map.MapLeftKey("C_Key");
                    map.MapRightKey("D_Key");
                    map.ToTable("CustomJoinCDTable");
                });
    }
}

public class Number2Context : DbContext
{
    public DbSet<EntityC> EntityCs { get; set; }
    public DbSet<EntityD> EntityDs { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new EntityCConfiguration());
    }
}

案例3:两个实体EntityJ和EntityK处于多对多关系中,连接表使用类JoinJK显式建模(如案例1中所示),但实体K没有导航属性加入JK(如案例2)。

有没有办法让这个案子有效?我尝试了以下代码,但它不起作用,它抱怨“类型中的每个属性名称必须是唯一的。属性名称'KKey'已经定义。”。有没有办法告诉实体框架“KKey”是主键的两部分,也是EntityK的外键,没有在EntityK中放置“List”导航属性?

(我们的想法是EntityK被许多其他实体引用,我们不希望它与许多导航属性混淆)。

public class EntityJ
{
    [Key]
    public Int64 PK { get; set; }
    public string Description { get; set; }

    public List<JoinJK> TheKEntities { get; set; }

    public EntityJ()
    {
        TheKEntities = new List<JoinJK>();
    }
}

public class EntityK 
{
    [Key]
    public Int64 PK { get; set; }
    public string SomeData { get; set; }
}

public class JoinJK
{
    public Int64 JKey { get; set; }
    public Int64 KKey { get; set; }

    public EntityJ EntityJ { get; set; }
    public EntityK EntityK { get; set; }
}

public class EntityJConfiguration : EntityTypeConfiguration<EntityJ>
{
    public EntityJConfiguration()
    {
        HasMany(x => x.TheKEntities).WithRequired(y => y.EntityJ).HasForeignKey(y => y.JKey).WillCascadeOnDelete(true);
    }
}

public class JoinJKConfiguration : EntityTypeConfiguration<JoinJK>
{
    public JoinJKConfiguration()
    {
        HasKey(x => new { x.JKey, x.KKey });
        HasRequired(x => x.EntityK).WithRequiredDependent().Map(m => m.MapKey("KKey") ).WillCascadeOnDelete(false);
    }
}


public class Number3Context : DbContext
{
    public DbSet<EntityJ> EntityJs { get; set; }
    public DbSet<EntityK> EntityKs { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new JoinJKConfiguration());
        modelBuilder.Configurations.Add(new EntityJConfiguration());
    }
}

请注意,案例3的行为不依赖于不遵循实体框架命名约定的这些类。如果我创建一个与案例3相同的案例4,但遵循命名约定的类EntityV和EntityW,它无论如何都不起作用。实体框架不会使JoinVW中的“EntityWId”成为外键,它会在JoinVW中创建一个新列“EntityW_Id”作为外键。 (如果我将我的密钥命名为“EntityWId”,它会忽略它,并在数据库中创建“EntityW_Id1”。

public class EntityV
{
    public Int64 Id { get; set; }
    public string Description { get; set; }

    public List<JoinVW> TheWEntities { get; set; }

    public EntityV()
    {
        TheWEntities = new List<JoinVW>();
    }
}

public class EntityW
{
    public Int64 Id { get; set; }
    public string SomeData { get; set; }
}

public class JoinVW
{
    public Int64 EntityVId { get; set; }
    public Int64 EntityWId { get; set; }

    public EntityV EntityV { get; set; }
    public EntityW EntityW { get; set; }
}

public class EntityVConfiguration : EntityTypeConfiguration<EntityV>
{
    public EntityVConfiguration()
    {
        HasMany(x => x.TheWEntities).WithRequired(y => y.EntityV).HasForeignKey(y => y.EntityVId).WillCascadeOnDelete(true);
    }
}

public class JoinVWConfiguration : EntityTypeConfiguration<JoinVW>
{
    public JoinVWConfiguration()
    {
        HasKey(x => new { x.EntityVId, x.EntityWId });
        HasRequired(x => x.EntityW).WithRequiredDependent().WillCascadeOnDelete(false);
    }
}


public class Number4Context : DbContext
{
    public DbSet<EntityV> EntityVs { get; set; }
    public DbSet<EntityW> EntityWs { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new JoinVWConfiguration());
        modelBuilder.Configurations.Add(new EntityVConfiguration());
    }
}

提前谢谢

德梅特里奥

编辑愚蠢的我

好的,我的代码中有关于案例3和案例4的错误。错误的行是:

        HasRequired(x => x.EntityK).WithRequiredDependent().Map(m => m.MapKey("KKey") ).WillCascadeOnDelete(false);

正确的行应该是:

        HasRequired(x => x.EntityK).WithMany().HasForeignKey(zz => zz.KKey).WillCascadeOnDelete(false);

使用此代码,案例3和案例4正常工作,所以我回答了我自己关于案例3/4的问题(“有没有办法告诉实体框架”KKey“是主键的两部分AND也是EntityK的外键,没有在EntityK中放置“List”导航属性?“。是的,有,并且是WithMany().HasForeignKey)。

但是我仍然找不到案例2的答案:在这种情况下如何禁用“删除级联”?

在所有四种情况下我注意到的另一件事:实体框架总是为连接表生成三个索引:第一个在密钥上,第二个在另一个密钥上,第三个是PK强制执行索引,集群唯一两个键上的索引。但是如果我想让其中一个索引聚集呢?单个键上的两个非聚簇索引之一是冗余的:例如,如果有一个聚簇索引(AKey,BKey),那么BKey上的非聚簇索引将是有用的,但AKey上的非聚簇索引将是无用的,因为群集一个(AKey,BKey)可以做同样的工作。

0 个答案:

没有答案