Добавьте файлы проекта.
This commit is contained in:
197
SQLVision.Services/Services/ScriptManager.cs
Normal file
197
SQLVision.Services/Services/ScriptManager.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SQLVision.Core.Enums;
|
||||
using SQLVision.Core.Interfaces;
|
||||
using SQLVision.Core.Models;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace SQLVision.Services.Services;
|
||||
|
||||
public class ScriptManager : IScriptManager, IDisposable
|
||||
{
|
||||
private readonly ISqlScriptParser _parser;
|
||||
private readonly ILogger<ScriptManager> _logger;
|
||||
private readonly FileSystemWatcher _watcher;
|
||||
private readonly ConcurrentDictionary<string, ScriptMetadata> _scripts;
|
||||
private readonly string _scriptsDirectory;
|
||||
|
||||
public event EventHandler<ScriptChangedEventArgs>? ScriptChanged;
|
||||
public event EventHandler<ScriptsReloadedEventArgs>? ScriptsReloaded;
|
||||
|
||||
public ScriptManager(ISqlScriptParser parser, IConfiguration configuration, ILogger<ScriptManager> logger)
|
||||
{
|
||||
_parser = parser;
|
||||
_logger = logger;
|
||||
_scripts = new ConcurrentDictionary<string, ScriptMetadata>();
|
||||
_scriptsDirectory = configuration["Scripts:Directory"] ?? "Scripts";
|
||||
|
||||
_watcher = new FileSystemWatcher
|
||||
{
|
||||
Path = _scriptsDirectory,
|
||||
Filter = "*.sql",
|
||||
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName,
|
||||
EnableRaisingEvents = false
|
||||
};
|
||||
|
||||
_watcher.Changed += OnScriptChanged;
|
||||
_watcher.Created += OnScriptCreated;
|
||||
_watcher.Deleted += OnScriptDeleted;
|
||||
_watcher.Renamed += OnScriptRenamed;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ScriptMetadata>> LoadScriptsAsync(string? directory = null)
|
||||
{
|
||||
var targetDirectory = directory ?? _scriptsDirectory;
|
||||
|
||||
if (!Directory.Exists(targetDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(targetDirectory);
|
||||
_logger.LogInformation("Created scripts directory: {Directory}", targetDirectory);
|
||||
return Enumerable.Empty<ScriptMetadata>();
|
||||
}
|
||||
|
||||
var sqlFiles = Directory.GetFiles(targetDirectory, "*.sql", SearchOption.AllDirectories);
|
||||
var tasks = sqlFiles.Select(LoadScriptAsync);
|
||||
var results = await Task.WhenAll(tasks);
|
||||
|
||||
_scripts.Clear();
|
||||
foreach (var script in results.Where(s => s != null))
|
||||
{
|
||||
_scripts[script!.FullPath] = script;
|
||||
}
|
||||
|
||||
StartWatching();
|
||||
|
||||
ScriptsReloaded?.Invoke(this, new ScriptsReloadedEventArgs(results.Where(s => s != null).ToList()!));
|
||||
|
||||
_logger.LogInformation("Loaded {Count} scripts from {Directory}", _scripts.Count, targetDirectory);
|
||||
return _scripts.Values;
|
||||
}
|
||||
|
||||
private async Task<ScriptMetadata?> LoadScriptAsync(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _parser.ParseAsync(filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error loading script: {FilePath}", filePath);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ScriptMetadata> ReloadScriptAsync(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var script = await LoadScriptAsync(filePath);
|
||||
if (script != null)
|
||||
{
|
||||
_scripts[filePath] = script;
|
||||
ScriptChanged?.Invoke(this, new ScriptChangedEventArgs(filePath, ScriptChangeType.Updated, script));
|
||||
}
|
||||
return script!;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error reloading script: {FilePath}", filePath);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnScriptChanged(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
// Задержка для избежания многократных вызовов
|
||||
Task.Delay(300).ContinueWith(async _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var script = await ReloadScriptAsync(e.FullPath);
|
||||
if (script != null)
|
||||
{
|
||||
_logger.LogInformation("Script changed: {FileName}", e.Name);
|
||||
}
|
||||
}
|
||||
catch { /* Игнорируем ошибки */ }
|
||||
});
|
||||
}
|
||||
|
||||
private void OnScriptCreated(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
Task.Delay(300).ContinueWith(async _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var script = await LoadScriptAsync(e.FullPath);
|
||||
if (script != null)
|
||||
{
|
||||
_scripts[e.FullPath] = script;
|
||||
ScriptChanged?.Invoke(this, new ScriptChangedEventArgs(e.FullPath, ScriptChangeType.Created, script));
|
||||
_logger.LogInformation("Script created: {FileName}", e.Name);
|
||||
}
|
||||
}
|
||||
catch { /* Игнорируем */ }
|
||||
});
|
||||
}
|
||||
|
||||
private void OnScriptDeleted(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
if (_scripts.TryRemove(e.FullPath, out var script))
|
||||
{
|
||||
ScriptChanged?.Invoke(this, new ScriptChangedEventArgs(e.FullPath, ScriptChangeType.Deleted, script));
|
||||
_logger.LogInformation("Script deleted: {FileName}", e.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnScriptRenamed(object sender, RenamedEventArgs e)
|
||||
{
|
||||
Task.Delay(300).ContinueWith(async _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Удаляем старый файл
|
||||
_scripts.TryRemove(e.OldFullPath, out var s);
|
||||
|
||||
// Загружаем новый
|
||||
var script = await LoadScriptAsync(e.FullPath);
|
||||
if (script != null)
|
||||
{
|
||||
_scripts[e.FullPath] = script;
|
||||
ScriptChanged?.Invoke(this,
|
||||
new ScriptChangedEventArgs(e.FullPath, ScriptChangeType.Renamed, script));
|
||||
_logger.LogInformation("Script renamed: {OldName} -> {NewName}",
|
||||
Path.GetFileName(e.OldFullPath), e.Name);
|
||||
}
|
||||
}
|
||||
catch { /* Игнорируем */ }
|
||||
});
|
||||
}
|
||||
|
||||
private void StartWatching()
|
||||
{
|
||||
if (!_watcher.EnableRaisingEvents)
|
||||
{
|
||||
_watcher.EnableRaisingEvents = true;
|
||||
_logger.LogDebug("Started watching directory: {Directory}", _scriptsDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
public void WatchDirectory(string directory, Action<string> onScriptChanged)
|
||||
{
|
||||
if (_watcher.EnableRaisingEvents)
|
||||
{
|
||||
_watcher.EnableRaisingEvents = false;
|
||||
}
|
||||
|
||||
_watcher.Path = directory;
|
||||
_watcher.EnableRaisingEvents = true;
|
||||
|
||||
ScriptChanged += (sender, e) => onScriptChanged?.Invoke(e.FilePath);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_watcher?.Dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user