Har*_*lse 8 c# sqlite entity-framework
我正在尝试让 Entity Framework(6.4.4。2020 年夏季的最新版本)与 SQLite(1.0.113.1,也是 2020 年夏季的最新版本)一起工作。
我找到了很多关于如何执行此操作的信息,但这些信息并不总是有帮助,它们常常相互矛盾。
既然我知道了如何做,我决定记下我是如何做的。
问题描述了类和表格,答案将描述如何做到这一点。
我描述了一个学校数据库,其中每所学校都有零个或多个学生和教师(一对多),每个学生和每个教师都有一个地址(一对一),教师教授零个或多个学生,而学生由零个或多个教师(多对多)授课
所以我有几张表:
课程:
地址及学校:
public class Address
{
public long Id { get; set; }
public string Street { get; set; }
public int Number { get; set; }
public string Ext { get; set; }
public string ExtraLine { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
public class School
{
public long Id { get; set; }
public string Name { get; set; }
// Every School has zero or more Students (one-to-many)
public virtual ICollection<Student> Students { get; set; }
// Every School has zero or more Teachers (one-to-many)
public virtual ICollection<Teacher> Teachers { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
各位老师、同学们:
public class Teacher
{
public long Id { get; set; }
public string Name { get; set; }
// Every Teacher lives at exactly one Address
public long AddressId { get; set; }
public virtual Address Address { get; set; }
// Every Teacher teaches at exactly one School, using foreign key
public long SchoolId { get; set; }
public virtual School School { get; set; }
// Every Teacher Teaches zero or more Students (many-to-many)
public virtual ICollection<Student> Students { get; set; }
}
public class Student
{
public long Id { get; set; }
public string Name { get; set; }
// Every Student lives at exactly one Address
public long AddressId { get; set; }
public virtual Address Address { get; set; }
// Every Student attends exactly one School, using foreign key
public long SchoolId { get; set; }
public virtual School School { get; set; }
// Every Student is taught by zero or more Teachers (many-to-many)
public virtual ICollection<Teacher> Teachers { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
最后是 DbContext:
public class SchoolDbContext : DbContext
{
public DbSet<Address> Addresses { get; set; }
public DbSet<School> Schools { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<Teacher> Teachers { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
使用实体框架时,不需要在 DbContext 中定义连接表 TeachersStudents。当然,这并不意味着您不需要它。
如果您使用 Microsoft SQL Server,这足以让实体框架识别表以及表之间的关系。
唉,对于 SQLite 这还不够。
那么:如何让它发挥作用。来回答吧!
Har*_*lse 11
所以我使用 Visual Studio 创建一个空解决方案并添加一个 DLL 项目:SchoolSQLite。为了查看这是否有效,我还添加了一个控制台应用程序,该应用程序将使用实体框架访问数据库。
为了完整,我添加了一些单元测试。这超出了这个答案的范围。
在 DLL 项目中我曾经References-Manage NUGET Packages搜索过System.Data.SQLite. 该版本添加了实体框架和 SQLite 所需的代码。如果需要:更新到最新版本。
添加问题中描述的类:Address、School、Teacher、Student、SchoolDbContext。
现在是我发现最困难的部分:App.Config控制台应用程序文件中的连接字符串。
为了让它工作,我需要 App.Config 中的以下部分:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit ... -->
<section name="entityFramework"
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework,
Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
requirePermission="false"/>
</configSections>
Run Code Online (Sandbox Code Playgroud)
稍后在 App.Config 中的 EntityFramework 部分:
<entityFramework>
<providers>
<provider invariantName="System.Data.SqlClient"
type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer"/>
<provider invariantName="System.Data.SQLite.EF6"
type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6"/>
</providers>
</entityFramework>
<system.data>
<DbProviderFactories>
<remove invariant="System.Data.SQLite.EF6" />
<add name="SQLite Data Provider"
invariant="System.Data.SQLite.EF6"
description=".NET Framework Data Provider for SQLite"
type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
</DbProviderFactories>
</system.data>
Run Code Online (Sandbox Code Playgroud)
最后是连接字符串。我的数据库所在的文件是C:\Users\Harald\Documents\DbSchools.sqlite. 当然,您可以选择自己的位置。
<connectionStrings>
<add name="SchoolDbContext"
connectionString="data source=C:\Users\Haral\Documents\DbSchools.sqlite"
providerName="System.Data.SQLite.EF6" />
Run Code Online (Sandbox Code Playgroud)
(可能还有更多到其他数据库的连接字符串)
这应该可以编译,但您还无法访问数据库。2020 年夏季实体框架不会创建表,因此您必须自己创建表。
因为我认为这是 SchoolDbContext 的一部分,所以我添加了一个方法。为此,您需要一点 SQL 知识,但我认为您已经掌握了要点:
protected void CreateTables()
{
const string sqlTextCreateTables = @"
CREATE TABLE IF NOT EXISTS Addresses
(
Id INTEGER PRIMARY KEY NOT NULL,
Street TEXT NOT NULL,
Number INTEGER NOT NULL,
Ext TEXT,
ExtraLine TEXT,
PostalCode TEXT NOT NULL,
City TEXT NOT NULL,
Country TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS indexAddresses ON Addresses (PostalCode, Number, Ext);
CREATE TABLE IF NOT EXISTS Schools
(
Id INTEGER PRIMARY KEY NOT NULL,
Name TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS Students
(
Id INTEGER PRIMARY KEY NOT NULL,
Name TEXT NOT NULL,
AddressId INTEGER NOT NULL,
SchoolId INTEGER NOT NULL,
FOREIGN KEY(AddressId) REFERENCES Addresses(Id) ON DELETE NO ACTION,
FOREIGN KEY(SchoolId) REFERENCES Schools(Id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS Teachers
(
Id INTEGER PRIMARY KEY NOT NULL,
Name TEXT NOT NULL,
AddressId INTEGER NOT NULL,
SchoolId INTEGER NOT NULL,
FOREIGN KEY(AddressId) REFERENCES Addresses(Id) ON DELETE NO ACTION,
FOREIGN KEY(SchoolId) REFERENCES Schools(Id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS TeachersStudents
(
TeacherId INTEGER NOT NULL,
StudentId INTEGER NOT NULL,
PRIMARY KEY (TeacherId, StudentId)
FOREIGN KEY(TeacherId) REFERENCES Teachers(Id) ON DELETE NO ACTION,
FOREIGN KEY(StudentId) REFERENCES Students(Id) ON DELETE NO ACTION
)";
var connectionString = this.Database.Connection.ConnectionString;
using (var dbConnection = new System.Data.SQLite.SQLiteConnection(connectionString))
{
dbConnection.Open();
using (var dbCommand = dbConnection.CreateCommand())
{
dbCommand.CommandText = sqlTextCreateTables;
dbCommand.ExecuteNonQuery();
}
}
}
Run Code Online (Sandbox Code Playgroud)
有些事情值得一提:
当应用程序启动后首次执行实体框架查询时,OnModelCreating将调用方法。因此,现在是检查表是否存在的好时机,如果不存在,则创建它们。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
this.CreateTables();
Run Code Online (Sandbox Code Playgroud)
当然,您应该使用 OnModelCreating 向实体框架通知您的表以及表之间的关系。这可以在创建表后完成。
继续模型创建:
this.OnModelCreatingTable(modelBuilder.Entity<Address>());
this.OnModelCreatingTable(modelBuilder.Entity<School>());
this.OnModelCreatingTable(modelBuilder.Entity<Teacher>());
this.OnModelCreatingTable(modelBuilder.Entity<Student>());
this.OnModelCreatingTableRelations(modelBuilder);
base.OnModelCreating(modelBuilder);
}
Run Code Online (Sandbox Code Playgroud)
对于那些了解实体框架的人来说,对这些表进行建模相当简单。
地址; 简单表格的示例
private void OnModelCreatingTable(EntityTypeConfiguration<Address> addresses)
{
addresses.ToTable(nameof(SchoolDbContext.Addresses)).HasKey(address => address.Id);
addresses.Property(address => address.Street).IsRequired();
addresses.Property(address => address.Number).IsRequired();
addresses.Property(address => address.Ext).IsOptional();
addresses.Property(address => address.ExtraLine).IsOptional();
addresses.Property(address => address.PostAlCode).IsRequired();
addresses.Property(address => address.City).IsRequired();
addresses.Property(address => address.Country).IsRequired();
// The extra index, for fast search on [PostalCode, Number, Ext]
addresses.HasIndex(address => new {address.PostAlCode, address.Number, address.Ext})
.HasName("indexAddresses")
.IsUnique();
}
Run Code Online (Sandbox Code Playgroud)
学校也很简单:
private void OnModelCreatingTable(EntityTypeConfiguration<School> schools)
{
schools.ToTable(nameof(this.Schools))
.HasKey(school => school.Id);
schools.Property(school => school.Name)
.IsRequired();
}
Run Code Online (Sandbox Code Playgroud)
教师和学生:他们需要学校的外键,每所学校有零个或多个学生/教师:
private void OnModelCreatingTable(EntityTypeConfiguration<Teacher> teachers)
{
teachers.ToTable(nameof(SchoolDbContext.Teachers))
.HasKey(teacher => teacher.Id);
teachers.Property(teacher => teacher.Name)
.IsRequired();
// Specify one-to-many to Schools using foreign key SchoolId
teachers.HasRequired(teacher => teacher.School)
.WithMany(school => school.Teachers)
.HasForeignKey(teacher => teacher.SchoolId);
}
private void OnModelCreatingTable(EntityTypeConfiguration<Student> students)
{
students.ToTable(nameof(SchoolDbContext.Students))
.HasKey(student => student.Id);
students.Property(student => student.Name)
.IsRequired();
// Specify one-to-many to Schools using foreign key SchoolId
students.HasRequired(student => student.School)
.WithMany(school => school.Students)
.HasForeignKey(student => student.SchoolId);
}
Run Code Online (Sandbox Code Playgroud)
注意:默认情况下:如果学校被删除,这将向下级联:其所有教师和学生都将被删除。
只剩下一种表关系:联结表。如果我愿意,我也可以在这里定义学校和教师以及学校和学生之间的一对多关系。我在定义教师和学生时已经这样做了。所以这里不需要它们。我留下了代码,作为示例,如果您想将它们放在这里。
private void OnModelCreatingTableRelations(DbModelBuilder modelBuilder)
{
//// School <--> Teacher: One-to-Many
//modelBuilder.Entity<School>()
// .HasMany(school => school.Teachers)
// .WithRequired(teacher => teacher.School)
// .HasForeignKey(teacher => teacher.SchoolId)
// .WillCascadeOnDelete(true);
//// School <--> Student: One-To-Many
//modelBuilder.Entity<School>()
// .HasMany(school => school.Students)
// .WithRequired(student => student.School)
// .HasForeignKey(student => student.SchoolId)
// .WillCascadeOnDelete(true);
// Teacher <--> Student: Many-to-many
modelBuilder.Entity<Teacher>()
.HasMany(teacher => teacher.Students)
.WithMany(student => student.Teachers)
.Map(manyToMany =>
{
manyToMany.ToTable("TeachersStudents");
manyToMany.MapLeftKey("TeacherId");
manyToMany.MapRightKey("StudentId");
});
}
Run Code Online (Sandbox Code Playgroud)
这里解释了多对多映射
现在我们快完成了。我们所要做的就是确保数据库不会被删除并重新创建。这通常在以下位置完成:
Database.SetInitializer<SchoolDbContext>(null);
Run Code Online (Sandbox Code Playgroud)
因为我想隐藏我们使用 SQLite,所以我将其作为方法添加到 SchoolDbContext:
public class SchoolDbContext : DbContext
{
public static void SetInitializeNoCreate()
{
Database.SetInitializer<SchoolDbContext>(null);
}
public SchoolDbContext() : base() { }
public SchoolDbContext(string nameOrConnectionString) : base(nameOrConnectionString) { }
// etc: add the DbSets, OnModelCreating and CreateTables as described earlier
}
Run Code Online (Sandbox Code Playgroud)
我有时看到人们在构造函数中设置初始化程序:
public SchoolDbContext() : base()
{
Database.SetInitializer<SchoolDbContext>(null);
}
Run Code Online (Sandbox Code Playgroud)
然而,这个构造函数会被频繁调用。我觉得每次都这样做有点浪费。
当然,有一些模式可以在第一次构造 SchoolDbContext 时自动设置初始值设定项。为了简单起见,我在这里没有使用它们。
static void Main(string[] args)
{
Console.SetBufferSize(120, 1000);
Console.SetWindowSize(120, 40);
Program p = new Program();
p.Run();
// just for some neat ending:
if (System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine();
Console.WriteLine("Fin");
Console.ReadKey();
}
}
Program()
{
// Set the database initializer:
SchoolDbContext.SetInitializeNoCreate();
}
Run Code Online (Sandbox Code Playgroud)
现在有趣的部分是:添加学校,添加老师和学生,然后让老师教这个学生。
void Run()
{
// Add a School:
School schoolToAdd = this.CreateRandomSchool();
long addedSchoolId;
using (var dbContext = new SchoolDbContext())
{
var addedSchool = dbContext.Schools.Add(schoolToAdd);
dbContext.SaveChanges();
addedSchoolId = addedSchool.Id;
}
Run Code Online (Sandbox Code Playgroud)
添加老师:
Teacher teacherToAdd = this.CreateRandomTeacher();
teacherToAdd.SchoolId = addedSchoolId;
long addedTeacherId;
using (var dbContext = new SchoolDbContext())
{
var addedTeacher = dbContext.Teachers.Add(teacherToAdd);
dbContext.SaveChanges();
addedTeacherId = addedTeacher.Id;
}
Run Code Online (Sandbox Code Playgroud)
添加学生。
Student studentToAdd = this.CreateRandomStudent();
studentToAdd.SchoolId = addedSchoolId;
long addedStudentId;
using (var dbContext = new SchoolDbContext())
{
var addedStudent = dbContext.Students.Add(studentToAdd);
dbContext.SaveChanges();
addedStudentId = addedStudent.Id;
}
Run Code Online (Sandbox Code Playgroud)
差不多完成了:只剩下老师和学生之间的多对多关系:
学生决定由老师教授:
using (var dbContext = new SchoolDbContext())
{
var fetchedStudent = dbContext.Find(addedStudentId);
var fetchedTeacher = dbContext.Find(addedTeacherId);
// either Add the Student to the Teacher:
fetchedTeacher.Students.Add(fetchedStudent);
// or Add the Teacher to the Student:
fetchedStudents.Teachers.Add(fetchedTeacher);
dbContext.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
我还尝试将老师从学校中除名,发现这并没有伤害学生。此外,如果学生离开学校,老师会继续教学。最后:如果我删除学校,所有学生和教师都会被删除。
现在我向您展示了:
我没有显示一个关系:自引用:同一个表中另一个对象的外键。我在学校数据库中找不到一个很好的例子。如果有人有好主意,请编辑此答案并添加自引用表。
希望这对您有用。
| 归档时间: |
|
| 查看次数: |
12664 次 |
| 最近记录: |