我一直在研究一个使用LINQ来管理简单SQLite数据库的C#程序。数据库由不同的表组成,在我的代码中这些表由不同的类表示。这是我创建表的方式:
public ITable<Doctors> Doctors => GetTable<Doctors>();
public ITable<Patients> Patients=> GetTable<Patients>();
public ITable<Prescriptions> Prescriptions=>GetTable<Prescriptions>();
public ITable<Assistants> Assistants=> GetTable<Assistants>();
public ITable<Medicines> Medicines => GetTable<Medicines>();
public ITable<General_medicines> General_medicines=> GetTable<General_medicines>();
public ITable<Stocks> Stocks=> GetTable<Stocks>();
public ITable<Dosages> Dosages=> GetTable<Dosages>();
public ITable<Recipes> Recipes=> GetTable<Recipes>();
public ITable<Prescriptions_MP> Prescriptions_MP=> GetTable<Prescriptions_MP>();
现在,我想创建一个LINQ查询(在一个单独的类中),在该查询中所有这些表均具有不同的属性,并将它们放入IEnumerable中,以便以后进行扫描。 为此,我按以下步骤操作:
public IEnumerable<Therapy> TakePrescriptions()
{
HealthDataContext DbContext = DbFactory.Create();
var dbPrescriptions = DbContext.GetTable<Prescriptions>();
IEnumerable<Prescriptions> prescriptions= dbPrescriptions.AsEnumerable();
var dbPatients= DbContext.GetTable<Patients>();
IEnumerable<Pazienti> patients= dbPatients.AsEnumerable();
var dbPrescrizioniMP = DbContext.GetTable<Prescriptions_MP>();
IEnumerable<Prescriptions_MP> prescriptionsMP = dbPrescriptionsMP .AsEnumerable();
var dbRecipes = DbContext.GetTable<Recipes>();
IEnumerable<Recipes> recipes= dbRecipes .AsEnumerable();
var dbMedicines= DbContext.GetTable<Medicines>();
IEnumerable<Medicines> medicines= dbMedicines.AsEnumerable();
var dbGeneral_medicines = DbContext.GetTable<General_medicines>();
IEnumerable<General_medicines> general_medicines= dbGeneral_medicines.AsEnumerable();
var dbDosages = DbContext.GetTable<Dosages>();
IEnumerable<Dosages> dosages= dbDosages .AsEnumerable();
var query = from p in patients
join pr in prescriptions_MP on p.Id equals pr.Patient
join pre in prescriptions on pr.Prescription equals pre.Id
join fc in medicines on pre.Medicine equals fc.Id
join fg in general_medicines on fc.Medicine equals fg.Id
join ds in dosages on fg.Id equals ds.General_Medicine
where p.Doctor== IdDoctor
select new
{
IdDoctor, //int
p.Name, //string
pr.Prescription, //int
pre.Id, //int
fc.Format, //string
fc.Administration, //string
fc.Downloadable, //boolean
fc.Full_stomach, //boolean
nameM= fg.Name, //string
ds.Quantity, //int
ds.Hour //string
};
List < Therapy> therapy = new List<Therapy>();
foreach(var object in query)
{
Therapy t = new Therapy(IdDoctor, object.Name, object.Prescription, object.Id, object.Format, object .Administration, object.Downloadable, object.Full_stomach, object.nameM, object.Quantity, object.Hour);
therapy.Add(t);
}
return therapy;
}
现在,当我尝试加载应该显示结果列表的页面时,我得到 InvalidOperationException:一个开放的阅读器与此命令相关联。在更改CommandText属性之前将其关闭。在 foreach 操作中。
当我尝试调试时,可以看到在查询之前创建的表内部有项目,但是查询的结果为NULL 。
我尝试处置DBContext,但是随后出现此异常: ObjectDisposedException:IDataContext被处置,请参见https://github.com/linq2db/linq2db/wiki/Managing-data-connection对象名称:'DataConnection'。
答案 0 :(得分:1)
您遇到的错误“此命令关联了一个开放的阅读器。在更改CommandText属性之前将其关闭”,这表明已打开多个阅读器。但是,查看您的查询似乎为您的一个查询打开了一个阅读器。 但是,现实是不同的。您有5个表,因此每个表与另一个表之间存在1对多的关系。例如表患者与处方表具有一对多的关系。作为一个病人可以有多种处方。
因此,仅考虑这两个表,我们首先有一个查询以加载所有患者,然后每个患者另一个查询以加载其所有处方,这意味着如果您有N个患者,这将转换为1 + N查询, 1加载所有患者,N加载每个患者的处方。
现在,考虑到您在代码中具有5级联接,进行数学运算,您可以看到有多少潜在的开放阅读器。 数据按需加载,这意味着在您遍历查询结果后便会激活读取器,这是为了避免占用大量内存,从而降低了性能,因此在foreach循环中,您开始遍历对象,该数据是实际获取的。
要解决此问题,您可以尝试在查询的末尾转换ToList以鼓励绑定(因此请尽快加载),或者其中一位评论者建议将MultipleActiveResultSets = true传递给您的连接字符串。
答案 1 :(得分:1)
您应该从查询中使用的表中删除AsEnumerable()
调用,因为它们会迫使linq2db作为单独的查询执行它们。
这有两个后果:
InvalidOperationException
AsEnumerable()
替换为ToList()
来读取每个查询的所有数据)。在这种情况下,它将所有数据加载到应用程序中并使用C#代码执行联接-这将导致非常糟糕的性能,尤其是在需要丢弃一些不符合联接条件的数据的情况下。