using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using MP.TaskMan.Models;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static MP.TaskMan.Objects.Enums;
namespace MP.TaskMan.Controllers
{
public class MpTaskController : IDisposable
{
#region Public Constructors
public MpTaskController(IConfiguration configuration)
{
_configuration = configuration;
Log.Info("Avviato MpTaskController");
}
#endregion Public Constructors
#region Public Methods
public DateTime CalcNextExe(TaskListModel taskRec)
{
DateTime dtNext = DateTime.Today;
DateTime adesso = DateTime.Now;
int maxIter = 1000;
try
{
// prendo come partenza la DATA di fine a cui aggiungo l'ora/minuti schedulata precedentemente
TimeSpan oraSched = taskRec.DtNextExec.Subtract(taskRec.DtNextExec.Date);
DateTime baseDT = taskRec.DtLastExec.Date.Add(oraSched);
// calcolo next exec da tipo... con ciclo fino a quando supero dataora attuale...
while (dtNext < adesso && maxIter > 0)
{
switch (taskRec.Freq)
{
case TaskFreqType.ND:
dtNext = baseDT.AddDays(taskRec.Cad);
break;
case TaskFreqType.Sec:
dtNext = baseDT.AddSeconds(taskRec.Cad);
break;
case TaskFreqType.Min:
dtNext = baseDT.AddMinutes(taskRec.Cad);
break;
case TaskFreqType.Hour:
dtNext = baseDT.AddHours(taskRec.Cad);
break;
case TaskFreqType.Day:
dtNext = baseDT.AddDays(taskRec.Cad);
break;
case TaskFreqType.Week:
dtNext = baseDT.AddDays(7 * taskRec.Cad);
break;
case TaskFreqType.Month:
dtNext = baseDT.AddMonths(taskRec.Cad);
break;
case TaskFreqType.Year:
dtNext = baseDT.AddYears(taskRec.Cad);
break;
default:
dtNext = baseDT.AddDays(taskRec.Cad);
break;
}
baseDT = dtNext;
maxIter--;
}
}
catch (Exception exc)
{
Log.Error($"Eccezione in CalcNextExe{Environment.NewLine}{exc}");
}
return dtNext;
}
public void Dispose()
{
_configuration = null;
}
///
/// Chiamata esecuzione di un singolo task programmato, SE in stato abilitato
///
///
/// Se true rischedula successiva chiamata
///
public TaskResultModel ExecuteSqlTask(int TaskId, bool SchedNext)
{
TaskResultModel callRes = new TaskResultModel();
using (var dbCtx = new TaskContext(_configuration))
{
// imposto timeout a 5 min
//var currTimeout = dbCtx.Database.GetCommandTimeout();
dbCtx.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));
string sqlCall = "";
try
{
DateTime dtStart = DateTime.Now;
// recupero i dati da richiamare...
var currRec = dbCtx
.DbSetTaskList
.Where(x => x.TaskId == TaskId && x.Enabled)
.FirstOrDefault();
if (currRec != null)
{
// recupero comando
string sqlCommand = currRec.Command;
string rawParams = currRec.Args;
// salvo la call x eventuale log errore
sqlCall = $"EXEC {sqlCommand} {rawParams}";
var rawData = dbCtx
.DbSetTaskResult
.FromSqlRaw($"EXEC {sqlCommand} {rawParams}")
.ToList();
callRes = rawData.FirstOrDefault() ?? new TaskResultModel();
DateTime dtEnd = DateTime.Now;
// preparo record esecuzione...
TaskExecModel resRec = new TaskExecModel()
{
TaskId = TaskId,
DtStart = dtStart,
DtEnd = dtEnd,
IsError = callRes.ExecResult < 0,
Result = callRes.TextResult
};
dbCtx
.DbSetTaskExe
.Add(resRec);
// aggiorno record chiamata...
currRec.DtLastExec = dtStart;
currRec.LastResult = resRec.Result;
currRec.LastIsError = resRec.IsError;
currRec.LastDuration = dtEnd.Subtract(dtStart).TotalSeconds;
// solo se richiesto rischedulazione ricalcola chiamata
if (SchedNext)
{
// calcolo prossima esecuzione...
currRec.DtNextExec = CalcNextExe(currRec);
}
// segno modificato
dbCtx.Entry(currRec).State = EntityState.Modified;
// salvo modifiche!
dbCtx.SaveChanges();
}
}
catch (Exception exc)
{
Log.Error($"Eccezione in ExecuteSqlCommand{Environment.NewLine}Call eseguita:{Environment.NewLine}{sqlCall}{Environment.NewLine}------- Stack Eccezione -------{Environment.NewLine}{exc}");
}
}
return callRes;
}
///
/// Annulla modifiche su una specifica entity (cancel update)
///
///
///
public bool RollBackEntity(object item)
{
bool answ = false;
using (var dbCtx = new TaskContext(_configuration))
{
try
{
if (dbCtx.Entry(item).State == EntityState.Deleted || dbCtx.Entry(item).State == EntityState.Modified)
{
dbCtx.Entry(item).Reload();
}
}
catch (Exception exc)
{
Log.Error($"Eccezione in rollBackEntity{Environment.NewLine}{exc}");
}
}
return answ;
}
///
/// Ricerca task dato tipo + num max (desc)
///
/// TaskId da cui deriva
///
public List TaskExecGetFilt(int TaskId, int maxRec)
{
List dbResult = new List();
using (var dbCtx = new TaskContext(_configuration))
{
dbResult = dbCtx
.DbSetTaskExe
.Include(x => x.TaskListNav)
.Where(x => (x.TaskId == TaskId))
.OrderByDescending(x => x.DtStart)
.Take(maxRec)
.ToList();
}
return dbResult;
}
///
/// Esegue registrazione di un Task generico (NON SQL)
///
///
/// Se true rischedula successiva chiamata
/// Record esecuzione task esterno
///
public TaskResultModel TaskExecSaveExecuted(int TaskId, bool SchedNext, TaskExecModel ResRec)
{
TaskResultModel callRes = new TaskResultModel();
using (var dbCtx = new TaskContext(_configuration))
{
try
{
// recupero i dati da richiamare...
var currRec = dbCtx
.DbSetTaskList
.Where(x => x.TaskId == TaskId)
.FirstOrDefault();
if (currRec != null)
{
// registro task ricevuto
dbCtx
.DbSetTaskExe
.Add(ResRec);
// aggiorno record chiamata...
currRec.DtLastExec = ResRec.DtStart;
currRec.LastResult = ResRec.Result;
currRec.LastIsError = ResRec.IsError;
currRec.LastDuration = ResRec.DtEnd.Subtract(ResRec.DtStart).TotalSeconds;
// solo se richiesto rischedulazione ricalcola chiamata
if (SchedNext)
{
// calcolo prossima esecuzione...
currRec.DtNextExec = CalcNextExe(currRec);
}
// segno modificato
dbCtx.Entry(currRec).State = EntityState.Modified;
// salvo modifiche!
dbCtx.SaveChanges();
}
}
catch (Exception exc)
{
Log.Error($"Eccezione in TaskExecSaveExecuted{Environment.NewLine}{exc}");
}
}
return callRes;
}
///
/// Upsert record TaskExec
///
/// Record da aggiornare/inserire
///
public bool TaskExecUpsert(TaskExecModel rec2upd)
{
bool done = false;
using (var dbCtx = new TaskContext(_configuration))
{
try
{
var currData = dbCtx
.DbSetTaskExe
.Where(x => x.TaskExecId == rec2upd.TaskExecId)
.FirstOrDefault();
if (currData != null)
{
currData.TaskId = rec2upd.TaskId;
currData.DtStart = rec2upd.DtStart;
currData.DtEnd = rec2upd.DtEnd;
currData.IsError = rec2upd.IsError;
currData.Result = rec2upd.Result;
dbCtx.Entry(currData).State = EntityState.Modified;
}
else
{
dbCtx
.DbSetTaskExe
.Add(rec2upd);
}
dbCtx.SaveChanges();
done = true;
}
catch (Exception exc)
{
Log.Error($"Eccezione in TaskExecUpsert{Environment.NewLine}{exc}");
}
}
return done;
}
///
/// Ricerca task dato tipo e
///
///
///
public List TaskListGetAll(Task2ExeType TType)
{
List dbResult = new List();
using (var dbCtx = new TaskContext(_configuration))
{
dbResult = dbCtx
.DbSetTaskList
.Where(x => (TType == Task2ExeType.ALL || x.TType == TType))
.OrderBy(x => x.Ordinal)
.ToList();
}
return dbResult;
}
///
/// Update ordinamento task
///
/// Record da spostare x priorità
///
public bool TaskListMove(TaskListModel rec2upd, bool moveUp)
{
bool done = false;
using (var dbCtx = new TaskContext(_configuration))
{
try
{
var currData = dbCtx
.DbSetTaskList
.Where(x => x.TaskId == rec2upd.TaskId)
.FirstOrDefault();
if (currData != null)
{
int actOrdinal = currData.Ordinal;
TaskListModel? otherRec = null;
// cerco, secondo richiesta, precedente o successivo
if (moveUp)
{
otherRec = dbCtx
.DbSetTaskList
.Where(x => x.Ordinal < currData.Ordinal && x.Group==currData.Group)
.OrderByDescending(x => x.Ordinal)
.FirstOrDefault();
}
else
{
otherRec = dbCtx
.DbSetTaskList
.Where(x => x.Ordinal > currData.Ordinal && x.Group == currData.Group)
.OrderBy(x => x.Ordinal)
.FirstOrDefault();
}
// inverto ordinale SE ho record
if (otherRec != null)
{
currData.Ordinal = otherRec.Ordinal;
otherRec.Ordinal = actOrdinal;
dbCtx.Entry(currData).State = EntityState.Modified;
dbCtx.Entry(otherRec).State = EntityState.Modified;
}
}
//salvo
dbCtx.SaveChanges();
done = true;
}
catch (Exception exc)
{
Log.Error($"Eccezione in TaskListUpsert{Environment.NewLine}{exc}");
}
}
return done;
}
///
/// Riordino record di un dato gruppo (da richiamare post cambio gruppo...)
///
///
///
public bool TaskListReorder(int codGroup)
{
bool done = false;
using (var dbCtx = new TaskContext(_configuration))
{
// in primis recupero i record del gruppo ordinati
var currList = dbCtx
.DbSetTaskList
.Where(x => x.Group == codGroup)
.OrderBy(x => x.Ordinal)
.ThenBy(x => x.TaskId)
.ToList();
int newIdx = 0;
int numRec = currList.Count;
// li verifico in base al loro id attuale...
foreach (var item in currList)
{
newIdx++;
// se va cambiato lo riassegno
if (item.Ordinal != newIdx)
{
item.Ordinal = newIdx;
dbCtx.Entry(item).State = EntityState.Modified;
}
}
// salvo!
try
{
dbCtx.SaveChanges();
done = true;
}
catch (Exception exc)
{
Log.Error($"Eccezione in TaskListReorder{Environment.NewLine}{exc}");
}
}
return done;
}
///
/// Upsert record TaskList
///
/// Record da aggiornare/inserire
///
public bool TaskListUpsert(TaskListModel rec2upd)
{
bool done = false;
using (var dbCtx = new TaskContext(_configuration))
{
try
{
var currData = dbCtx
.DbSetTaskList
.Where(x => x.TaskId == rec2upd.TaskId && rec2upd.TaskId > 0)
.FirstOrDefault();
if (currData != null)
{
currData.Ordinal = rec2upd.Ordinal;
currData.Group = rec2upd.Group;
currData.Enabled = rec2upd.Enabled;
currData.Name = rec2upd.Name;
currData.Descript = rec2upd.Descript;
currData.Command = rec2upd.Command;
currData.Args = rec2upd.Args;
currData.Freq = rec2upd.Freq;
currData.Cad = rec2upd.Cad;
currData.DtLastExec = rec2upd.DtLastExec;
currData.DtNextExec = rec2upd.DtNextExec;
currData.LastDuration = rec2upd.LastDuration;
currData.LastResult = rec2upd.LastResult;
dbCtx.Entry(currData).State = EntityState.Modified;
}
else
{
dbCtx
.DbSetTaskList
.Add(rec2upd);
}
dbCtx.SaveChanges();
done = true;
}
catch (Exception exc)
{
Log.Error($"Eccezione in TaskListUpsert{Environment.NewLine}{exc}");
}
}
return done;
}
#endregion Public Methods
#region Private Fields
private static Logger Log = LogManager.GetCurrentClassLogger();
#endregion Private Fields
#region Private Properties
private static IConfiguration _configuration { get; set; } = null!;
#endregion Private Properties
}
}