EF 导航 属性 未加载
EF Navigation Property Not Loading
我有以下型号 - Person
和 Address
。
Person
可以没有 Address
- 一个
Address
总是属于一个Person
类:
public class Person {
// properties
[ForeignKey("Address")]
public int? AddressId { get; set; }
public virtual Address Address { get; set; }
}
public class Address {
// properties
[ForeignKey("Person")]
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
个人配置:
HasOptional(a => a.Address)
.WithMany()
.HasForeignKey(u => u.AddressId);
地址配置:
HasRequired(a => a.Person)
.WithMany()
.HasForeignKey(u => u.PersonId);
问题
SSMS 显示所有 FK 和约束都符合预期。但是,当我执行以下操作时:
var dbPerson = db.Persons.Include(s => s.Address).ToList();
返回的 Person
个对象(具有地址的对象)中的 None 填充了 Address
或 AddressId
。一切都是空的。
当我对 db.Address
执行相同操作时,我会按预期填充所有属性 - 有效关系保持完整。是什么导致我的 1:1 可选关系的主要端没有引入依赖实体?
我应该注意我确实需要在上面定义的两个实体上都可以访问的 FK ID。
我告诉你,One-One/Optional关系不是这样炼成的。我正在分享如何建立 1:1/0 关系的代码。
此外,当您使用 fluent API 时,无需使用 Data Annotation 属性。只用其中一个,流利API更好,因为关系看起来很清楚
在1:1/0关系中,外键没有单独定义。外键只定义在任何一个table中,一个实体的主键成为另一个相关实体的主键和外键。在这个例子中,我将Id字段作为Person实体(table)的主键,并将Id作为Address实体(table)的主键和外键。这是 1:1/0 关系的正确方式。如果我们不遵守这个约定,关系就不会建立起来,就会面临问题。
这里是代码
public class Person
{
// properties
public int Id { get; set; }
public string Name { get; set; }
public virtual Address Address { get; set; }
}
public class Address
{
// properties
public int Id { get; set; }
public string Location { get; set; }
public virtual Person Person { get; set; }
}
public class PersonConfiguration : EntityTypeConfiguration<Person>
{
public PersonConfiguration()
{
ToTable("Person");
HasKey(p => p.Id);
}
}
public class AddressConfiguration : EntityTypeConfiguration<Address>
{
public AddressConfiguration()
{
ToTable("Address");
HasKey(p => p.Id);
Property(a => a.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
HasRequired(p => p.Person)
.WithOptional(a => a.Address);
}
}
public class AppObjectContext : DbContext
{
public AppObjectContext() : base("AppConnectionString")
{
}
public DbSet<Person> People { get; set; }
public DbSet<Address> Addresses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new PersonConfiguration());
modelBuilder.Configurations.Add(new AddressConfiguration());
}
}
这是生成的屏幕截图
在屏幕截图中您可以看到,由于映射关系,我们可以从 Person 实例访问 Address 实例,从 Address 实例访问 Person 实例。
这是我输入的数据 table.
这是table的结构
人物tableSQL剧本
CREATE TABLE [dbo].[Person](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
CONSTRAINT [PK_dbo.Person] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
地址TableSQL脚本
CREATE TABLE [dbo].[Address](
[Id] [int] NOT NULL,
[Location] [nvarchar](max) NULL,
CONSTRAINT [PK_dbo.Address] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Address] WITH CHECK ADD CONSTRAINT [FK_dbo.Address_dbo.Person_Id] FOREIGN KEY([Id])
REFERENCES [dbo].[Person] ([Id])
GO
ALTER TABLE [dbo].[Address] CHECK CONSTRAINT [FK_dbo.Address_dbo.Person_Id]
GO
回复您的备注:
I should note that I do need FK IDs accessible on both entities as
defined above.
违反了1:1/0关系的惯例,但更好的做法如下
在一对一关系中,外键和主键的值是相同的,也就是说,如果你访问一个实体的主键实体,它也是另一个实体的外键和主键。
比如人的主键是20,那么这个人映射的外键和address的主键也是20,这样才是正确的访问方式。
class Program
{
static void Main(string[] args)
{
using (var db = new NavigationContext())
{
Console.Write("Enter address: ");
var addr = Console.ReadLine();
Console.Write("Enter person: ");
var prs = Console.ReadLine();
Address address = new Address { Name = addr };
db.Addresses.Add(address);
Person person = new Person { Name = prs, AddressID = address.AddressID };
db.Persons.Add(person);
db.SaveChanges();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
[Table("Person")]
public class Person
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int PersonID { get; set; }
public string Name { get; set; }
public int? AddressID { get; set; }
//[ForeignKey("AddressID")]
//public virtual Address Address { get; set; }
}
[Table("Address")]
public class Address
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int AddressID { get; set; }
public string Name { get; set; }
public int PersonID { get; set; }
[ForeignKey("PersonID")]
public virtual Person Person { get; set; }
}
public class NavigationContext : DbContext
{
public NavigationContext()
: base("SQLDBConnection")
{
}
public DbSet<Person> Persons { get; set; }
public DbSet<Address> Addresses { get; set; }
}
我有以下型号 - Person
和 Address
。
Person
可以没有Address
- 一个
Address
总是属于一个Person
类:
public class Person {
// properties
[ForeignKey("Address")]
public int? AddressId { get; set; }
public virtual Address Address { get; set; }
}
public class Address {
// properties
[ForeignKey("Person")]
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
个人配置:
HasOptional(a => a.Address)
.WithMany()
.HasForeignKey(u => u.AddressId);
地址配置:
HasRequired(a => a.Person)
.WithMany()
.HasForeignKey(u => u.PersonId);
问题
SSMS 显示所有 FK 和约束都符合预期。但是,当我执行以下操作时:
var dbPerson = db.Persons.Include(s => s.Address).ToList();
返回的 Person
个对象(具有地址的对象)中的 None 填充了 Address
或 AddressId
。一切都是空的。
当我对 db.Address
执行相同操作时,我会按预期填充所有属性 - 有效关系保持完整。是什么导致我的 1:1 可选关系的主要端没有引入依赖实体?
我应该注意我确实需要在上面定义的两个实体上都可以访问的 FK ID。
我告诉你,One-One/Optional关系不是这样炼成的。我正在分享如何建立 1:1/0 关系的代码。 此外,当您使用 fluent API 时,无需使用 Data Annotation 属性。只用其中一个,流利API更好,因为关系看起来很清楚
在1:1/0关系中,外键没有单独定义。外键只定义在任何一个table中,一个实体的主键成为另一个相关实体的主键和外键。在这个例子中,我将Id字段作为Person实体(table)的主键,并将Id作为Address实体(table)的主键和外键。这是 1:1/0 关系的正确方式。如果我们不遵守这个约定,关系就不会建立起来,就会面临问题。
这里是代码
public class Person
{
// properties
public int Id { get; set; }
public string Name { get; set; }
public virtual Address Address { get; set; }
}
public class Address
{
// properties
public int Id { get; set; }
public string Location { get; set; }
public virtual Person Person { get; set; }
}
public class PersonConfiguration : EntityTypeConfiguration<Person>
{
public PersonConfiguration()
{
ToTable("Person");
HasKey(p => p.Id);
}
}
public class AddressConfiguration : EntityTypeConfiguration<Address>
{
public AddressConfiguration()
{
ToTable("Address");
HasKey(p => p.Id);
Property(a => a.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
HasRequired(p => p.Person)
.WithOptional(a => a.Address);
}
}
public class AppObjectContext : DbContext
{
public AppObjectContext() : base("AppConnectionString")
{
}
public DbSet<Person> People { get; set; }
public DbSet<Address> Addresses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new PersonConfiguration());
modelBuilder.Configurations.Add(new AddressConfiguration());
}
}
这是生成的屏幕截图
在屏幕截图中您可以看到,由于映射关系,我们可以从 Person 实例访问 Address 实例,从 Address 实例访问 Person 实例。
这是我输入的数据 table.
这是table的结构
人物tableSQL剧本
CREATE TABLE [dbo].[Person](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
CONSTRAINT [PK_dbo.Person] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
地址TableSQL脚本
CREATE TABLE [dbo].[Address](
[Id] [int] NOT NULL,
[Location] [nvarchar](max) NULL,
CONSTRAINT [PK_dbo.Address] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Address] WITH CHECK ADD CONSTRAINT [FK_dbo.Address_dbo.Person_Id] FOREIGN KEY([Id])
REFERENCES [dbo].[Person] ([Id])
GO
ALTER TABLE [dbo].[Address] CHECK CONSTRAINT [FK_dbo.Address_dbo.Person_Id]
GO
回复您的备注:
I should note that I do need FK IDs accessible on both entities as defined above.
违反了1:1/0关系的惯例,但更好的做法如下
在一对一关系中,外键和主键的值是相同的,也就是说,如果你访问一个实体的主键实体,它也是另一个实体的外键和主键。
比如人的主键是20,那么这个人映射的外键和address的主键也是20,这样才是正确的访问方式。
class Program
{
static void Main(string[] args)
{
using (var db = new NavigationContext())
{
Console.Write("Enter address: ");
var addr = Console.ReadLine();
Console.Write("Enter person: ");
var prs = Console.ReadLine();
Address address = new Address { Name = addr };
db.Addresses.Add(address);
Person person = new Person { Name = prs, AddressID = address.AddressID };
db.Persons.Add(person);
db.SaveChanges();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
[Table("Person")]
public class Person
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int PersonID { get; set; }
public string Name { get; set; }
public int? AddressID { get; set; }
//[ForeignKey("AddressID")]
//public virtual Address Address { get; set; }
}
[Table("Address")]
public class Address
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int AddressID { get; set; }
public string Name { get; set; }
public int PersonID { get; set; }
[ForeignKey("PersonID")]
public virtual Person Person { get; set; }
}
public class NavigationContext : DbContext
{
public NavigationContext()
: base("SQLDBConnection")
{
}
public DbSet<Person> Persons { get; set; }
public DbSet<Address> Addresses { get; set; }
}