C# 如何根据列表优化列表查询
我正在使用Xamarin.Forms构建一个应用程序,我在数据中遇到了一个非常缓慢的查询,如果可能的话,我需要对其进行优化 为了理解我试图构建的问题,我需要很好地解释数据库关系。我试图构建的业务软件允许用户将员工安排到班组,班组安排到作业操作中。出于本说明的目的,我们可以忽略作业,即使数据库对象的名称中包含“作业” 为了逐日安排员工,我创建了一个看板,允许用户将员工姓名拖到他们希望安排员工的工作组,当他们完成编辑时,他们使用工具栏按钮导航到他们希望安排的下一个日期。在后台,代码正在创建数据库对象,这些对象创建员工、日期和工作人员之间的链接 为了每天安排操作,我创建了一个侧面滚动甘特图样式的调度器,允许用户将操作块拖到一天的工作组上。在后台,代码正在创建数据库对象,这些对象创建操作、日期和工作人员之间的链接 下面是数据库对象外观的简单版本C# 如何根据列表优化列表查询,c#,performance,linq,C#,Performance,Linq,我正在使用Xamarin.Forms构建一个应用程序,我在数据中遇到了一个非常缓慢的查询,如果可能的话,我需要对其进行优化 为了理解我试图构建的问题,我需要很好地解释数据库关系。我试图构建的业务软件允许用户将员工安排到班组,班组安排到作业操作中。出于本说明的目的,我们可以忽略作业,即使数据库对象的名称中包含“作业” 为了逐日安排员工,我创建了一个看板,允许用户将员工姓名拖到他们希望安排员工的工作组,当他们完成编辑时,他们使用工具栏按钮导航到他们希望安排的下一个日期。在后台,代码正在创建数据库对象
public interface IEmployee
{
long? Id { get; set; }
string Name { get; }
string Password { get; set; }
}
因此,当我想找出一名员工安排了哪些操作时,情况就变得复杂了。我必须首先查询以查找员工的日程对象,创建一个员工日程安排的班组/日期列表,然后获取一个与日期/班组列表匹配的工作日程列表,然后将其归结为一个不同的操作列表(因为一个操作可以跨多天安排)。以下是我当前执行此操作的代码:
public async Task<List<IOperation>> GetOperationsByEmployee(IDataService<IJobSchedule> JobScheduleRepository)
{
JobScheduleRepository = JobScheduleRepository ??
throw new ArgumentNullException(nameof(JobScheduleRepository));
var result = new List<IOperation>();
var empSchedMatches = await GetEmployeeSchedules().ConfigureAwait(false);
var jobSchedules = await GetJobSchedules(JobScheduleRepository, empSchedMatches).ConfigureAwait(false);
result = jobSchedules.Select(x => x.Operation).Distinct().ToList();
return result;
}
因此,当我使用模拟数据运行应用程序时,内存中大约有10个对象,运行时间约为100毫秒。当我在真实数据库中有50000个对象,需要30名员工、10名工作人员、500个工作和1500个操作进行排序时,需要约7000毫秒。这种比较可能很明显,但关键是我需要找到某种方法,如果可能的话,以优化查询。如果可以的话,我想让它接近1秒的加载时间
一如既往,谢谢你的帮助
编辑
恐怕我没有得到我所希望的答案,因为我并不是真的在问题的数据访问方面寻求建议,我是在问题的LINQ方面寻求建议。我不知道这是否有助于理解数据访问场景,因此我将简要解释
我用Xamarin.Forms编写代码,用作依赖项注入器。我试图使用接口来允许从数据服务中抽象出对数据服务的调用
数据存储在SQL Server中,位于办公室的服务器上。该应用程序正在使用一个名为SQL-to-SQLite的API。Zumero将请求的表与SQL Server同步,并将它们存放到移动设备上的本地文件中
我正在使用将数据提供给程序,同样是通过使用接口和字段映射来尝试将对数据库对象的调用从数据库对象本身中抽象出来
编辑2
我将尝试在这里重新提问,以便更清楚地了解我在寻找什么:
我有一个SQLite文件,其中包含员工、操作、每日员工计划和每日操作计划。我可以通过哪些方式编写查询,以获取员工已安排的操作列表
假想问题:
Bob当前计划的操作是什么
数据表中的虚拟行:
谢谢你的帮助 答案很简单,您正在创建整个表到运行时,并通过C#匹配它们,这是错误的 这就是数据库的用途,您必须使用它
您有许多选择、查询、视图和存储过程,但可以肯定的是,询问整个数据库并通过代码执行匹配是一种错误的方式。为了加快查询速度,您可以做很多事情。我将实施一个明显的变化(如果我足够了解流程): 您似乎对每个对象都要这样做,所以应该对所有代码进行重构
您还可以尝试为该操作编写一个存储过程,并保留ORM超时。至少从我阅读本文的方式来看,您正在执行大量查询,实际上是在内存中查询之前从数据库中获取整个数据集。如果您发现有一些记录,那么这很好,但是如果您在每个请求前加载整个表,您将发现自己完全陷入瓶颈,应用程序最终将完全停止运行 这里有很多代码,只是作为一个例子
var allJobSched = await JobScheduleRepository.GetItemsAsync().ConfigureAwait(false);
allJobSched = allJobSched.Where(x => x.Date >= Date && x.Crew != null && x.Operation != null);
int count = allJobSched.Count();
在上面的代码片段中,首先从数据库中提取所有作业计划,这必然会涉及从数据库中提取大量数据(不太好)
如果你写些类似的东西,你会好得多
public interface IOperation
{
long? Id { get; set; }
int Priority { get; set; }
}
public async Task<List<IOperation>> GetOperationsByEmployee(IDataService<IJobSchedule> JobScheduleRepository)
{
JobScheduleRepository = JobScheduleRepository ??
throw new ArgumentNullException(nameof(JobScheduleRepository));
var result = new List<IOperation>();
var empSchedMatches = await GetEmployeeSchedules().ConfigureAwait(false);
var jobSchedules = await GetJobSchedules(JobScheduleRepository, empSchedMatches).ConfigureAwait(false);
result = jobSchedules.Select(x => x.Operation).Distinct().ToList();
return result;
}
private async Task<IEnumerable<ICrewMember>> GetEmployeeSchedules()
{
//Get complete list of employee schedules to sort through
var allEmpSched = await CrewMemberRepository.GetItemsAsync().ConfigureAwait(false);
//Get schedules with date greater than or equal to Date for this employee
var empSchedMatches = allEmpSched.Where(x => x.Date >= Date && x.Employee == Employee);
return empSchedMatches;
}
private async Task<IEnumerable<IJobSchedule>> GetJobSchedules(IDataService<IJobSchedule> JobScheduleRepository, IEnumerable<ICrewMember> employeeSchedules)
{
//Get complete list of job schedules to sort through
var allJobSched = await JobScheduleRepository.GetItemsAsync().ConfigureAwait(false);
allJobSched = allJobSched.Where(x => x.Date >= Date && x.Crew != null && x.Operation != null);
int count = allJobSched.Count();
var result = new List<IJobSchedule>();
foreach (var empSched in employeeSchedules)
{
//For each employee schedule, there should be 1 matching job schedule
//if the crew was assigned a job for that day
var matches = allJobSched.Where(x => x.Date == empSched.Date && x.Crew == empSched.Crew);
result.AddRange(matches);
string message = $"GetJobSchedules() comparing ({count}) Job Schedules " +
$"to empSched.{empSched.Id} crew.{empSched.Crew.Id} date.{empSched.Date:M/d}";
System.Diagnostics.Debug.WriteLine(message);
}
return result;
}
[0:] Method Called: GetOperationsByEmployee
[0:] GetOperationsByEmployee() executing query...
[0:] Method Called: GetEmployeeSchedules
[0:] Method Called: GetJobSchedules
[0:] GetJobSchedules() comparing (51) Job Schedules to empSched.17196 crew.3 date.2/6
[0:] GetJobSchedules() comparing (51) Job Schedules to empSched.18096 crew.3 date.2/4
[0:] GetJobSchedules() comparing (51) Job Schedules to empSched.18221 crew.3 date.2/3
[0:] GetJobSchedules() comparing (51) Job Schedules to empSched.18902 crew.3 date.2/7
[0:] GetJobSchedules() comparing (51) Job Schedules to empSched.21243 crew.3 date.1/27
[0:] GetJobSchedules() comparing (51) Job Schedules to empSched.21321 crew.3 date.1/28
[0:] GetJobSchedules() comparing (51) Job Schedules to empSched.21360 crew.3 date.1/29
[0:] GetJobSchedules() comparing (51) Job Schedules to empSched.21399 crew.3 date.1/30
[0:] GetJobSchedules() comparing (51) Job Schedules to empSched.21438 crew.3 date.1/31
[0:] GetJobSchedules() comparing (51) Job Schedules to empSched.21528 crew.3 date.2/5
[0:] Data loaded 6391 ms
//change to something where you pass your parameters as to not need to load all of your jobs every time
var allJobSched = await JobScheduleRepository.GetItemsAsync().ConfigureAwait(false);
//change to
var matchingJobSched = await JobScheduleRepository.FindMatchingJobSchedules(DateTime date, int crewId).ConfigureAwait(false);
var allJobSched = await JobScheduleRepository.GetItemsAsync().ConfigureAwait(false);
allJobSched = allJobSched.Where(x => x.Date >= Date && x.Crew != null && x.Operation != null);
int count = allJobSched.Count();
var jobs= await JobScheduleRepository.GetValidJobsAfterDate(Date);
private async Task<IEnumerable<ICrewMember>> GetEmployeeSchedules()
{
//Get complete list of employee schedules to sort through
var allEmpSched = await CrewMemberRepository.GetItemsAsync().ConfigureAwait(false);
//Get schedules with date greater than or equal to Date for this employee
var empSchedMatches = allEmpSched.Where(x => x.Date >= Date && x.Employee == Employee);
return empSchedMatches;
}
var dict = new Dictionary<Employee, List<IJobSchedule>>
foreach(var item in allEmpSched) {
if (dict.TryGetValue(item.Employee, out var schedules)) {
schedules.Add(item)
} else {
dict[item.Employee] = new List<IJobSchedule>() { item };
}
}
public async Task<List<IOperation>> GetOperationsByEmployee(
IDataService<IJobSchedule> JobScheduleRepository,
DateTime Date,
IEmployee Employee)
{
var result = new List<IOperation>();
var empSchedMatches = allEmployeeSchedules.Where(x => x.Date >= Date && x.Employee == Employee);
var jobSchedules = new List<IJob>();
foreach (var empSched in empSchedMatches)
{
var matches = allJobSched.Where(x => x.Date == empSched.Date && x.Crew == empSched.Crew);
jobSchedules.AddRange(matches);
}
result = jobSchedules.Select(x => x.Operation).Distinct().ToList();
return result;
}
internal class ReadOnlyEFDatabase : EFDatabase
{
public ReadOnlyEFDatabase(string dbPath, DbContextOptions options) : base(dbPath, options)
{
this.ChangeTracker.AutoDetectChangesEnabled = false;
this.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
public override int SaveChanges()
{
throw new Exception("Attempting to save changes from a read-only connection to the database.");
}
}