前言:
根据关系的方向性,及老外说的是否能导航到关联属性.
我们可以将一对多/多对一划分成三种情况
1.单向的一对多
2.单向的多对一
3.双向一对多或多对一
无论一对多的关系如何,但在数据库的表结构都是一样的.
在EFCore中,它默认支持以上的所有的方式,且不需要使用那个FluentAPI进行配置.
所以问题变得十分的简单了,这就很大的方便了开发工作
使用方式:
1.单向多对一:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public Grade Grade { get; set; }
}
public class Grade
{
public int GradeId { get; set; }
public string GradeName { get; set; }
public string Section { get; set; }
}
class SGDbContext:DbContext
{
public DbSet<Student> students { get; set; }
public DbSet<Grade> grades { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var Connection = "server=.;database=XXXX;uid=XXXX;pwd=XXXXX";
optionsBuilder.UseSqlServer(Connection);
}
}
再看一看表生成的代码
protected override void Up(MigrationBuilder migrationBuilder)
{
//生成grades表(主表),将属性映射成字段
migrationBuilder.CreateTable(
name: "grades",
columns: table => new
{
GradeId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
GradeName = table.Column<string>(type: "nvarchar(max)", nullable: true),
Section = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_grades", x => x.GradeId);
});
//生成students表(从表),并设置了外键关联
migrationBuilder.CreateTable(
name: "students",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
GradeId = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_students", x => x.Id);
//将Grade属性映射成外键参照grades表的主键Gradeid
table.ForeignKey(
name: "FK_students_grades_GradeId",
column: x => x.GradeId,
principalTable: "grades",
principalColumn: "GradeId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_students_GradeId",
table: "students",
column: "GradeId");
}
2.单向一对多
class StudentSec
{
public int StudentId { get; set; }
public string StudentName { get; set; }
}
class GradeSec
{
public int GradeId { get; set; }
public string GradeName { get; set; }
public string Section { get; set; }
//多方不能导航到一方,是为单向一对多关联
public ICollection<StudentSec> Students { get; set; }
}
class SGDbContext:DbContext
{
public DbSet<StudentSec> student2 { get; set; }
public DbSet<GradeSec> grade2 { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var Connection = "server=.;Database=xxxxxx;uid=xxx;pwd=xxxxx";
optionsBuilder.UseSqlServer(Connection);
}
}
回到建表代码,只能说和单向多对一一模一样,所以不必过多赘述
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "grade2",
columns: table => new
{
GradeSecId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
GradeSecName = table.Column<string>(type: "nvarchar(max)", nullable: true),
Section = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_grade2", x => x.GradeSecId);
});
migrationBuilder.CreateTable(
name: "student2",
columns: table => new
{
StudentSecId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
StudentSecName = table.Column<string>(type: "nvarchar(max)", nullable: true),
GradeSecId = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_student2", x => x.StudentSecId);
table.ForeignKey(
name: "FK_student2_grade2_GradeSecId",
column: x => x.GradeSecId,
principalTable: "grade2",
principalColumn: "GradeSecId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_student2_GradeSecId",
table: "student2",
column: "GradeSecId");
}
3.双向多对一/一对多
双向的多对一和一对多是同一回事,
因为可以双向导航,对于一方是一对多,对于多方是多对一,
所以不必区分开来
同样按照他的约定,我们根本不用写FluentAPI
class Person
{
public int PersonId { get; set; }
public int Name { get; set; }
public ICollection<Pet> pets { get; set; }
}
class Pet
{
public int PetId { get; set; }
public string name { get; set; }
public Person owner { get; set; }
}
class PPDbContext:DbContext
{
public DbSet<Person> persons { get; set; }
public DbSet<Pet> pets { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var Connection = "server=.;Database=XXXXX;uid=XXXX;pwd=XXXX";
optionsBuilder.UseSqlServer(Connection);
}
}
再大致看一下建表的代码,发现建表的代码结构都是一样的
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "persons",
columns: table => new
{
PersonId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_persons", x => x.PersonId);
});
migrationBuilder.CreateTable(
name: "pets",
columns: table => new
{
PetId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
name = table.Column<string>(type: "nvarchar(max)", nullable: true),
ownerPersonId = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_pets", x => x.PetId);
table.ForeignKey(
name: "FK_pets_persons_ownerPersonId",
column: x => x.ownerPersonId,
principalTable: "persons",
principalColumn: "PersonId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_pets_ownerPersonId",
table: "pets",
column: "ownerPersonId");
}
这里就测试一下吧
static void Main(string[] args)
{
PPDbContext context = new PPDbContext();
Person p1 = new Person();
p1.Name = 999;
Person p2 = new Person();
p2.Name = 999;
//这里不让Person去维护关系,(其实是不让一方去维护关系,多方维护关系比较清晰一点)
Pet dog = new Pet();
dog.name = "旺財";
dog.owner = p1;
Pet cat = new Pet();
cat.name = "阿喵";
cat.owner = p1;
context.persons.Add(p1);
context.persons.Add(p1);
context.pets.Add(dog);
context.pets.Add(cat);
context.SaveChanges();
List<Person> list = context.persons
.Include(person =>person.pets)
.Where(person => person.pets!=null)
.ToList();
Console.WriteLine("Person的ID為:"+list.First<Person>().PersonId+"Person的名字為"+list.First<Person>().Name);
Person one = list.First<Person>();
Console.WriteLine(one.pets.ElementAt<Pet>(0).name+ one.pets.ElementAt<Pet>(1).name);
}
结果:
Person的ID為:1Person的名字為999
旺財阿喵
(PS:双向关系的话不需要双方同时维护关系,在Hibertnate中可以设置inverse属性来迫使一方放弃维护关系,这样能少发一条SQL语句也能减少数据库的压力,由于我们并不知道EFCore为我们生成了什么sql语句,也就无从得知EFCore维护实体关系的规则,或许之后可以研究一下)