所以你有一个Animals 的表和一个Vaccines 的表
class Animal
{
public int Id {get; set;}
public string Name {get; set;}
... // other properties, like BirthDate
}
class Vaccine
{
public int Id {get; set;}
public string Name {get; set;}
... // other properties
}
您给动物注射一剂疫苗。要记住哪个动物得到了哪个剂量,您需要一张表格VacineDosages。在此表中,您可以看到哪种动物获得了哪种疫苗的剂量。您可能想知道动物在哪一天服用了剂量,可能还有剂量。
Animals 和 VaccineDosages 之间存在一对多的关系:每个 Animals 获得零个或多个 VaccineDosages,每个 VaccineDosage 都被给予一个 Animals
类似地,疫苗和疫苗剂量之间也存在一对多的关系:每个疫苗在一个疫苗剂量中被使用了零次或多次,每个疫苗剂量都是一个疫苗的剂量。
当使用关系数据库时,一对多关系是使用外键实现的。 “多”端的项目获得它所属的“一”端项目的外键。
每一种疫苗剂量都用于为一只动物接种疫苗;剂量“属于”这个动物,因此它得到了这个动物的外键。类似地,一个 VaccineDosage 属于一个 Vaccine,因此有一个外键
class VaccineDosage
{
public int Id {get; set;}
// foreign keys
public int AnimalId {get; set;}
public int VaccineId {get; set;}
... // other properties, like Vaccination Date and Amount
}
现在要添加 VaccineDosages,您需要知道 VaccineDosage 是针对哪种动物给予的,以及使用哪种疫苗。好吧,您不需要知道 Animal 和 Vaccine,只要它们的 Id 就可以了。
所以假设您有一系列 VaccineDosages 尚未添加到数据库中,因此它们还没有 Id。
Id AnimalId VaccineId Amount Date
0 10 23 10 2021-05-10
0 10 24 5 2021-05-10
0 12 23 10 2021-05-09
0 13 23 10 2021-05-10
etc.
或者,在 C# 类中:
IEnumerable<VaccineDosage> vaccineDosages = new []
{
new VaccineDosage
{
AnimalId = 10,
VaccineId = 23,
Amount = 10,
Date = new DateTime(2021,05,10),
},
new VaccineDosage
{
AnimalId = 10,
VaccineId = 24,
Amount = 5,
Date = new DateTime(2021, 05, 10),
},
... etc
}
现在如何将此列表添加到数据库中?方法取决于你使用什么方法与数据库通信:
- LOW 级别:使用 SQL,并逐个添加 values 参数
- 中级:使用 nuget 包 DAPPER:您只需要提供 SQL 和对象
- 高级:使用实体框架。
当然,作为一个优秀的程序员,你想隐藏你保存数据的位置和方式:它可以在数据库中,但也许你想在 XML 文件中进行单元测试,CSV,Json?
用户想知道的一切:我想将项目放入一个对象中,然后,即使在计算机重新启动后,我也可以再次从同一个对象中获取项目。
这个“仓库”的东西,通常被称为存储库:
class Repository
{
public int AddAnimal(Animal animal} {...}
public int AddVaccine(Vaccine vaccine {...}
public int AddDosage(VaccineDosage dosage) {...}
这些方法返回新创建的添加项的Id,所以稍后你可以通过Id获取它们:
public Animal FetchAnimal(int animalId) {...}
您可以根据需要向存储库添加方法:
public IEnumerable<Animal> FetchAllAnimals() {...}
public IEnumerable<Animal> FetchAnimalsOfOwner(int ownerId) {...}
public IEnumerable<Animal> FetchUnvaccinateAnimals(int vaccineId) {...}
等等
在您的情况下,您还需要一种方法来添加一系列 VaccineDosages。
public void AddDosages(IEnumerable<VaccineDosage> dosages) {...}
也许所有的 VaccineDosages 都被插入到同一个 Animal 中,或者在同一个日期,如果是这样的话,考虑创建超载:
public void AddDosages(int animalId, IEnumerable<Vaccine> vaccins, ...)
为了让您的生活更轻松,这些重载可以调用您的原始版本。您可能不会每秒添加 1000 个疫苗。
存储库的好处是您隐藏了数据的保存方式。对于小型项目和单元测试,您可以将其保存为 CSV 文件或 XML。稍后您可以使用 SQL 安全地将其更改为保存在数据库中,您可以更改数据库的类型,或用于与 DBMS 通信的方法。您的存储库的用户无需更改,因此无需再次测试。
LOW 级别:带参数的 SQL
如果您仍在学习数据库,最好从低级别开始,这样您就可以在使用高级方法时感觉到哪些代码是“在后台”完成的
在与数据库进行低级别通信时,您会创建一个数据库连接。您要求连接创建命令,然后用 SQL 文本和参数填充命令。最后执行命令。
通常您不希望长时间打开数据库连接。您为一个操作创建一个打开和关闭数据库连接的过程:添加一个 VaccineDosage,或一个相当小的 VaccinedDosage 序列。
如果您需要在一次调用中将一百万个疫苗剂量添加到数据库中,那么您就是在谈论批量使用。这可能需要不同的方法。这超出了您的问题范围。
一个数据库连接有一个连接字符串。这取决于您使用的数据库管理系统 (DBMS),是否需要提供此连接字符串。如果您不提供,应用程序将使用文件 app.config 中提供的连接字符串。
要隐藏您使用的实际 DBMS,请考虑创建一个工厂方法来为您创建正确的数据库连接:
public DbConnection CreateDatabaseConnection()
{
// for example: use SQLight as database:
string dbConnectionString = this.CreateDbConnectionString();
return new System.Data.SQLite.SQLiteConnection(dbConnectionString);
}
如果以后您决定使用不同的 DBMS,您只需更改此过程。
隐藏表的名称,以便以后更改它们:
private const string tableNameAnimals = "Animals";
private const string tableNameVaccines = "Vaccines";
private const string tableNameVaccineDosages = "VaccineDosages";
public void Add(VaccineDosage dosage)
{
const string sqlText = @"Insert into table " + tableNameVaccineDosages
+ " (AnimalId, VaccineId, Amount, Date)"
+ " Values(@AnimalId, @VaccineId, @Amount, @Date);"
using (var dbConnection = this.CreateDatabaseConnection())
{
dbConnection.Open();
using (var dbCommand = dbConnection.CreateDbCommand())
{
dbCommand.CommandText = sqlText;
// add all parameters:
dbCommand.Parameters.AddWithValue("@AnimalId, dosage.AnimalId);
dbCommand.Parameters.AddWithValue("@VaccineId, dosage.VaccineId);
... // add the other parameters
// Execute the command:
dbCommand.ExecuteNonQuery();
}
}
}
using 声明断言所有物品一旦不再使用就会正确关闭和丢弃
如果你想添加一堆VaccineDosages,你可以多次调用这个方法。或者稍微优化一下,重用dbCommand
public void Add(IEnumerable<VaccineDosage> dosages)
{
const string sqlText = @"Insert into table " + tableNameVaccineDosages
+ " (AnimalId, VaccineId, Amount, Date)"
+ " Values(@AnimalId, @VaccineId, @Amount, @Date);"
using (var dbConnection = this.CreateDatabaseConnection())
{
dbConnection.Open();
foreach (var dosage in dosages)
{
using (var dbCommand = dbConnection.CreateDbCommand())
{
... etc.
}
}
}
}
当然,您可以省略使用参数并创建一个包含完整 sql 命令的 sql 文本,但这可能很危险:人们可能会破坏您的数据库。见SQL Injection attack
始终尝试使用常量 sql 字符串作为文本。不要编辑 sql 字符串,要为参数添加值,请始终使用Parameters.AddWithValue