LINQ具有不同对象的多个联接

时间:2018-08-24 11:46:46

标签: c# linq linq2db

我一直在研究一个使用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'。

2 个答案:

答案 0 :(得分:1)

您遇到的错误“此命令关联了一个开放的阅读器。在更改CommandText属性之前将其关闭”,这表明已打开多个阅读器。但是,查看您的查询似乎为您的一个查询打开了一个阅读器。 但是,现实是不同的。您有5个表,因此每个表与另一个表之间存在1对多的关系。例如表患者与处方表具有一对多的关系。作为一个病人可以有多种处方。

因此,仅考虑这两个表,我们首先有一个查询以加载所有患者,然后每个患者另一个查询以加载其所有处方,这意味着如果您有N个患者,这将转换为1 + N查询, 1加载所有患者,N加载每个患者的处方。

现在,考虑到您在代码中具有5级联接,进行数学运算,您可以看到有多少潜在的开放阅读器。 数据按需加载,这意味着在您遍历查询结果后便会激活读取器,这是为了避免占用大量内存,从而降低了性能,因此在foreach循环中,您开始遍历对象,该数据是实际获取的。

要解决此问题,您可以尝试在查询的末尾转换ToList以鼓励绑定(因此请尽快加载),或者其中一位评论者建议将MultipleActiveResultSets = true传递给您的连接字符串。

答案 1 :(得分:1)

您应该从查询中使用的表中删除AsEnumerable()调用,因为它们会迫使linq2db作为单独的查询执行它们。

这有两个后果:

  • 它尝试通过单个数据库连接启动多个查询,这不受支持,并且您得到InvalidOperationException
  • 让我们想象一下它会起作用(例如,您将AsEnumerable()替换为ToList()来读取每个查询的所有数据)。在这种情况下,它将所有数据加载到应用程序中并使用C#代码执行联接-这将导致非常糟糕的性能,尤其是在需要丢弃一些不符合联接条件的数据的情况下。