Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HivePerformance/sources/HeuristicLab.Services.Hive/3.3/HiveDao.cs @ 9381

Last change on this file since 9381 was 9381, checked in by pfleck, 11 years ago

#2030
Activated Delayed Loading for binary data.
Added HiveOperationContext to store HiveDataContext for whole ServiceOperation duration.
Added HiveDao methods to query database objects, not DTOs.
Changed HartbeatManager to use only database objects from new queries.

File size: 44.5 KB
RevLine 
[6983]1#region License Information
2/* HeuristicLab
[7259]3 * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[6983]4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Linq.Expressions;
26using DT = HeuristicLab.Services.Hive.DataTransfer;
27
28namespace HeuristicLab.Services.Hive.DataAccess {
29  public class HiveDao : IHiveDao {
30    public static HiveDataContext CreateContext(bool longRunning = false) {
31      var context = new HiveDataContext(Settings.Default.HeuristicLab_Hive_LinqConnectionString);
32      if (longRunning) context.CommandTimeout = (int)Settings.Default.LongRunningDatabaseCommandTimeout.TotalSeconds;
33      return context;
34    }
35
36    public HiveDao() { }
37
38    #region Task Methods
39    public DT.Task GetTask(Guid id) {
40      using (var db = CreateContext()) {
41        return DT.Convert.ToDto(db.Tasks.SingleOrDefault(x => x.TaskId == id));
42      }
43    }
44
[9381]45    public Task GetTaskDA(Guid id) {
46      var db = HiveOperationContext.Current.DataContext;
47      return db.Tasks.SingleOrDefault(x => x.TaskId == id);
48    }
49
[6983]50    public IEnumerable<DT.Task> GetTasks(Expression<Func<Task, bool>> predicate) {
51      using (var db = CreateContext()) {
52        return db.Tasks.Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
53      }
54    }
55
[9022]56    public IEnumerable<DT.LightweightTask> GetLightweightTasks(Expression<Func<Task, bool>> predicate) {
57      List<DT.LightweightTask> tasks = new List<DT.LightweightTask>();
58
59      using (var db = CreateContext()) {
60        var tasksQuery = db.Tasks.Where(predicate).Select(task => new { task.TaskId, task.ExecutionTimeMs, task.ParentTaskId, task.StateLogs, task.State, task.Command });
61        var taskDatasQuery = db.Tasks.Where(predicate).Where(task => task.JobData != null).Select(task => new { task.TaskId, task.JobData.LastUpdate });
62
63        foreach (var task in tasksQuery) {
64          DT.LightweightTask t = new DT.LightweightTask();
65          t.Id = task.TaskId;
66          t.ExecutionTime = TimeSpan.FromMilliseconds(task.ExecutionTimeMs);
67          t.ParentTaskId = task.ParentTaskId;
68          t.StateLog = task.StateLogs == null ? new List<DT.StateLog>() : task.StateLogs.Select(x => DataTransfer.Convert.ToDto(x)).OrderBy(x => x.DateTime).ToList();
69          t.State = DataTransfer.Convert.ToDto(task.State);
70          t.Command = DataTransfer.Convert.ToDto(task.Command);
71          t.LastTaskDataUpdate = taskDatasQuery.Where(x => x.TaskId == task.TaskId).Count() > 0 ? taskDatasQuery.Select(x => x.LastUpdate).First() : DateTime.MinValue;
72          tasks.Add(t);
73        }
74      }
75      return tasks;
76    }
77
[9219]78    public IEnumerable<DT.LightweightTask> GetLightweightTasksWithoutStateLog(Expression<Func<Task, bool>> predicate) {
79      List<DT.LightweightTask> tasks = new List<DT.LightweightTask>();
80
81      using (var db = CreateContext()) {
82        var tasksQuery = db.Tasks.Where(predicate).Select(task => new { task.TaskId, task.ExecutionTimeMs, task.ParentTaskId, task.State, task.Command });
83        var taskDatasQuery = db.Tasks.Where(predicate).Where(task => task.JobData != null).Select(task => new { task.TaskId, task.JobData.LastUpdate });
84
85        foreach (var task in tasksQuery) {
86          DT.LightweightTask t = new DT.LightweightTask();
87          t.Id = task.TaskId;
88          t.ExecutionTime = TimeSpan.FromMilliseconds(task.ExecutionTimeMs);
89          t.ParentTaskId = task.ParentTaskId;
90          t.StateLog = new List<DT.StateLog>();
91          t.State = DataTransfer.Convert.ToDto(task.State);
92          t.Command = DataTransfer.Convert.ToDto(task.Command);
93          t.LastTaskDataUpdate = taskDatasQuery.Where(x => x.TaskId == task.TaskId).Count() > 0 ? taskDatasQuery.Select(x => x.LastUpdate).First() : DateTime.MinValue;
94          tasks.Add(t);
95        }
96      }
97      return tasks;
98    }
99
[6983]100    public Guid AddTask(DT.Task dto) {
101      using (var db = CreateContext()) {
102        var entity = DT.Convert.ToEntity(dto);
103        db.Tasks.InsertOnSubmit(entity);
104        db.SubmitChanges();
105        foreach (Guid pluginId in dto.PluginsNeededIds) {
106          db.RequiredPlugins.InsertOnSubmit(new RequiredPlugin() { TaskId = entity.TaskId, PluginId = pluginId });
107        }
108        db.SubmitChanges();
109        return entity.TaskId;
110      }
111    }
112
[9123]113    public void UpdateTaskAndPlugins(DT.Task dto) {
[6983]114      using (var db = CreateContext()) {
115        var entity = db.Tasks.FirstOrDefault(x => x.TaskId == dto.Id);
116        if (entity == null) db.Tasks.InsertOnSubmit(DT.Convert.ToEntity(dto));
117        else DT.Convert.ToEntity(dto, entity);
118        foreach (Guid pluginId in dto.PluginsNeededIds) {
119          if (db.RequiredPlugins.Count(p => p.PluginId == pluginId) == 0) {
120            db.RequiredPlugins.InsertOnSubmit(new RequiredPlugin() { TaskId = entity.TaskId, PluginId = pluginId });
121          }
122        }
123        db.SubmitChanges();
124      }
125    }
126
[9266]127    public void UpdateTaskAndStateLogs(DT.Task dto) {
[9123]128      using (var db = CreateContext()) {
129        var entity = db.Tasks.FirstOrDefault(x => x.TaskId == dto.Id);
130        if (entity == null) db.Tasks.InsertOnSubmit(DT.Convert.ToEntity(dto));
131        else DT.Convert.ToEntity(dto, entity);
132        db.SubmitChanges();
133      }
134    }
135
[9266]136    public void UpdateTask(DT.Task dto) {
137      using (var db = CreateContext()) {
138        db.DeferredLoadingEnabled = false;
139
140        var entity = db.Tasks.FirstOrDefault(x => x.TaskId == dto.Id);
141        if (entity == null) db.Tasks.InsertOnSubmit(DT.Convert.ToEntity(dto));
142        else DT.Convert.ToEntityTaskOnly(dto, entity);
143        db.SubmitChanges();
144      }
145    }
146
[9381]147    public void UpdateTaskDA(Task task) {
148      var db = HiveOperationContext.Current.DataContext;
149      db.SubmitChanges();
150    }
151
[6983]152    public void DeleteTask(Guid id) {
153      using (var db = CreateContext()) {
154        var entity = db.Tasks.FirstOrDefault(x => x.TaskId == id);
155        if (entity != null) db.Tasks.DeleteOnSubmit(entity);
156        db.SubmitChanges(); // taskData and child tasks are deleted by db-trigger
157      }
158    }
159
160    /// <summary>
161    /// returns all parent tasks which are waiting for their child tasks to finish
162    /// </summary>
163    /// <param name="resourceIds">list of resourceids which for which the task should be valid</param>
164    /// <param name="count">maximum number of task to return</param>
165    /// <param name="finished">if true, all parent task which have FinishWhenChildJobsFinished=true are returned, otherwise only FinishWhenChildJobsFinished=false are returned</param>
166    /// <returns></returns>
167    public IEnumerable<DT.Task> GetParentTasks(IEnumerable<Guid> resourceIds, int count, bool finished) {
168      using (var db = CreateContext()) {
169        var query = from ar in db.AssignedResources
170                    where resourceIds.Contains(ar.ResourceId)
171                       && ar.Task.State == TaskState.Waiting
172                       && ar.Task.IsParentTask
173                       && (finished ? ar.Task.FinishWhenChildJobsFinished : !ar.Task.FinishWhenChildJobsFinished)
174                       && (from child in db.Tasks
175                           where child.ParentTaskId == ar.Task.TaskId
176                           select child.State == TaskState.Finished
177                               || child.State == TaskState.Aborted
178                               || child.State == TaskState.Failed).All(x => x)
179                       && (from child in db.Tasks // avoid returning WaitForChildTasks task where no child-task exist (yet)
180                           where child.ParentTaskId == ar.Task.TaskId
181                           select child).Count() > 0
182                    orderby ar.Task.Priority descending, db.Random()
183                    select DT.Convert.ToDto(ar.Task);
184        return count == 0 ? query.ToArray() : query.Take(count).ToArray();
185      }
186    }
187
[9123]188    public IEnumerable<TaskInfoForScheduler> GetWaitingTasks(DT.Slave slave) {
[6983]189      using (var db = CreateContext()) {
190        var resourceIds = GetParentResources(slave.Id).Select(r => r.Id);
[7178]191        //Originally we checked here if there are parent tasks which should be calculated (with GetParentTasks(resourceIds, count, false);).
192        //Because there is at the moment no case where this makes sense (there don't exist parent tasks which need to be calculated),
193        //we skip this step because it's wasted runtime
[6983]194
195        var query = from ar in db.AssignedResources
196                    where resourceIds.Contains(ar.ResourceId)
197                       && !(ar.Task.IsParentTask && ar.Task.FinishWhenChildJobsFinished)
198                       && ar.Task.State == TaskState.Waiting
199                       && ar.Task.CoresNeeded <= slave.FreeCores
200                       && ar.Task.MemoryNeeded <= slave.FreeMemory
[9123]201                    select new TaskInfoForScheduler() { TaskId = ar.Task.TaskId, JobId = ar.Task.JobId, Priority = ar.Task.Priority };
202        var waitingTasks = query.ToArray();
[7178]203        return waitingTasks;
[6983]204      }
205    }
206
[9381]207    public IEnumerable<TaskInfoForScheduler> GetWaitingTasksDA(Slave slave) {
208      var db = HiveOperationContext.Current.DataContext;
209      var parentResources = GetParentResourcesDA(slave.ResourceId);
210      var resourceIds = parentResources.Select(x => x.ResourceId);
211      //Originally we checked here if there are parent tasks which should be calculated (with GetParentTasks(resourceIds, count, false);).
212      //Because there is at the moment no case where this makes sense (there don't exist parent tasks which need to be calculated),
213      //we skip this step because it's wasted runtime
214
215      var query = from ar in db.AssignedResources
216                  where resourceIds.Contains(ar.ResourceId)
217                     && !(ar.Task.IsParentTask && ar.Task.FinishWhenChildJobsFinished)
218                     && ar.Task.State == TaskState.Waiting
219                     && ar.Task.CoresNeeded <= slave.FreeCores
220                     && ar.Task.MemoryNeeded <= slave.FreeMemory
221                  select new TaskInfoForScheduler() { TaskId = ar.Task.TaskId, JobId = ar.Task.JobId, Priority = ar.Task.Priority };
222      var waitingTasks = query.ToArray();
223      return waitingTasks;
224    }
225
[6983]226    public DT.Task UpdateTaskState(Guid taskId, TaskState taskState, Guid? slaveId, Guid? userId, string exception) {
227      using (var db = CreateContext()) {
[9266]228        db.DeferredLoadingEnabled = false;
229
[6983]230        db.StateLogs.InsertOnSubmit(new StateLog {
231          TaskId = taskId,
232          State = taskState,
233          SlaveId = slaveId,
234          UserId = userId,
235          Exception = exception,
236          DateTime = DateTime.Now
237        });
238        db.SubmitChanges();
239      }
[9266]240
241      using (var db = CreateContext()) {
242        db.DeferredLoadingEnabled = false;
243
244        var task = db.Tasks.SingleOrDefault(x => x.TaskId == taskId);
245        task.State = taskState;
246        db.SubmitChanges();
[9304]247      }
248
249      using (var db = CreateContext()) {
250        var task = db.Tasks.SingleOrDefault(x => x.TaskId == taskId);
[9266]251        return DT.Convert.ToDto(task);
252      }
[6983]253    }
[9381]254
255    public Task UpdateTaskStateDA(Guid taskId, TaskState taskState, Guid? slaveId, Guid? userId, string exception) {
256      var db = HiveOperationContext.Current.DataContext;
257
258      db.StateLogs.InsertOnSubmit(new StateLog {
259        TaskId = taskId,
260        State = taskState,
261        SlaveId = slaveId,
262        UserId = userId,
263        Exception = exception,
264        DateTime = DateTime.Now
265      });
266
267      var task = db.Tasks.SingleOrDefault(x => x.TaskId == taskId);
268      task.State = taskState;
269
270      db.SubmitChanges();
271
272      return task;
273    }
[6983]274    #endregion
275
276    #region TaskData Methods
277    public DT.TaskData GetTaskData(Guid id) {
278      using (var db = CreateContext(true)) {
279        return DT.Convert.ToDto(db.TaskDatas.SingleOrDefault(x => x.TaskId == id));
280      }
281    }
282
283    public IEnumerable<DT.TaskData> GetTaskDatas(Expression<Func<TaskData, bool>> predicate) {
284      using (var db = CreateContext(true)) {
285        return db.TaskDatas.Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
286      }
287    }
288
289    public Guid AddTaskData(DT.TaskData dto) {
290      using (var db = CreateContext(true)) {
291        var entity = DT.Convert.ToEntity(dto);
292        db.TaskDatas.InsertOnSubmit(entity);
293        db.SubmitChanges();
294        return entity.TaskId;
295      }
296    }
297
298    public void UpdateTaskData(DT.TaskData dto) {
299      using (var db = CreateContext(true)) {
300        var entity = db.TaskDatas.FirstOrDefault(x => x.TaskId == dto.TaskId);
301        if (entity == null) db.TaskDatas.InsertOnSubmit(DT.Convert.ToEntity(dto));
302        else DT.Convert.ToEntity(dto, entity);
303        db.SubmitChanges();
304      }
305    }
306
307    public void DeleteTaskData(Guid id) {
308      using (var db = CreateContext()) {
309        var entity = db.TaskDatas.FirstOrDefault(x => x.TaskId == id); // check if all the byte[] is loaded into memory here. otherwise work around to delete without loading it
310        if (entity != null) db.TaskDatas.DeleteOnSubmit(entity);
311        db.SubmitChanges();
312      }
313    }
314    #endregion
315
316    #region StateLog Methods
317    public DT.StateLog GetStateLog(Guid id) {
318      using (var db = CreateContext()) {
319        return DT.Convert.ToDto(db.StateLogs.SingleOrDefault(x => x.StateLogId == id));
320      }
321    }
322
323    public IEnumerable<DT.StateLog> GetStateLogs(Expression<Func<StateLog, bool>> predicate) {
324      using (var db = CreateContext()) {
325        return db.StateLogs.Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
326      }
327    }
328
329    public Guid AddStateLog(DT.StateLog dto) {
330      using (var db = CreateContext()) {
331        var entity = DT.Convert.ToEntity(dto);
332        db.StateLogs.InsertOnSubmit(entity);
333        db.SubmitChanges();
334        return entity.StateLogId;
335      }
336    }
337
338    public void UpdateStateLog(DT.StateLog dto) {
339      using (var db = CreateContext()) {
340        var entity = db.StateLogs.FirstOrDefault(x => x.StateLogId == dto.Id);
341        if (entity == null) db.StateLogs.InsertOnSubmit(DT.Convert.ToEntity(dto));
342        else DT.Convert.ToEntity(dto, entity);
343        db.SubmitChanges();
344      }
345    }
346
347    public void DeleteStateLog(Guid id) {
348      using (var db = CreateContext()) {
349        var entity = db.StateLogs.FirstOrDefault(x => x.StateLogId == id);
350        if (entity != null) db.StateLogs.DeleteOnSubmit(entity);
351        db.SubmitChanges();
352      }
353    }
354    #endregion
355
356    #region Job Methods
357    public DT.Job GetJob(Guid id) {
358      using (var db = CreateContext()) {
359        return AddStatsToJob(db, DT.Convert.ToDto(db.Jobs.SingleOrDefault(x => x.JobId == id)));
360      }
361    }
362
363    private DT.Job AddStatsToJob(HiveDataContext db, DT.Job exp) {
364      if (exp == null)
365        return null;
366
367      var jobs = db.Tasks.Where(j => j.JobId == exp.Id);
368      exp.JobCount = jobs.Count();
369      exp.CalculatingCount = jobs.Count(j => j.State == TaskState.Calculating);
370      exp.FinishedCount = jobs.Count(j => j.State == TaskState.Finished);
371      return exp;
372    }
373
374    public IEnumerable<DT.Job> GetJobs(Expression<Func<Job, bool>> predicate) {
375      using (var db = CreateContext()) {
376        return db.Jobs.Where(predicate).Select(x => AddStatsToJob(db, DT.Convert.ToDto(x))).ToArray();
377      }
378    }
379
[9123]380    public IEnumerable<JobInfoForScheduler> GetJobInfoForScheduler(Expression<Func<Job, bool>> predicate) {
381      using (var db = CreateContext()) {
382        return db.Jobs.Where(predicate).Select(x => new JobInfoForScheduler() { Id = x.JobId, DateCreated = x.DateCreated, OwnerUserId = x.OwnerUserId }).ToArray();
383      }
384    }
385
[6983]386    public Guid AddJob(DT.Job dto) {
387      using (var db = CreateContext()) {
388        var entity = DT.Convert.ToEntity(dto);
389        db.Jobs.InsertOnSubmit(entity);
[9123]390        if (!db.UserPriorities.Any(x => x.UserId == dto.OwnerUserId))
391          EnqueueUserPriority(new DT.UserPriority { Id = dto.OwnerUserId, DateEnqueued = dto.DateCreated });
[6983]392        db.SubmitChanges();
393        return entity.JobId;
394      }
395    }
396
397    public void UpdateJob(DT.Job dto) {
398      using (var db = CreateContext()) {
399        var entity = db.Jobs.FirstOrDefault(x => x.JobId == dto.Id);
400        if (entity == null) db.Jobs.InsertOnSubmit(DT.Convert.ToEntity(dto));
401        else DT.Convert.ToEntity(dto, entity);
402        db.SubmitChanges();
403      }
404    }
405
406    public void DeleteJob(Guid id) {
407      using (var db = CreateContext()) {
408        var entity = db.Jobs.FirstOrDefault(x => x.JobId == id);
409        if (entity != null) db.Jobs.DeleteOnSubmit(entity);
410        db.SubmitChanges();
411      }
412    }
413    #endregion
414
415    #region JobPermission Methods
416    public DT.JobPermission GetJobPermission(Guid jobId, Guid grantedUserId) {
417      using (var db = CreateContext()) {
418        return DT.Convert.ToDto(db.JobPermissions.SingleOrDefault(x => x.JobId == jobId && x.GrantedUserId == grantedUserId));
419      }
420    }
421
422    public IEnumerable<DT.JobPermission> GetJobPermissions(Expression<Func<JobPermission, bool>> predicate) {
423      using (var db = CreateContext()) {
424        return db.JobPermissions.Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
425      }
426    }
427
428    public void AddJobPermission(DT.JobPermission dto) {
429      using (var db = CreateContext()) {
430        var entity = DT.Convert.ToEntity(dto);
431        db.JobPermissions.InsertOnSubmit(entity);
432        db.SubmitChanges();
433      }
434    }
435
436    public void UpdateJobPermission(DT.JobPermission dto) {
437      using (var db = CreateContext()) {
438        var entity = db.JobPermissions.FirstOrDefault(x => x.JobId == dto.JobId && x.GrantedUserId == dto.GrantedUserId);
439        if (entity == null) db.JobPermissions.InsertOnSubmit(DT.Convert.ToEntity(dto));
440        else DT.Convert.ToEntity(dto, entity);
441        db.SubmitChanges();
442      }
443    }
444
445    public void DeleteJobPermission(Guid jobId, Guid grantedUserId) {
446      using (var db = CreateContext()) {
447        var entity = db.JobPermissions.FirstOrDefault(x => x.JobId == jobId && x.GrantedUserId == grantedUserId);
448        if (entity != null) db.JobPermissions.DeleteOnSubmit(entity);
449        db.SubmitChanges();
450      }
451    }
452
453    /// <summary>
454    /// Sets the permissions for a experiment. makes sure that only one permission per user exists.
455    /// </summary>
456    public void SetJobPermission(Guid jobId, Guid grantedByUserId, Guid grantedUserId, Permission permission) {
457      using (var db = CreateContext()) {
458        JobPermission jobPermission = db.JobPermissions.SingleOrDefault(x => x.JobId == jobId && x.GrantedUserId == grantedUserId);
459        if (jobPermission != null) {
460          if (permission == Permission.NotAllowed) {
461            // not allowed, delete
462            db.JobPermissions.DeleteOnSubmit(jobPermission);
463          } else {
464            // update
465            jobPermission.Permission = permission;
466            jobPermission.GrantedByUserId = grantedByUserId; // update grantedByUserId, always the last "granter" is stored
467          }
468        } else {
469          // insert
470          if (permission != Permission.NotAllowed) {
471            jobPermission = new JobPermission() { JobId = jobId, GrantedByUserId = grantedByUserId, GrantedUserId = grantedUserId, Permission = permission };
472            db.JobPermissions.InsertOnSubmit(jobPermission);
473          }
474        }
475        db.SubmitChanges();
476      }
477    }
478    #endregion
479
480    #region Plugin Methods
481    public DT.Plugin GetPlugin(Guid id) {
482      using (var db = CreateContext()) {
483        return DT.Convert.ToDto(db.Plugins.SingleOrDefault(x => x.PluginId == id));
484      }
485    }
486
487    public IEnumerable<DT.Plugin> GetPlugins(Expression<Func<Plugin, bool>> predicate) {
488      using (var db = CreateContext()) {
489        return db.Plugins.Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
490      }
491    }
492
493    public Guid AddPlugin(DT.Plugin dto) {
494      using (var db = CreateContext()) {
495        var entity = DT.Convert.ToEntity(dto);
496        db.Plugins.InsertOnSubmit(entity);
497        db.SubmitChanges();
498        return entity.PluginId;
499      }
500    }
501
502    public void UpdatePlugin(DT.Plugin dto) {
503      using (var db = CreateContext()) {
504        var entity = db.Plugins.FirstOrDefault(x => x.PluginId == dto.Id);
505        if (entity == null) db.Plugins.InsertOnSubmit(DT.Convert.ToEntity(dto));
506        else DT.Convert.ToEntity(dto, entity);
507        db.SubmitChanges();
508      }
509    }
510
511    public void DeletePlugin(Guid id) {
512      using (var db = CreateContext()) {
513        var entity = db.Plugins.FirstOrDefault(x => x.PluginId == id);
514        if (entity != null) db.Plugins.DeleteOnSubmit(entity);
515        db.SubmitChanges();
516      }
517    }
518    #endregion
519
520    #region PluginData Methods
521    public DT.PluginData GetPluginData(Guid id) {
522      using (var db = CreateContext()) {
523        return DT.Convert.ToDto(db.PluginDatas.SingleOrDefault(x => x.PluginDataId == id));
524      }
525    }
526
527    public IEnumerable<DT.PluginData> GetPluginDatas(Expression<Func<PluginData, bool>> predicate) {
528      using (var db = CreateContext()) {
529        return db.PluginDatas.Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
530      }
531    }
532
533    public Guid AddPluginData(DT.PluginData dto) {
534      using (var db = CreateContext()) {
535        var entity = DT.Convert.ToEntity(dto);
536        db.PluginDatas.InsertOnSubmit(entity);
537        db.SubmitChanges();
538        return entity.PluginDataId;
539      }
540    }
541
542    public void UpdatePluginData(DT.PluginData dto) {
543      using (var db = CreateContext()) {
544        var entity = db.PluginDatas.FirstOrDefault(x => x.PluginId == dto.PluginId);
545        if (entity == null) db.PluginDatas.InsertOnSubmit(DT.Convert.ToEntity(dto));
546        else DT.Convert.ToEntity(dto, entity);
547        db.SubmitChanges();
548      }
549    }
550
551    public void DeletePluginData(Guid id) {
552      using (var db = CreateContext()) {
553        var entity = db.PluginDatas.FirstOrDefault(x => x.PluginDataId == id);
554        if (entity != null) db.PluginDatas.DeleteOnSubmit(entity);
555        db.SubmitChanges();
556      }
557    }
558    #endregion
559
560    #region Slave Methods
561    public DT.Slave GetSlave(Guid id) {
562      using (var db = CreateContext()) {
563        return DT.Convert.ToDto(db.Resources.OfType<Slave>().SingleOrDefault(x => x.ResourceId == id));
564      }
565    }
566
[9381]567    public Slave GetSlaveDA(Guid id) {
568      var db = HiveOperationContext.Current.DataContext;
569      return db.Resources.OfType<Slave>().SingleOrDefault(x => x.ResourceId == id);
570    }
571
[6983]572    public IEnumerable<DT.Slave> GetSlaves(Expression<Func<Slave, bool>> predicate) {
573      using (var db = CreateContext()) {
574        return db.Resources.OfType<Slave>().Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
575      }
576    }
577
578    public Guid AddSlave(DT.Slave dto) {
579      using (var db = CreateContext()) {
580        var entity = DT.Convert.ToEntity(dto);
581        db.Resources.InsertOnSubmit(entity);
582        db.SubmitChanges();
583        return entity.ResourceId;
584      }
585    }
586
587    public void UpdateSlave(DT.Slave dto) {
588      using (var db = CreateContext()) {
589        var entity = db.Resources.OfType<Slave>().FirstOrDefault(x => x.ResourceId == dto.Id);
590        if (entity == null) db.Resources.InsertOnSubmit(DT.Convert.ToEntity(dto));
591        else DT.Convert.ToEntity(dto, entity);
592        db.SubmitChanges();
593      }
594    }
595
[9381]596    public void UpdateSlaveDA(Slave slave) {
597      var db = HiveOperationContext.Current.DataContext;
598      db.SubmitChanges();
599    }
600
[6983]601    public void DeleteSlave(Guid id) {
602      using (var db = CreateContext()) {
603        var entity = db.Resources.OfType<Slave>().FirstOrDefault(x => x.ResourceId == id);
604        if (entity != null) db.Resources.DeleteOnSubmit(entity);
605        db.SubmitChanges();
606      }
607    }
608    #endregion
609
610    #region SlaveGroup Methods
611    public DT.SlaveGroup GetSlaveGroup(Guid id) {
612      using (var db = CreateContext()) {
613        return DT.Convert.ToDto(db.Resources.OfType<SlaveGroup>().SingleOrDefault(x => x.ResourceId == id));
614      }
615    }
616
617    public IEnumerable<DT.SlaveGroup> GetSlaveGroups(Expression<Func<SlaveGroup, bool>> predicate) {
618      using (var db = CreateContext()) {
619        return db.Resources.OfType<SlaveGroup>().Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
620      }
621    }
622
623    public Guid AddSlaveGroup(DT.SlaveGroup dto) {
624      using (var db = CreateContext()) {
625        if (dto.Id == Guid.Empty)
626          dto.Id = Guid.NewGuid();
627        var entity = DT.Convert.ToEntity(dto);
628        db.Resources.InsertOnSubmit(entity);
629        db.SubmitChanges();
630        return entity.ResourceId;
631      }
632    }
633
634    public void UpdateSlaveGroup(DT.SlaveGroup dto) {
635      using (var db = CreateContext()) {
636        var entity = db.Resources.OfType<SlaveGroup>().FirstOrDefault(x => x.ResourceId == dto.Id);
637        if (entity == null) db.Resources.InsertOnSubmit(DT.Convert.ToEntity(dto));
638        else DT.Convert.ToEntity(dto, entity);
639        db.SubmitChanges();
640      }
641    }
642
643    public void DeleteSlaveGroup(Guid id) {
644      using (var db = CreateContext()) {
645        var entity = db.Resources.OfType<SlaveGroup>().FirstOrDefault(x => x.ResourceId == id);
646        if (entity != null) {
647          if (db.Resources.Where(r => r.ParentResourceId == id).Count() > 0) {
648            throw new InvalidOperationException("Cannot delete SlaveGroup as long as there are Slaves in the group");
649          }
650          db.Resources.DeleteOnSubmit(entity);
651        }
652        db.SubmitChanges();
653      }
654    }
655    #endregion
656
657    #region Resource Methods
658    public DT.Resource GetResource(Guid id) {
659      using (var db = CreateContext()) {
660        return DT.Convert.ToDto(db.Resources.SingleOrDefault(x => x.ResourceId == id));
661      }
662    }
663
664    public IEnumerable<DT.Resource> GetResources(Expression<Func<Resource, bool>> predicate) {
665      using (var db = CreateContext()) {
666        return db.Resources.Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
667      }
668    }
669
670    public Guid AddResource(DT.Resource dto) {
671      using (var db = CreateContext()) {
672        var entity = DT.Convert.ToEntity(dto);
673        db.Resources.InsertOnSubmit(entity);
674        db.SubmitChanges();
675        return entity.ResourceId;
676      }
677    }
678
679    public void UpdateResource(DT.Resource dto) {
680      using (var db = CreateContext()) {
681        var entity = db.Resources.FirstOrDefault(x => x.ResourceId == dto.Id);
682        if (entity == null) db.Resources.InsertOnSubmit(DT.Convert.ToEntity(dto));
683        else DT.Convert.ToEntity(dto, entity);
684        db.SubmitChanges();
685      }
686    }
687
688    public void DeleteResource(Guid id) {
689      using (var db = CreateContext()) {
690        var entity = db.Resources.FirstOrDefault(x => x.ResourceId == id);
691        if (entity != null) db.Resources.DeleteOnSubmit(entity);
692        db.SubmitChanges();
693      }
694    }
695
[9257]696    public void AssignJobToResource(Guid taskId, IEnumerable<Guid> resourceIds) {
[6983]697      using (var db = CreateContext()) {
[9266]698        db.DeferredLoadingEnabled = false;
699
[9259]700        List<AssignedResource> assignedResources = new List<AssignedResource>();
[9257]701        foreach (Guid rId in resourceIds) {
[9259]702          assignedResources.Add(new AssignedResource() { TaskId = taskId, ResourceId = rId });
[9257]703        }
[9259]704        db.AssignedResources.InsertAllOnSubmit(assignedResources);
[6983]705        db.SubmitChanges();
706      }
707    }
708
709    public IEnumerable<DT.Resource> GetAssignedResources(Guid jobId) {
710      using (var db = CreateContext()) {
711        var job = db.Tasks.Where(x => x.TaskId == jobId).Single();
712        return job.AssignedResources.Select(x => DT.Convert.ToDto(x.Resource)).ToArray();
713      }
714    }
715
716    /// <summary>
717    /// Returns all parent resources of a resource (the given resource is also added)
718    /// </summary>
719    public IEnumerable<DT.Resource> GetParentResources(Guid resourceId) {
720      using (var db = CreateContext()) {
721        var resources = new List<Resource>();
722        CollectParentResources(resources, db.Resources.Where(r => r.ResourceId == resourceId).Single());
723        return resources.Select(r => DT.Convert.ToDto(r)).ToArray();
724      }
725    }
726
[9381]727    public IEnumerable<Resource> GetParentResourcesDA(Guid resourceId) {
728      var db = HiveOperationContext.Current.DataContext;
729      var resources = new List<Resource>();
730      CollectParentResources(resources, db.Resources.Where(r => r.ResourceId == resourceId).Single());
731      return resources;
732    }
733
[6983]734    private void CollectParentResources(List<Resource> resources, Resource resource) {
735      if (resource == null) return;
736      resources.Add(resource);
737      CollectParentResources(resources, resource.ParentResource);
738    }
739
740    /// <summary>
741    /// Returns all child resources of a resource (without the given resource)
742    /// </summary>
743    public IEnumerable<DT.Resource> GetChildResources(Guid resourceId) {
744      using (var db = CreateContext()) {
[9025]745        return CollectChildResources(resourceId, db);
[6983]746      }
747    }
748
[9025]749    public IEnumerable<DT.Resource> CollectChildResources(Guid resourceId, HiveDataContext db) {
750      var childs = new List<DT.Resource>();
751      foreach (var child in db.Resources.Where(x => x.ParentResourceId == resourceId)) {
752        childs.Add(DT.Convert.ToDto(child));
753        childs.AddRange(CollectChildResources(child.ResourceId, db));
754      }
755      return childs;
756    }
757
[6983]758    public IEnumerable<DT.Task> GetJobsByResourceId(Guid resourceId) {
759      using (var db = CreateContext()) {
760        var resources = GetChildResources(resourceId).Select(x => x.Id).ToList();
761        resources.Add(resourceId);
762
763        var jobs = db.Tasks.Where(j =>
764          j.State == TaskState.Calculating &&
765          j.StateLogs.OrderByDescending(x => x.DateTime).First().SlaveId.HasValue &&
766          resources.Contains(j.StateLogs.OrderByDescending(x => x.DateTime).First().SlaveId.Value));
767        return jobs.Select(j => DT.Convert.ToDto(j)).ToArray();
768      }
769    }
770    #endregion
771
[7916]772    #region ResourcePermission Methods
773    public DT.ResourcePermission GetResourcePermission(Guid resourceId, Guid grantedUserId) {
774      using (var db = CreateContext()) {
775        return DT.Convert.ToDto(db.ResourcePermissions.SingleOrDefault(x => x.ResourceId == resourceId && x.GrantedUserId == grantedUserId));
776      }
777    }
778
779    public IEnumerable<DT.ResourcePermission> GetResourcePermissions(Expression<Func<ResourcePermission, bool>> predicate) {
780      using (var db = CreateContext()) {
781        return db.ResourcePermissions.Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
782      }
783    }
784
785    public void AddResourcePermission(DT.ResourcePermission dto) {
786      using (var db = CreateContext()) {
787        var entity = db.ResourcePermissions.SingleOrDefault(x => x.ResourceId == dto.ResourceId && x.GrantedUserId == dto.GrantedUserId);
788        if (entity == null) { db.ResourcePermissions.InsertOnSubmit(DT.Convert.ToEntity(dto)); db.SubmitChanges(); }
789      }
790    }
791
792    public void UpdateResourcePermission(DT.ResourcePermission dto) {
793      using (var db = CreateContext()) {
794        var entity = db.ResourcePermissions.FirstOrDefault(x => x.ResourceId == dto.ResourceId && x.GrantedUserId == dto.GrantedUserId);
795        if (entity == null) db.ResourcePermissions.InsertOnSubmit(DT.Convert.ToEntity(dto));
796        else DT.Convert.ToEntity(dto, entity);
797        db.SubmitChanges();
798      }
799    }
800
801    public void DeleteResourcePermission(Guid resourceId, Guid grantedUserId) {
802      using (var db = CreateContext()) {
803        var entity = db.ResourcePermissions.FirstOrDefault(x => x.ResourceId == resourceId && x.GrantedUserId == grantedUserId);
804        if (entity != null) db.ResourcePermissions.DeleteOnSubmit(entity);
805        db.SubmitChanges();
806      }
807    }
808    #endregion
809
[6983]810    #region Authorization Methods
811    public Permission GetPermissionForTask(Guid taskId, Guid userId) {
812      using (var db = CreateContext()) {
813        return GetPermissionForJob(GetJobForTask(taskId), userId);
814      }
815    }
816
817    public Permission GetPermissionForJob(Guid jobId, Guid userId) {
818      using (var db = CreateContext()) {
819        Job job = db.Jobs.SingleOrDefault(x => x.JobId == jobId);
820        if (job == null) return Permission.NotAllowed;
821        if (job.OwnerUserId == userId) return Permission.Full;
822        JobPermission permission = db.JobPermissions.SingleOrDefault(p => p.JobId == jobId && p.GrantedUserId == userId);
823        return permission != null ? permission.Permission : Permission.NotAllowed;
824      }
825    }
826
827    public Guid GetJobForTask(Guid taskId) {
828      using (var db = CreateContext()) {
829        return db.Tasks.Single(j => j.TaskId == taskId).JobId;
830      }
831    }
832    #endregion
833
834    #region Lifecycle Methods
835    public DateTime GetLastCleanup() {
836      using (var db = CreateContext()) {
837        var entity = db.Lifecycles.SingleOrDefault();
838        return entity != null ? entity.LastCleanup : DateTime.MinValue;
839      }
840    }
841
842    public void SetLastCleanup(DateTime datetime) {
843      using (var db = CreateContext()) {
844        var entity = db.Lifecycles.SingleOrDefault();
845        if (entity != null) {
846          entity.LastCleanup = datetime;
847        } else {
848          entity = new Lifecycle();
849          entity.LifecycleId = 0; // always only one entry with ID:0
850          entity.LastCleanup = datetime;
851          db.Lifecycles.InsertOnSubmit(entity);
852        }
853        db.SubmitChanges();
854      }
855    }
856    #endregion
857
858    #region Downtime Methods
859    public DT.Downtime GetDowntime(Guid id) {
860      using (var db = CreateContext()) {
861        return DT.Convert.ToDto(db.Downtimes.SingleOrDefault(x => x.DowntimeId == id));
862      }
863    }
864
865    public IEnumerable<DT.Downtime> GetDowntimes(Expression<Func<Downtime, bool>> predicate) {
866      using (var db = CreateContext()) {
867        return db.Downtimes.Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
868      }
869    }
870
871    public Guid AddDowntime(DT.Downtime dto) {
872      using (var db = CreateContext()) {
873        var entity = DT.Convert.ToEntity(dto);
874        db.Downtimes.InsertOnSubmit(entity);
875        db.SubmitChanges();
876        return entity.DowntimeId;
877      }
878    }
879
880    public void UpdateDowntime(DT.Downtime dto) {
881      using (var db = CreateContext()) {
882        var entity = db.Downtimes.FirstOrDefault(x => x.DowntimeId == dto.Id);
883        if (entity == null) db.Downtimes.InsertOnSubmit(DT.Convert.ToEntity(dto));
884        else DT.Convert.ToEntity(dto, entity);
885        db.SubmitChanges();
886      }
887    }
888
889    public void DeleteDowntime(Guid id) {
890      using (var db = CreateContext()) {
891        var entity = db.Downtimes.FirstOrDefault(x => x.DowntimeId == id);
892        if (entity != null) db.Downtimes.DeleteOnSubmit(entity);
893        db.SubmitChanges();
894      }
895    }
896    #endregion
897
898    #region Statistics Methods
899    public DT.Statistics GetStatistic(Guid id) {
900      using (var db = CreateContext()) {
901        return DT.Convert.ToDto(db.Statistics.SingleOrDefault(x => x.StatisticsId == id));
902      }
903    }
904
905    public IEnumerable<DT.Statistics> GetStatistics(Expression<Func<Statistics, bool>> predicate) {
906      using (var db = CreateContext()) {
907        return db.Statistics.Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
908      }
909    }
910
911    public Guid AddStatistics(DT.Statistics dto) {
912      using (var db = CreateContext()) {
913        var entity = DT.Convert.ToEntity(dto);
914        db.Statistics.InsertOnSubmit(entity);
915        db.SubmitChanges();
916        foreach (var slaveStat in dto.SlaveStatistics) {
917          slaveStat.Id = entity.StatisticsId;
918          db.SlaveStatistics.InsertOnSubmit(DT.Convert.ToEntity(slaveStat));
919        }
920        if (dto.UserStatistics != null) {
921          foreach (var userStat in dto.UserStatistics) {
922            userStat.Id = entity.StatisticsId;
923            db.UserStatistics.InsertOnSubmit(DT.Convert.ToEntity(userStat));
924          }
925        }
926        db.SubmitChanges();
927        return entity.StatisticsId;
928      }
929    }
930
931    public void DeleteStatistics(Guid id) {
932      using (var db = CreateContext()) {
933        var entity = db.Statistics.FirstOrDefault(x => x.StatisticsId == id);
934        if (entity != null) db.Statistics.DeleteOnSubmit(entity);
935        db.SubmitChanges();
936      }
937    }
938
[9022]939    public Dictionary<Guid, int> GetWaitingTasksByUser() {
940      using (var db = CreateContext()) {
941        var waitingTasksByUser = from task in db.Tasks
942                                 where task.State == TaskState.Waiting
943                                 group task by task.Job.OwnerUserId into g
944                                 select new { UserId = g.Key, UsedCores = g.Count() };
945        return waitingTasksByUser.ToDictionary(x => x.UserId, x => x.UsedCores);
946      }
947    }
948
[9033]949    public Dictionary<Guid, int> GetWaitingTasksByUserForResources(List<Guid> resourceIds) {
950      using (var db = CreateContext()) {
951        var waitingTasksByUser = from task in db.Tasks
952                                 where task.State == TaskState.Waiting && task.AssignedResources.Any(x => resourceIds.Contains(x.ResourceId))
953                                 group task by task.Job.OwnerUserId into g
954                                 select new { UserId = g.Key, UsedCores = g.Count() };
955        return waitingTasksByUser.ToDictionary(x => x.UserId, x => x.UsedCores);
956      }
957    }
958
[9022]959    public Dictionary<Guid, int> GetCalculatingTasksByUser() {
960      using (var db = CreateContext()) {
961        var calculatingTasksByUser = from task in db.Tasks
[9025]962                                     where task.State == TaskState.Calculating
963                                     group task by task.Job.OwnerUserId into g
964                                     select new { UserId = g.Key, UsedCores = g.Count() };
[9022]965        return calculatingTasksByUser.ToDictionary(x => x.UserId, x => x.UsedCores);
966      }
967    }
968
[9033]969    public Dictionary<Guid, int> GetCalculatingTasksByUserForResources(List<Guid> resourceIds) {
970      using (var db = CreateContext()) {
971        var calculatingTasksByUser = from task in db.Tasks
972                                     where task.State == TaskState.Calculating && task.AssignedResources.Any(x => resourceIds.Contains(x.ResourceId))
973                                     group task by task.Job.OwnerUserId into g
974                                     select new { UserId = g.Key, UsedCores = g.Count() };
975        return calculatingTasksByUser.ToDictionary(x => x.UserId, x => x.UsedCores);
976      }
977    }
978
[6983]979    public List<DT.UserStatistics> GetUserStatistics() {
980      using (var db = CreateContext()) {
981        var userStats = new Dictionary<Guid, DT.UserStatistics>();
982
983        var usedCoresByUser = from job in db.Tasks
984                              where job.State == TaskState.Calculating
985                              group job by job.Job.OwnerUserId into g
986                              select new { UserId = g.Key, UsedCores = g.Count() };
987
988        foreach (var item in usedCoresByUser) {
989          if (!userStats.ContainsKey(item.UserId)) {
990            userStats.Add(item.UserId, new DT.UserStatistics() { UserId = item.UserId });
991          }
992          userStats[item.UserId].UsedCores += item.UsedCores;
993        }
994
995        var executionTimesByUser = from task in db.Tasks
996                                   group task by task.Job.OwnerUserId into g
997                                   select new { UserId = g.Key, ExecutionTime = TimeSpan.FromMilliseconds(g.Select(x => x.ExecutionTimeMs).Sum()) };
998        foreach (var item in executionTimesByUser) {
999          if (!userStats.ContainsKey(item.UserId)) {
1000            userStats.Add(item.UserId, new DT.UserStatistics() { UserId = item.UserId });
1001          }
1002          userStats[item.UserId].ExecutionTime += item.ExecutionTime;
1003        }
1004
1005        // execution times only of finished task - necessary to compute efficieny
1006        var executionTimesFinishedJobs = from job in db.Tasks
1007                                         where job.State == TaskState.Finished
1008                                         group job by job.Job.OwnerUserId into g
1009                                         select new { UserId = g.Key, ExecutionTimeFinishedJobs = TimeSpan.FromMilliseconds(g.Select(x => x.ExecutionTimeMs).Sum()) };
1010
1011        foreach (var item in executionTimesFinishedJobs) {
1012          if (!userStats.ContainsKey(item.UserId)) {
1013            userStats.Add(item.UserId, new DT.UserStatistics() { UserId = item.UserId });
1014          }
1015          userStats[item.UserId].ExecutionTimeFinishedJobs += item.ExecutionTimeFinishedJobs;
1016        }
1017
1018        // start to end times only of finished task - necessary to compute efficiency
1019        var startToEndTimesFinishedJobs = from job in db.Tasks
1020                                          where job.State == TaskState.Finished
1021                                          group job by job.Job.OwnerUserId into g
1022                                          select new {
1023                                            UserId = g.Key,
1024                                            StartToEndTime = new TimeSpan(g.Select(x => x.StateLogs.OrderByDescending(sl => sl.DateTime).First().DateTime - x.StateLogs.OrderBy(sl => sl.DateTime).First().DateTime).Sum(ts => ts.Ticks))
1025                                          };
1026        foreach (var item in startToEndTimesFinishedJobs) {
1027          if (!userStats.ContainsKey(item.UserId)) {
1028            userStats.Add(item.UserId, new DT.UserStatistics() { UserId = item.UserId });
1029          }
1030          userStats[item.UserId].StartToEndTime += item.StartToEndTime;
1031        }
1032
1033        // also consider executiontimes of DeletedJobStats
1034        var deletedJobsExecutionTimesByUsers = from del in db.DeletedJobStatistics
1035                                               group del by del.UserId into g
1036                                               select new {
1037                                                 UserId = g.Key,
1038                                                 ExecutionTime = TimeSpan.FromSeconds(g.Select(x => x.ExecutionTimeS).Sum()),
1039                                                 ExecutionTimeFinishedJobs = TimeSpan.FromSeconds(g.Select(x => x.ExecutionTimeSFinishedJobs).Sum()),
1040                                                 StartToEndTime = TimeSpan.FromSeconds(g.Select(x => x.StartToEndTimeS).Sum())
1041                                               };
1042        foreach (var item in deletedJobsExecutionTimesByUsers) {
1043          if (!userStats.ContainsKey(item.UserId)) {
1044            userStats.Add(item.UserId, new DT.UserStatistics() { UserId = item.UserId });
1045          }
1046          userStats[item.UserId].ExecutionTime += item.ExecutionTime;
1047          userStats[item.UserId].ExecutionTimeFinishedJobs += item.ExecutionTimeFinishedJobs;
1048          userStats[item.UserId].StartToEndTime += item.StartToEndTime;
1049        }
1050
1051        return userStats.Values.ToList();
1052      }
1053    }
1054    #endregion
1055
[9123]1056    #region UserPriority Methods
1057    public IEnumerable<DT.UserPriority> GetUserPriorities(Expression<Func<UserPriority, bool>> predicate) {
1058      using (var db = CreateContext()) {
1059        return db.UserPriorities.Where(predicate).Select(x => DT.Convert.ToDto(x)).ToArray();
1060      }
1061    }
1062
1063    public void EnqueueUserPriority(DT.UserPriority dto) {
1064      using (var db = CreateContext()) {
1065        var entity = db.UserPriorities.FirstOrDefault(x => x.UserId == dto.Id);
1066        if (entity == null) db.UserPriorities.InsertOnSubmit(DT.Convert.ToEntity(dto));
1067        else DT.Convert.ToEntity(dto, entity);
1068        db.SubmitChanges();
1069      }
1070    }
1071    #endregion
1072
[6983]1073    #region Helpers
1074    private void CollectChildTasks(HiveDataContext db, Guid parentTaskId, List<Task> collection) {
1075      var tasks = db.Tasks.Where(j => j.ParentTaskId == parentTaskId);
1076      foreach (var task in tasks) {
1077        collection.Add(task);
1078        if (task.IsParentTask)
1079          CollectChildTasks(db, task.TaskId, collection);
1080      }
1081    }
1082    #endregion
1083  }
1084}
Note: See TracBrowser for help on using the repository browser.