在我看来,ForeignKey
属性对我来说不起作用,但我猜我错了;)
用代码解释更容易:
public class BaseCard
{
public int Id {get ; set; }
public int BaseCardId { get; set; }
public List<Skill> Skills { get; set; }
}
public class Skill
{
public int Id { get; set; }
public int BaseCardId { get; set; }
[ForeignKey("BaseCardId")]
public BaseCard BaseCard { get; set; }
}
当我尝试使用种子方法填充这些对象时,我收到此错误:
INSERT语句与FOREIGN KEY约束“FK_dbo.Skills_dbo.BaseCards_BaseCardId”冲突。冲突发生在数据库“数据库”,表“dbo.BaseCards”,列“Id”。
在我看来,ForeignKey
中的Skill
试图指向Id
的{{1}}列,而不是BaseCards
列,我可以弄清楚为什么......
如果我尝试删除BaseCardId
的“普通”Id
属性,并将BaseCard
设置为PK(带有属性BaseCardId
),我会得到以下内容错误:
存储更新,插入或删除语句会影响意外的行数(0)。自实体加载后,实体可能已被修改或删除。刷新ObjectStateManager条目。
有没有人知道如何让这段代码生效,因此[Key]
类中的属性BaseCardId
将指向Skill
的{{1}}属性,而不是显而易见的BaseCardId
属性?
提前致谢!
答案 0 :(得分:8)
可以自己设置BaseCard的Id,但首先必须使用Fluent Api来指定这个和一对多的关系。此外,通过这种方式,您不必在模型类中指定属性。你的模型类是这样的:
public class BaseCard
{
public int Id {get ; set; }
public virtual ICollection<Skill> Skills { get; set; }
}
public class Skill
{
public int Id { get; set; }
public int BaseCardId { get; set; }
public virtual BaseCard BaseCard { get; set; }
}
正如您所看到的,我将导航属性更改为虚拟。如果您将导航属性定义为虚拟EF将在运行时创建一个从您的BaseCard类派生的新类(动态代理)并使用它(相同的情况发生在技能)。这个新动态创建的类包含第一次访问时加载导航属性的逻辑。此功能称为延迟加载。它使实体框架能够避免加载数据库中不需要的整个依赖对象树。您可以在以下帖子中找到有关此主题的更多信息:Why Navigation Properties are virtual by default in EF和Entity Framework 4.1 Virtual Properties。
我在你的模型中提出的另一个变化是使用ICollection&lt;&gt;而不是列出&lt;&gt;在技能属性。正如您在此post中所看到的,在这种情况下,接口是一种很好的做法。它允许您稍后指定所需的实现(可以是List&lt;&gt;)。
现在,回到你的问题,在你的Context类中,你需要覆盖OnModelCreating方法来为BaseCards表中的Id指定关系和no autogenerate列。
public class YourContext : DbContext
{
public DbSet<BaseCard> BaseCards { get; set; }
public DbSet<Skill> Skill { get; set; }
//...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure the primary key for BaseCard
modelBuilder.Entity<BaseCard>().HasKey(t => t.Id);
//specify no autogenerate the Id Column
modelBuilder.Entity<BaseCard>().Property(b => b.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
//one-to-many relationship
modelBuilder.Entity<Skill>().HasRequired(c => c.BaseCard)
.WithMany(s => s.Skills)
.HasForeignKey(c => c.BaseCardId);
}
}
使用此配置,您必须始终使用不同的值设置BaseCard对象的Id。
如果您更喜欢使用数据注释,可以通过这种方式指定相同的内容:
public class BaseCard
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public virtual ICollection<Skill> Skills { get; set; }
}
public class Skill
{
[Key]
public int Id { get; set; }
public int BaseCardId { get; set; }
[ForeignKey("BaseCardId")]
public virtual BaseCard BaseCard { get; set; }
}
我的推荐是使用Fluent API,它更灵活,您不必触摸您的模型类。您可以在此post
中看到一些有用的消息