Index: trunk/sources/HeuristicLab.Services.Hive/3.3/Manager/AuthorizationManager.cs
===================================================================
--- trunk/sources/HeuristicLab.Services.Hive/3.3/Manager/AuthorizationManager.cs	(revision 12172)
+++ trunk/sources/HeuristicLab.Services.Hive/3.3/Manager/AuthorizationManager.cs	(revision 12878)
@@ -22,5 +22,8 @@
 using System;
 using System.Security;
+using HeuristicLab.Services.Access;
 using HeuristicLab.Services.Hive.DataAccess;
+using HeuristicLab.Services.Hive.DataAccess.Interfaces;
+using DA = HeuristicLab.Services.Hive.DataAccess;
 using DT = HeuristicLab.Services.Hive.DataTransfer;
 
@@ -28,27 +31,72 @@
 namespace HeuristicLab.Services.Hive {
   public class AuthorizationManager : IAuthorizationManager {
+
+    private const string NOT_AUTHORIZED = "Current user is not authorized to access the requested resource";
+    private IPersistenceManager PersistenceManager {
+      get { return ServiceLocator.Instance.PersistenceManager; }
+    }
+
+    private IUserManager UserManager {
+      get { return ServiceLocator.Instance.UserManager; }
+    }
+
+    private IRoleVerifier RoleVerifier {
+      get { return ServiceLocator.Instance.RoleVerifier; }
+    }
+
     public void Authorize(Guid userId) {
       if (userId != ServiceLocator.Instance.UserManager.CurrentUserId)
-        throw new SecurityException("Current user is not authorized to access object");
+        throw new SecurityException(NOT_AUTHORIZED);
     }
 
     public void AuthorizeForTask(Guid taskId, DT.Permission requiredPermission) {
       if (ServiceLocator.Instance.RoleVerifier.IsInRole(HiveRoles.Slave)) return; // slave-users can access all tasks
-
-      Permission permission = ServiceLocator.Instance.HiveDao.GetPermissionForTask(taskId, ServiceLocator.Instance.UserManager.CurrentUserId);
-      if (permission == Permission.NotAllowed || (permission != DT.Convert.ToEntity(requiredPermission) && DT.Convert.ToEntity(requiredPermission) == Permission.Full))
-        throw new SecurityException("Current user is not authorized to access task");
+      var pm = PersistenceManager;
+      var taskDao = pm.TaskDao;
+      pm.UseTransaction(() => {
+        var task = taskDao.GetById(taskId);
+        if (task == null) throw new SecurityException(NOT_AUTHORIZED);
+        AuthorizeJob(pm, task.JobId, requiredPermission);
+      });
     }
 
     public void AuthorizeForJob(Guid jobId, DT.Permission requiredPermission) {
-      Permission permission = ServiceLocator.Instance.HiveDao.GetPermissionForJob(jobId, ServiceLocator.Instance.UserManager.CurrentUserId);
-      if (permission == Permission.NotAllowed || (permission != DT.Convert.ToEntity(requiredPermission) && DT.Convert.ToEntity(requiredPermission) == Permission.Full))
-        throw new SecurityException("Current user is not authorized to access task");
+      var pm = PersistenceManager;
+      pm.UseTransaction(() => {
+        AuthorizeJob(pm, jobId, requiredPermission);
+      });
     }
 
     public void AuthorizeForResourceAdministration(Guid resourceId) {
-      Resource resource = DT.Convert.ToEntity(ServiceLocator.Instance.HiveDao.GetResource(resourceId));
-      if (resource.OwnerUserId != ServiceLocator.Instance.UserManager.CurrentUserId && !ServiceLocator.Instance.RoleVerifier.IsInRole(HiveRoles.Administrator))
-        throw new SecurityException("Current user is not authorized to access resource");
+      var pm = PersistenceManager;
+      var resourceDao = pm.ResourceDao;
+      pm.UseTransaction(() => {
+        var resource = resourceDao.GetById(resourceId);
+        if (resource == null) throw new SecurityException(NOT_AUTHORIZED);
+        if (resource.OwnerUserId != UserManager.CurrentUserId
+            && !RoleVerifier.IsInRole(HiveRoles.Administrator)) {
+          throw new SecurityException(NOT_AUTHORIZED);
+        }
+      });
+    }
+
+    private DA.Permission GetPermissionForJob(IPersistenceManager pm, Guid jobId, Guid userId) {
+      var jobDao = pm.JobDao;
+      var jobPermissionDao = pm.JobPermissionDao;
+      var job = jobDao.GetById(jobId);
+      if (job == null) return DA.Permission.NotAllowed;
+      if (job.OwnerUserId == userId) return DA.Permission.Full;
+      var jobPermission = jobPermissionDao.GetByJobAndUserId(jobId, userId);
+      if (jobPermission == null) return DA.Permission.NotAllowed;
+      return jobPermission.Permission;
+    }
+
+    private void AuthorizeJob(IPersistenceManager pm, Guid jobId, DT.Permission requiredPermission) {
+      var requiredPermissionEntity = requiredPermission.ToEntity();
+      DA.Permission permission = GetPermissionForJob(pm, jobId, UserManager.CurrentUserId);
+      if (permission == Permission.NotAllowed
+          || ((permission != requiredPermissionEntity) && requiredPermissionEntity == Permission.Full)) {
+        throw new SecurityException(NOT_AUTHORIZED);
+      }
     }
   }
Index: trunk/sources/HeuristicLab.Services.Hive/3.3/Manager/EventManager.cs
===================================================================
--- trunk/sources/HeuristicLab.Services.Hive/3.3/Manager/EventManager.cs	(revision 12172)
+++ trunk/sources/HeuristicLab.Services.Hive/3.3/Manager/EventManager.cs	(revision 12878)
@@ -21,63 +21,28 @@
 
 using System;
-using System.Collections.Generic;
 using System.Linq;
 using HeuristicLab.Services.Hive.DataAccess;
-using DT = HeuristicLab.Services.Hive.DataTransfer;
+using HeuristicLab.Services.Hive.DataAccess.Interfaces;
 
-
-namespace HeuristicLab.Services.Hive {
-  /// <summary>
-  /// This class offers methods for cleaning up offline slaves and task
-  /// </summary>
+namespace HeuristicLab.Services.Hive.Manager {
   public class EventManager : IEventManager {
-    private IHiveDao dao {
-      get { return ServiceLocator.Instance.HiveDao; }
-    }
-    private IAuthorizationManager auth {
-      get { return ServiceLocator.Instance.AuthorizationManager; }
-    }
-    private ILogger log {
-      get { return LogFactory.GetLogger(this.GetType().Namespace); }
-    }
-    private DataAccess.ITransactionManager trans {
-      get { return ServiceLocator.Instance.TransactionManager; }
+    private const string SlaveTimeout = "Slave timed out.";
+    private IPersistenceManager PersistenceManager {
+      get { return ServiceLocator.Instance.PersistenceManager; }
     }
 
     public void Cleanup() {
-      trans.UseTransaction(() => {
-        SetTimeoutSlavesOffline();
-        SetTimeoutTasksWaiting();
-        DeleteObsoleteSlaves();
+      var pm = PersistenceManager;
+      pm.UseTransaction(() => {
+        SetTimeoutSlavesOffline(pm);
+        SetTimeoutTasksWaiting(pm);
+        DeleteObsoleteSlaves(pm);
+        pm.SubmitChanges();
       });
 
-      trans.UseTransaction(() => {
-        FinishParentTasks();
-        UpdateStatistics();
+      pm.UseTransaction(() => {
+        FinishParentTasks(pm);
+        pm.SubmitChanges();
       });
-    }
-
-    private void UpdateStatistics() {
-      var slaves = dao.GetSlaves(x => x.SlaveState == SlaveState.Calculating || x.SlaveState == SlaveState.Idle);
-
-      var stats = new DataTransfer.Statistics();
-      stats.TimeStamp = DateTime.Now;
-      var slaveStats = new List<DT.SlaveStatistics>();
-      foreach (var slave in slaves) {
-        slaveStats.Add(new DT.SlaveStatistics() {
-          SlaveId = slave.Id,
-          Cores = slave.Cores.HasValue ? slave.Cores.Value : 0,
-          FreeCores = slave.FreeCores.HasValue ? slave.FreeCores.Value : 0,
-          Memory = slave.Memory.HasValue ? slave.Memory.Value : 0,
-          FreeMemory = slave.FreeMemory.HasValue ? slave.FreeMemory.Value : 0,
-          CpuUtilization = slave.CpuUtilization
-        });
-      }
-      stats.SlaveStatistics = slaveStats;
-      //collecting user statistics slows down the db and results in timeouts. 
-      //we have to find another way to deal with this.  
-      //until then the next line is commented out...
-      //stats.UserStatistics = dtoDao.GetUserStatistics();
-      dao.AddStatistics(stats);
     }
 
@@ -85,10 +50,12 @@
     /// Searches for slaves which are timed out, puts them and their task offline
     /// </summary>
-    private void SetTimeoutSlavesOffline() {
-      var slaves = dao.GetSlaves(x => x.SlaveState != SlaveState.Offline);
-      foreach (DT.Slave slave in slaves) {
-        if (!slave.LastHeartbeat.HasValue || (DateTime.Now - slave.LastHeartbeat.Value) > HeuristicLab.Services.Hive.Properties.Settings.Default.SlaveHeartbeatTimeout) {
-          slave.SlaveState = DT.SlaveState.Offline;
-          dao.UpdateSlave(slave);
+    private void SetTimeoutSlavesOffline(IPersistenceManager pm) {
+      var slaveDao = pm.SlaveDao;
+      var slaves = slaveDao.GetOnlineSlaves();
+      foreach (var slave in slaves) {
+        if (!slave.LastHeartbeat.HasValue ||
+            (DateTime.Now - slave.LastHeartbeat.Value) >
+            Properties.Settings.Default.SlaveHeartbeatTimeout) {
+          slave.SlaveState = SlaveState.Offline;
         }
       }
@@ -98,8 +65,18 @@
     /// Looks for parent tasks which have FinishWhenChildJobsFinished and set their state to finished
     /// </summary>
-    private void FinishParentTasks() {
-      var parentTasksToFinish = dao.GetParentTasks(dao.GetResources(x => true).Select(x => x.Id), 0, true);
+    private void FinishParentTasks(IPersistenceManager pm) {
+      var resourceDao = pm.ResourceDao;
+      var taskDao = pm.TaskDao;
+      var resourceIds = resourceDao.GetAll().Select(x => x.ResourceId).ToList();
+      var parentTasksToFinish = taskDao.GetParentTasks(resourceIds, 0, true);
       foreach (var task in parentTasksToFinish) {
-        dao.UpdateTaskState(task.Id, TaskState.Finished, null, null, string.Empty);
+        task.State = TaskState.Finished;
+        task.StateLogs.Add(new StateLog {
+          State = task.State,
+          SlaveId = null,
+          UserId = null,
+          Exception = string.Empty,
+          DateTime = DateTime.Now
+        });
       }
     }
@@ -108,11 +85,18 @@
     /// Looks for task which have not sent heartbeats for some time and reschedules them for calculation
     /// </summary>
-    private void SetTimeoutTasksWaiting() {
-      var tasks = dao.GetTasks(x => (x.State == TaskState.Calculating && (DateTime.Now - x.LastHeartbeat) > HeuristicLab.Services.Hive.Properties.Settings.Default.CalculatingJobHeartbeatTimeout)
-                               || (x.State == TaskState.Transferring && (DateTime.Now - x.LastHeartbeat) > HeuristicLab.Services.Hive.Properties.Settings.Default.TransferringJobHeartbeatTimeout));
-      foreach (var j in tasks) {
-        DT.Task task = dao.UpdateTaskState(j.Id, TaskState.Waiting, null, null, "Slave timed out.");
+    private void SetTimeoutTasksWaiting(IPersistenceManager pm) {
+      var taskDao = pm.TaskDao;
+      var tasks = taskDao.GetAll().Where(x => (x.State == TaskState.Calculating && (DateTime.Now - x.LastHeartbeat) > Properties.Settings.Default.CalculatingJobHeartbeatTimeout)
+                                           || (x.State == TaskState.Transferring && (DateTime.Now - x.LastHeartbeat) > Properties.Settings.Default.TransferringJobHeartbeatTimeout));
+      foreach (var task in tasks) {
+        task.State = TaskState.Waiting;
+        task.StateLogs.Add(new StateLog {
+          State = task.State,
+          SlaveId = null,
+          UserId = null,
+          Exception = SlaveTimeout,
+          DateTime = DateTime.Now
+        });
         task.Command = null;
-        dao.UpdateTask(task);
       }
     }
@@ -121,15 +105,17 @@
     /// Searches for slaves that are disposable and deletes them if they were offline for too long
     /// </summary>
-    private void DeleteObsoleteSlaves() {
-      var slaves = dao.GetSlaves(x => x.IsDisposable.GetValueOrDefault() &&
-                                      x.SlaveState == SlaveState.Offline &&
-                                      (DateTime.Now - x.LastHeartbeat) > Hive.Properties.Settings.Default.SweepInterval)
-                                .Select(x => x.Id)
-                                .ToArray();
-
-      foreach (Guid slaveId in slaves) {
-        var downtimesAvailable = dao.GetDowntimes(x => x.ResourceId == slaveId).Any();
+    private void DeleteObsoleteSlaves(IPersistenceManager pm) {
+      var slaveDao = pm.SlaveDao;
+      var downtimeDao = pm.DowntimeDao;
+      var slaveIds = slaveDao.GetAll()
+        .Where(x => x.IsDisposable.GetValueOrDefault()
+                 && x.SlaveState == SlaveState.Offline
+                 && (DateTime.Now - x.LastHeartbeat) > Properties.Settings.Default.SweepInterval)
+        .Select(x => x.ResourceId)
+        .ToList();
+      foreach (var id in slaveIds) {
+        bool downtimesAvailable = downtimeDao.GetByResourceId(id).Any();
         if (!downtimesAvailable) {
-          dao.DeleteSlave(slaveId);
+          slaveDao.Delete(id);
         }
       }
Index: trunk/sources/HeuristicLab.Services.Hive/3.3/Manager/HeartbeatManager.cs
===================================================================
--- trunk/sources/HeuristicLab.Services.Hive/3.3/Manager/HeartbeatManager.cs	(revision 12172)
+++ trunk/sources/HeuristicLab.Services.Hive/3.3/Manager/HeartbeatManager.cs	(revision 12878)
@@ -25,18 +25,18 @@
 using System.Threading;
 using HeuristicLab.Services.Hive.DataAccess;
-using Heartbeat = HeuristicLab.Services.Hive.DataTransfer.Heartbeat;
-
-namespace HeuristicLab.Services.Hive {
+using HeuristicLab.Services.Hive.DataAccess.Interfaces;
+using HeuristicLab.Services.Hive.DataTransfer;
+using DA = HeuristicLab.Services.Hive.DataAccess;
+
+namespace HeuristicLab.Services.Hive.Manager {
   public class HeartbeatManager {
     private const string MutexName = "HiveTaskSchedulingMutex";
 
-    private IOptimizedHiveDao dao {
-      get { return ServiceLocator.Instance.OptimizedHiveDao; }
-    }
-    private ITaskScheduler taskScheduler {
+    private IPersistenceManager PersistenceManager {
+      get { return ServiceLocator.Instance.PersistenceManager; }
+    }
+
+    private ITaskScheduler TaskScheduler {
       get { return ServiceLocator.Instance.TaskScheduler; }
-    }
-    private DataAccess.ITransactionManager trans {
-      get { return ServiceLocator.Instance.TransactionManager; }
     }
 
@@ -47,9 +47,8 @@
     public List<MessageContainer> ProcessHeartbeat(Heartbeat heartbeat) {
       List<MessageContainer> actions = new List<MessageContainer>();
-
-      Slave slave = null;
-      trans.UseTransaction(() => {
-        slave = dao.GetSlaveById(heartbeat.SlaveId);
-      });
+      var pm = PersistenceManager;
+      var slaveDao = pm.SlaveDao;
+      var taskDao = pm.TaskDao;
+      var slave = pm.UseTransaction(() => slaveDao.GetById(heartbeat.SlaveId));
       if (slave == null) {
         actions.Add(new MessageContainer(MessageContainer.MessageType.SayHello));
@@ -58,22 +57,22 @@
           actions.Add(new MessageContainer(MessageContainer.MessageType.NewHBInterval));
         }
-        if (dao.SlaveHasToShutdownComputer(slave.ResourceId)) {
+        if (slaveDao.SlaveHasToShutdownComputer(slave.ResourceId)) {
           actions.Add(new MessageContainer(MessageContainer.MessageType.ShutdownComputer));
         }
-
-        // update slave data
+        // update slave data  
         slave.FreeCores = heartbeat.FreeCores;
         slave.FreeMemory = heartbeat.FreeMemory;
         slave.CpuUtilization = heartbeat.CpuUtilization;
-        slave.IsAllowedToCalculate = dao.SlaveIsAllowedToCalculate(slave.ResourceId);
-        slave.SlaveState = (heartbeat.JobProgress != null && heartbeat.JobProgress.Count > 0) ? SlaveState.Calculating : SlaveState.Idle;
+        slave.SlaveState = (heartbeat.JobProgress != null && heartbeat.JobProgress.Count > 0)
+          ? DA.SlaveState.Calculating
+          : DA.SlaveState.Idle;
         slave.LastHeartbeat = DateTime.Now;
-
-        trans.UseTransaction(() => {
-          dao.UpdateSlave(slave);
+        pm.UseTransaction(() => {
+          slave.IsAllowedToCalculate = slaveDao.SlaveIsAllowedToCalculate(slave.ResourceId);
+          pm.SubmitChanges();
         });
 
         // update task data
-        actions.AddRange(UpdateTasks(heartbeat, slave.IsAllowedToCalculate));
+        actions.AddRange(UpdateTasks(pm, heartbeat, slave.IsAllowedToCalculate));
 
         // assign new task
@@ -82,18 +81,22 @@
           var mutex = new Mutex(false, MutexName);
           try {
-
             mutexAquired = mutex.WaitOne(Properties.Settings.Default.SchedulingPatience);
-            if (!mutexAquired)
+            if (mutexAquired) {
+              var waitingTasks = pm.UseTransaction(() => taskDao.GetWaitingTasks(slave)
+                  .Select(x => new TaskInfoForScheduler {
+                    TaskId = x.TaskId,
+                    JobId = x.JobId,
+                    Priority = x.Priority
+                  })
+                  .ToList()
+              );
+              var availableTasks = TaskScheduler.Schedule(waitingTasks).ToArray();
+              if (availableTasks.Any()) {
+                var task = availableTasks.First();
+                AssignTask(pm, slave, task.TaskId);
+                actions.Add(new MessageContainer(MessageContainer.MessageType.CalculateTask, task.TaskId));
+              }
+            } else {
               LogFactory.GetLogger(this.GetType().Namespace).Log("HeartbeatManager: The mutex used for scheduling could not be aquired.");
-            else {
-              trans.UseTransaction(() => {
-                IEnumerable<TaskInfoForScheduler> availableTasks = null;
-                availableTasks = taskScheduler.Schedule(dao.GetWaitingTasks(slave).ToArray());
-                if (availableTasks.Any()) {
-                  var task = availableTasks.First();
-                  AssignTask(slave, task.TaskId);
-                  actions.Add(new MessageContainer(MessageContainer.MessageType.CalculateTask, task.TaskId));
-                }
-              });
             }
           }
@@ -102,5 +105,5 @@
           }
           catch (Exception ex) {
-            LogFactory.GetLogger(this.GetType().Namespace).Log("HeartbeatManager threw an exception in ProcessHeartbeat: " + ex.ToString());
+            LogFactory.GetLogger(this.GetType().Namespace).Log(string.Format("HeartbeatManager threw an exception in ProcessHeartbeat: {0}", ex));
           }
           finally {
@@ -112,10 +115,23 @@
     }
 
-    private void AssignTask(Slave slave, Guid taskId) {
-      var task = dao.UpdateTaskState(taskId, TaskState.Transferring, slave.ResourceId, null, null);
-
-      // from now on the task has some time to send the next heartbeat (ApplicationConstants.TransferringJobHeartbeatTimeout)
-      task.LastHeartbeat = DateTime.Now;
-      dao.UpdateTask(task);
+    private void AssignTask(IPersistenceManager pm, DA.Slave slave, Guid taskId) {
+      const DA.TaskState transferring = DA.TaskState.Transferring;
+      DateTime now = DateTime.Now;
+      var taskDao = pm.TaskDao;
+      var stateLogDao = pm.StateLogDao;
+      pm.UseTransaction(() => {
+        var task = taskDao.GetById(taskId);
+        stateLogDao.Save(new DA.StateLog {
+          State = transferring,
+          DateTime = now,
+          TaskId = taskId,
+          SlaveId = slave.ResourceId,
+          UserId = null,
+          Exception = null
+        });
+        task.State = transferring;
+        task.LastHeartbeat = now;
+        pm.SubmitChanges();
+      });
     }
 
@@ -124,52 +140,59 @@
     /// Checks if all the task sent by heartbeat are supposed to be calculated by this slave
     /// </summary>
-    private IEnumerable<MessageContainer> UpdateTasks(Heartbeat heartbeat, bool IsAllowedToCalculate) {
-      List<MessageContainer> actions = new List<MessageContainer>();
-
-      if (heartbeat.JobProgress == null)
+    private IEnumerable<MessageContainer> UpdateTasks(IPersistenceManager pm, Heartbeat heartbeat, bool isAllowedToCalculate) {
+      var taskDao = pm.TaskDao;
+      var assignedResourceDao = pm.AssignedResourceDao;
+      var actions = new List<MessageContainer>();
+      if (heartbeat.JobProgress == null || !heartbeat.JobProgress.Any())
         return actions;
 
-      if (!IsAllowedToCalculate && heartbeat.JobProgress.Count != 0) {
+      if (!isAllowedToCalculate && heartbeat.JobProgress.Count != 0) {
         actions.Add(new MessageContainer(MessageContainer.MessageType.PauseAll));
       } else {
+        // select all tasks and statelogs with one query
+        var taskIds = heartbeat.JobProgress.Select(x => x.Key).ToList();
+        var taskInfos = pm.UseTransaction(() =>
+          (from task in taskDao.GetAll()
+           where taskIds.Contains(task.TaskId)
+           let lastStateLog = task.StateLogs.OrderByDescending(x => x.DateTime).FirstOrDefault()
+           select new {
+             TaskId = task.TaskId,
+             Command = task.Command,
+             SlaveId = lastStateLog != null ? lastStateLog.SlaveId : default(Guid)
+           }).ToList()
+        );
+
         // process the jobProgresses
         foreach (var jobProgress in heartbeat.JobProgress) {
-          Tuple<Task, Guid?> taskWithLastStateLogSlaveId = null;
-          trans.UseTransaction(() => {
-            taskWithLastStateLogSlaveId = dao.GetTaskByIdAndLastStateLogSlaveId(jobProgress.Key);
-          });
-          var curTask = taskWithLastStateLogSlaveId != null ? taskWithLastStateLogSlaveId.Item1 : null;
+          var progress = jobProgress;
+          var curTask = taskInfos.SingleOrDefault(x => x.TaskId == progress.Key);
           if (curTask == null) {
-            // task does not exist in db
-            actions.Add(new MessageContainer(MessageContainer.MessageType.AbortTask, jobProgress.Key));
+            actions.Add(new MessageContainer(MessageContainer.MessageType.AbortTask, progress.Key));
             LogFactory.GetLogger(this.GetType().Namespace).Log("Task on slave " + heartbeat.SlaveId + " does not exist in DB: " + jobProgress.Key);
           } else {
-            var slaveId = taskWithLastStateLogSlaveId.Item2;
+            var slaveId = curTask.SlaveId;
             if (slaveId == Guid.Empty || slaveId != heartbeat.SlaveId) {
               // assigned slave does not match heartbeat
               actions.Add(new MessageContainer(MessageContainer.MessageType.AbortTask, curTask.TaskId));
-              LogFactory.GetLogger(this.GetType().Namespace).Log("The slave " + heartbeat.SlaveId + " is not supposed to calculate task: " + curTask);
-            } else if (!dao.TaskIsAllowedToBeCalculatedBySlave(curTask.TaskId, heartbeat.SlaveId)) {
+              LogFactory.GetLogger(this.GetType().Namespace).Log("The slave " + heartbeat.SlaveId + " is not supposed to calculate task: " + curTask.TaskId);
+            } else if (!assignedResourceDao.TaskIsAllowedToBeCalculatedBySlave(curTask.TaskId, heartbeat.SlaveId)) {
               // assigned resources ids of task do not match with slaveId (and parent resourceGroupIds); this might happen when slave is moved to different group
               actions.Add(new MessageContainer(MessageContainer.MessageType.PauseTask, curTask.TaskId));
             } else {
-              // save task execution time
-              curTask.ExecutionTimeMs = jobProgress.Value.TotalMilliseconds;
-              curTask.LastHeartbeat = DateTime.Now;
-
+              // update task execution time
+              pm.UseTransaction(() => {
+                taskDao.UpdateExecutionTime(curTask.TaskId, progress.Value.TotalMilliseconds);
+              });
               switch (curTask.Command) {
-                case Command.Stop:
+                case DA.Command.Stop:
                   actions.Add(new MessageContainer(MessageContainer.MessageType.StopTask, curTask.TaskId));
                   break;
-                case Command.Pause:
+                case DA.Command.Pause:
                   actions.Add(new MessageContainer(MessageContainer.MessageType.PauseTask, curTask.TaskId));
                   break;
-                case Command.Abort:
+                case DA.Command.Abort:
                   actions.Add(new MessageContainer(MessageContainer.MessageType.AbortTask, curTask.TaskId));
                   break;
               }
-              trans.UseTransaction(() => {
-                dao.UpdateTask(curTask);
-              });
             }
           }
