这篇主要配合数据使用
先说数据库
名字都很标准----------------------------------
------------------------------------------------------------------
贴代码
1.IScheduleTaskService-----------Task表的接口
/// <summary> /// Task service interface /// </summary> public partial interface IScheduleTaskService { /// <summary> /// Deletes a task /// </summary> /// <param name="task">Task</param> void DeleteTask(ScheduleTask task); /// <summary> /// Gets a task /// </summary> /// <param name="taskId">Task identifier</param> /// <returns>Task</returns> ScheduleTask GetTaskById(int taskId); /// <summary> /// Gets a task by its type /// </summary> /// <param name="type">Task type</param> /// <returns>Task</returns> ScheduleTask GetTaskByType(string type); /// <summary> /// Gets all tasks /// </summary> /// <param name="showHidden">A value indicating whether to show hidden records</param> /// <returns>Tasks</returns> IList<ScheduleTask> GetAllTasks(bool showHidden = false); /// <summary> /// Inserts a task /// </summary> /// <param name="task">Task</param> void InsertTask(ScheduleTask task); /// <summary> /// Updates the task /// </summary> /// <param name="task">Task</param> void UpdateTask(ScheduleTask task); }
2.ScheduleTaskService-------------Task表的实现
/// <summary> /// Task service /// </summary> public partial class ScheduleTaskService : IScheduleTaskService { #region Fields private readonly IRepository<ScheduleTask> _taskRepository; #endregion #region Ctor public ScheduleTaskService(IRepository<ScheduleTask> taskRepository) { this._taskRepository = taskRepository; } #endregion #region Methods /// <summary> /// Deletes a task /// </summary> /// <param name="task">Task</param> public virtual void DeleteTask(ScheduleTask task) { if (task == null) throw new ArgumentNullException("task"); _taskRepository.Delete(task); } /// <summary> /// Gets a task /// </summary> /// <param name="taskId">Task identifier</param> /// <returns>Task</returns> public virtual ScheduleTask GetTaskById(int taskId) { if (taskId == 0) return null; return _taskRepository.GetById(taskId); } /// <summary> /// Gets a task by its type /// </summary> /// <param name="type">Task type</param> /// <returns>Task</returns> public virtual ScheduleTask GetTaskByType(string type) { if (String.IsNullOrWhiteSpace(type)) return null; var query = _taskRepository.Table; query = query.Where(st => st.Type == type); query = query.OrderByDescending(t => t.Id); var task = query.FirstOrDefault(); return task; } /// <summary> /// Gets all tasks /// </summary> /// <param name="showHidden">A value indicating whether to show hidden records</param> /// <returns>Tasks</returns> public virtual IList<ScheduleTask> GetAllTasks(bool showHidden = false) { var query = _taskRepository.Table; if (!showHidden) { query = query.Where(t => t.Enabled); } query = query.OrderByDescending(t => t.Seconds); var tasks = query.ToList(); return tasks; } /// <summary> /// Inserts a task /// </summary> /// <param name="task">Task</param> public virtual void InsertTask(ScheduleTask task) { if (task == null) throw new ArgumentNullException("task"); _taskRepository.Insert(task); } /// <summary> /// Updates the task /// </summary> /// <param name="task">Task</param> public virtual void UpdateTask(ScheduleTask task) { if (task == null) throw new ArgumentNullException("task"); _taskRepository.Update(task); } #endregion }
IRepository--------后续写数据库相关的时候再连着EF一起说------------------------
3.ScheduleTask---------------------实体类
/// <summary> /// Schedule task /// </summary> public partial class ScheduleTask : BaseEntity { /// <summary> /// Gets or sets the name /// </summary> public string Name { get; set; } /// <summary> /// Gets or sets the run period (in seconds) /// </summary> public int Seconds { get; set; } /// <summary> /// Gets or sets the type of appropriate ITask class /// </summary> public string Type { get; set; } /// <summary> /// Gets or sets the value indicating whether a task is enabled /// </summary> public bool Enabled { get; set; } /// <summary> /// Gets or sets the value indicating whether a task should be stopped on some error /// </summary> public bool StopOnError { get; set; } /// <summary> /// Gets or sets the machine name (instance) that leased this task. It's used when running in web farm (ensure that a task in run only on one machine). It could be null when not running in web farm. /// </summary> public string LeasedByMachineName { get; set; } /// <summary> /// Gets or sets the datetime until the task is leased by some machine (instance). It's used when running in web farm (ensure that a task in run only on one machine). /// </summary> public DateTime? LeasedUntilUtc { get; set; } /// <summary> /// Gets or sets the datetime when it was started last time /// </summary> public DateTime? LastStartUtc { get; set; } /// <summary> /// Gets or sets the datetime when it was finished last time (no matter failed ir success) /// </summary> public DateTime? LastEndUtc { get; set; } /// <summary> /// Gets or sets the datetime when it was sucessfully finished last time /// </summary> public DateTime? LastSuccessUtc { get; set; } }
4.BaseEntity------------------------实体类,基类,写的很有意思
/// <summary> /// Base class for entities /// </summary> public abstract partial class BaseEntity { /// <summary> /// Gets or sets the entity identifier /// </summary> public int Id { get; set; } public override bool Equals(object obj) { return Equals(obj as BaseEntity); } private static bool IsTransient(BaseEntity obj) { return obj != null && Equals(obj.Id, default(int)); } private Type GetUnproxiedType() { return GetType(); } public virtual bool Equals(BaseEntity other) { if (other == null) return false; if (ReferenceEquals(this, other)) return true; if (!IsTransient(this) && !IsTransient(other) && Equals(Id, other.Id)) { var otherType = other.GetUnproxiedType(); var thisType = GetUnproxiedType(); return thisType.IsAssignableFrom(otherType) || otherType.IsAssignableFrom(thisType); } return false; } public override int GetHashCode() { if (Equals(Id, default(int))) return base.GetHashCode(); return Id.GetHashCode(); } public static bool operator ==(BaseEntity x, BaseEntity y) { return Equals(x, y); } public static bool operator !=(BaseEntity x, BaseEntity y) { return !(x == y); } }
5.Mapping映射
public partial class ScheduleTaskMap : EntityTypeConfiguration<ScheduleTask> { public ScheduleTaskMap() { this.ToTable("ScheduleTask"); this.HasKey(t => t.Id); this.Property(t => t.Name).IsRequired(); this.Property(t => t.Type).IsRequired(); } }
-----------------至此操作上面scheduletask表的方法实体类都写完了-----------------------------------
6.ITask-----自动任务执行继承这个接口就可以了
/// <summary> /// Interface that should be implemented by each task /// </summary> public partial interface ITask { /// <summary> /// Executes a task /// </summary> void Execute(); }
7.Task
/// <summary> /// Task /// </summary> public partial class Task { #region Ctor /// <summary> /// Ctor for Task /// </summary> private Task() { this.Enabled = true; } /// <summary> /// Ctor for Task /// </summary> /// <param name="task">Task </param> public Task(ScheduleTask task) { this.Type = task.Type; this.Enabled = task.Enabled; this.StopOnError = task.StopOnError; this.Name = task.Name; } #endregion #region Utilities private ITask CreateTask(ILifetimeScope scope) { ITask task = null; if (this.Enabled) { var type2 = System.Type.GetType(this.Type); if (type2 != null) { object instance; if (!EngineContext.Current.ContainerManager.TryResolve(type2, scope, out instance)) { //not resolved instance = EngineContext.Current.ContainerManager.ResolveUnregistered(type2, scope); } task = instance as ITask; } } return task; } #endregion #region Methods /// <summary> /// Executes the task /// </summary> /// <param name="throwException">A value indicating whether exception should be thrown if some error happens</param> /// <param name="dispose">A value indicating whether all instances should be disposed after task run</param> /// <param name="ensureRunOnOneWebFarmInstance">A value indicating whether we should ensure this task is run on one farm node at a time</param> public void Execute(bool throwException = false, bool dispose = true, bool ensureRunOnOneWebFarmInstance = true) { //background tasks has an issue with Autofac //because scope is generated each time it's requested //that's why we get one single scope here //this way we can also dispose resources once a task is completed var scope = EngineContext.Current.ContainerManager.Scope(); var scheduleTaskService = EngineContext.Current.ContainerManager.Resolve<IScheduleTaskService>("", scope); var scheduleTask = scheduleTaskService.GetTaskByType(this.Type); try { //task is run on one farm node at a time? if (ensureRunOnOneWebFarmInstance) { //is web farm enabled (multiple instances)? var nopConfig = EngineContext.Current.ContainerManager.Resolve<NopConfig>("", scope); if (nopConfig.MultipleInstancesEnabled) { var machineNameProvider = EngineContext.Current.ContainerManager.Resolve<IMachineNameProvider>("", scope); var machineName = machineNameProvider.GetMachineName(); if (String.IsNullOrEmpty(machineName)) { throw new Exception("Machine name cannot be detected. You cannot run in web farm."); //actually in this case we can generate some unique string (e.g. Guid) and store it in some "static" (!!!) variable //then it can be used as a machine name } //lease can't be aquired only if for a different machine and it has not expired if (scheduleTask.LeasedUntilUtc.HasValue && scheduleTask.LeasedUntilUtc.Value >= DateTime.UtcNow && scheduleTask.LeasedByMachineName != machineName) return; //lease the task. so it's run on one farm node at a time scheduleTask.LeasedByMachineName = machineName; scheduleTask.LeasedUntilUtc = DateTime.UtcNow.AddMinutes(30); scheduleTaskService.UpdateTask(scheduleTask); } } //initialize and execute var task = this.CreateTask(scope); if (task != null) { this.LastStartUtc = DateTime.UtcNow; if (scheduleTask != null) { //update appropriate datetime properties scheduleTask.LastStartUtc = this.LastStartUtc; scheduleTaskService.UpdateTask(scheduleTask); } task.Execute(); this.LastEndUtc = this.LastSuccessUtc = DateTime.UtcNow; } } catch (Exception exc) { this.Enabled = !this.StopOnError; this.LastEndUtc = DateTime.UtcNow; //log error var logger = EngineContext.Current.ContainerManager.Resolve<ILogger>("", scope); logger.Error(string.Format("Error while running the '{0}' schedule task. {1}", this.Name, exc.Message), exc); if (throwException) throw; } if (scheduleTask != null) { //update appropriate datetime properties scheduleTask.LastEndUtc = this.LastEndUtc; scheduleTask.LastSuccessUtc = this.LastSuccessUtc; scheduleTaskService.UpdateTask(scheduleTask); } //dispose all resources if (dispose) { scope.Dispose(); } } #endregion #region Properties /// <summary> /// Datetime of the last start /// </summary> public DateTime? LastStartUtc { get; private set; } /// <summary> /// Datetime of the last end /// </summary> public DateTime? LastEndUtc { get; private set; } /// <summary> /// Datetime of the last success /// </summary> public DateTime? LastSuccessUtc { get; private set; } /// <summary> /// A value indicating type of the task /// </summary> public string Type { get; private set; } /// <summary> /// A value indicating whether to stop task on error /// </summary> public bool StopOnError { get; private set; } /// <summary> /// Get the task name /// </summary> public string Name { get; private set; } /// <summary> /// A value indicating whether the task is enabled /// </summary> public bool Enabled { get; set; } #endregion }
8.TaskManager
/// <summary> /// Represents task manager /// </summary> public partial class TaskManager { private static readonly TaskManager _taskManager = new TaskManager(); private readonly List<TaskThread> _taskThreads = new List<TaskThread>(); private const int _notRunTasksInterval = 60 * 30; //30 minutes private TaskManager() { } /// <summary> /// Initializes the task manager /// </summary> public void Initialize() { this._taskThreads.Clear(); var taskService = EngineContext.Current.Resolve<IScheduleTaskService>(); var scheduleTasks = taskService .GetAllTasks() .OrderBy(x => x.Seconds) .ToList(); //group by threads with the same seconds foreach (var scheduleTaskGrouped in scheduleTasks.GroupBy(x => x.Seconds)) { //create a thread var taskThread = new TaskThread { Seconds = scheduleTaskGrouped.Key }; foreach (var scheduleTask in scheduleTaskGrouped) { var task = new Task(scheduleTask); taskThread.AddTask(task); } this._taskThreads.Add(taskThread); } //sometimes a task period could be set to several hours (or even days). //in this case a probability that it'll be run is quite small (an application could be restarted) //we should manually run the tasks which weren't run for a long time var notRunTasks = scheduleTasks //find tasks with "run period" more than 30 minutes .Where(x => x.Seconds >= _notRunTasksInterval) .Where(x => !x.LastStartUtc.HasValue || x.LastStartUtc.Value.AddSeconds(x.Seconds) < DateTime.UtcNow) .ToList(); //create a thread for the tasks which weren't run for a long time if (notRunTasks.Any()) { var taskThread = new TaskThread { RunOnlyOnce = true, Seconds = 60 * 5 //let's run such tasks in 5 minutes after application start }; foreach (var scheduleTask in notRunTasks) { var task = new Task(scheduleTask); taskThread.AddTask(task); } this._taskThreads.Add(taskThread); } } /// <summary> /// Starts the task manager /// </summary> public void Start() { foreach (var taskThread in this._taskThreads) { taskThread.InitTimer(); } } /// <summary> /// Stops the task manager /// </summary> public void Stop() { foreach (var taskThread in this._taskThreads) { taskThread.Dispose(); } } /// <summary> /// Gets the task mamanger instance /// </summary> public static TaskManager Instance { get { return _taskManager; } } /// <summary> /// Gets a list of task threads of this task manager /// </summary> public IList<TaskThread> TaskThreads { get { return new ReadOnlyCollection<TaskThread>(this._taskThreads); } } }
9.TaskThread
/// <summary> /// Represents task thread /// </summary> public partial class TaskThread : IDisposable { private Timer _timer; private bool _disposed; private readonly Dictionary<string, Task> _tasks; internal TaskThread() { this._tasks = new Dictionary<string, Task>(); this.Seconds = 10 * 60; } private void Run() { if (Seconds <= 0) return; this.StartedUtc = DateTime.UtcNow; this.IsRunning = true; foreach (Task task in this._tasks.Values) { task.Execute(); } this.IsRunning = false; } private void TimerHandler(object state) { this._timer.Change(-1, -1); this.Run(); if (this.RunOnlyOnce) { this.Dispose(); } else { this._timer.Change(this.Interval, this.Interval); } } /// <summary> /// Disposes the instance /// </summary> public void Dispose() { if ((this._timer != null) && !this._disposed) { lock (this) { this._timer.Dispose(); this._timer = null; this._disposed = true; } } } /// <summary> /// Inits a timer /// </summary> public void InitTimer() { if (this._timer == null) { this._timer = new Timer(new TimerCallback(this.TimerHandler), null, this.Interval, this.Interval); } } /// <summary> /// Adds a task to the thread /// </summary> /// <param name="task">The task to be added</param> public void AddTask(Task task) { if (!this._tasks.ContainsKey(task.Name)) { this._tasks.Add(task.Name, task); } } /// <summary> /// Gets or sets the interval in seconds at which to run the tasks /// </summary> public int Seconds { get; set; } /// <summary> /// Get or sets a datetime when thread has been started /// </summary> public DateTime StartedUtc { get; private set; } /// <summary> /// Get or sets a value indicating whether thread is running /// </summary> public bool IsRunning { get; private set; } /// <summary> /// Get a list of tasks /// </summary> public IList<Task> Tasks { get { var list = new List<Task>(); foreach (var task in this._tasks.Values) { list.Add(task); } return new ReadOnlyCollection<Task>(list); } } /// <summary> /// Gets the interval at which to run the tasks /// </summary> public int Interval { get { return this.Seconds * 1000; } } /// <summary> /// Gets or sets a value indicating whether the thread whould be run only once (per appliction start) /// </summary> public bool RunOnlyOnce { get; set; } }
-------------------------
10.Global.ascx.cs
protected void Application_Start() { TaskManager.Instance.Initialize(); TaskManager.Instance.Start(); }
使用
CancelOrderTask-----------路径:BotanicSystem.Services.Order.CancelOrderTask,BotanicSystem.Services
贴一条数据库数据
3 | 订单自动取消 | 1000 | BotanicSystem.Services.Order.CancelOrderTask,BotanicSystem.Services | False | False | NULL | NULL | 2016-10-13 00:54:31.927 | NULL | NULL |
贴代码
public partial class CancelOrderTask:ITask { #region fields private readonly IOrderInfoService _orderInfoService; #endregion #region Ctor public CancelOrderTask(IOrderInfoService orderInfoService) { _orderInfoService = orderInfoService; } #endregion public void Execute() { //获取所有30分钟内未支付的订单 var cancelOrders = _orderInfoService.GetListByWhere( p => DbFunctions.DiffMinutes(p.Add_Time, DateTime.Now) >= 30 && (p.Status == (int) OrderStatus.未提交) || p.Status == (int) OrderStatus.进行中); //设置订单状态为取消 foreach (var order in cancelOrders) { order.Status = (int) OrderStatus.已过期; } _orderInfoService.UpdateOrders(cancelOrders); } }