在等待 filesystemwatcher 找到新更改的文件时,您可以让您的程序做其他事情吗?
Can you make your program do other things while waiting for the filesystemwatcher to find newly changed files?
我正在构建一个程序,用于跟踪目录并在目录中创建新文件时向 mysql 数据库添加一个条目。
我将展示 FileSystemWatcher 的代码,存储实例是 mysql class:
FileSystemWatcher watcher = new FileSystemWatcher
{
Path = directoryToWatch,
IncludeSubdirectories = true,
NotifyFilter = NotifyFilters.Attributes |
NotifyFilters.DirectoryName |
NotifyFilters.FileName,
EnableRaisingEvents = true,
Filter = "*.*"
};
watcher.Created += (OnDirectoryChange);
public void OnDirectoryChange(object sender, FileSystemEventArgs e)
{
storage.Insert(Settings.Default.added_files, e.Name);
}
很清楚了。这是 mysql 数据库的代码。 CloseConnection 与 'OpenConnection' 几乎相同,所以我没有复制它。
public bool OpenConnection()
{
try
{
connection.Open();
return true;
}
catch (Exception ex)
{
throw ex;
}
}
public void Insert(string tablename, string filename, string attractionCode, int number=0)
{
string query = "INSERT INTO " + tablename + " (FILE_NAME) VALUES('" + filename + "')";
MySqlCommand cmd = new MySqlCommand(query, connection);
OpenConnection();
cmd.ExecuteNonQuery();
CloseConnection();
}
现在的问题是,当我一次将 20 个文件粘贴到 filesystemwatcher 检查的目录中时,sql 只会处理其中的 18 个。它会抛出类似 'Connection was open already' 的错误。我还在 sql 代码的某处使用了 select 语句,其中包含这部分,例如:
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataReader dataReader = cmd.ExecuteReader();
while (dataReader.Read())
{
list[0].Add(dataReader["id"] + "");
list[1].Add(dataReader["code"] + "");
list[2].Add(dataReader["name"] + "");
}
dataReader.Close();
这有时也会抛出类似 'can only use one datareader' 的错误。
我认为对我来说解决方案是为 filesystemwatcher 处理的所有文件创建某种队列,然后一个一个地遍历这个队列。但是我该如何处理呢,因为 filesystemwatcher 必须一直监视目录。恐怕在处理队列时可能会遗漏一些文件。有什么用?
从这个优秀的 solution 中借鉴 class 在您的项目中的某处添加:
public class BackgroundQueue
{
private Task previousTask = Task.FromResult(true);
private object key = new object();
public Task QueueTask(Action action)
{
lock (key)
{
previousTask = previousTask.ContinueWith(t => action()
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.Default);
return previousTask;
}
}
public Task<T> QueueTask<T>(Func<T> work)
{
lock (key)
{
var task = previousTask.ContinueWith(t => work()
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.Default);
previousTask = task;
return task;
}
}
}
我提议对您的主模块进行以下更改:
// Place this as a module level variable.. so it doesn't go out of scope as long
// as the FileSystemWatcher is running
BackgroundQueue _bq = new BackgroundQueue();
然后进行以下更改以调用队列:
FileSystemWatcher watcher = new FileSystemWatcher
{
Path = directoryToWatch,
IncludeSubdirectories = true,
NotifyFilter = NotifyFilters.Attributes |
NotifyFilters.DirectoryName |
NotifyFilters.FileName,
EnableRaisingEvents = true,
Filter = "*.*"
};
watcher.Created += (OnDirectoryChange);
public void OnDirectoryChange(object sender, FileSystemEventArgs e)
{
// Using the shorthand lambda syntax
_bq.QueueTask(() => storage.Insert(Settings.Default.added_files, e.Name));
}
这应该对 FileSystemWatcher 抛出的每个更改进行排队。请记住此 SO Question 中有关 FileSystemWatcher 未捕获所有内容的评论。
我正在构建一个程序,用于跟踪目录并在目录中创建新文件时向 mysql 数据库添加一个条目。
我将展示 FileSystemWatcher 的代码,存储实例是 mysql class:
FileSystemWatcher watcher = new FileSystemWatcher
{
Path = directoryToWatch,
IncludeSubdirectories = true,
NotifyFilter = NotifyFilters.Attributes |
NotifyFilters.DirectoryName |
NotifyFilters.FileName,
EnableRaisingEvents = true,
Filter = "*.*"
};
watcher.Created += (OnDirectoryChange);
public void OnDirectoryChange(object sender, FileSystemEventArgs e)
{
storage.Insert(Settings.Default.added_files, e.Name);
}
很清楚了。这是 mysql 数据库的代码。 CloseConnection 与 'OpenConnection' 几乎相同,所以我没有复制它。
public bool OpenConnection()
{
try
{
connection.Open();
return true;
}
catch (Exception ex)
{
throw ex;
}
}
public void Insert(string tablename, string filename, string attractionCode, int number=0)
{
string query = "INSERT INTO " + tablename + " (FILE_NAME) VALUES('" + filename + "')";
MySqlCommand cmd = new MySqlCommand(query, connection);
OpenConnection();
cmd.ExecuteNonQuery();
CloseConnection();
}
现在的问题是,当我一次将 20 个文件粘贴到 filesystemwatcher 检查的目录中时,sql 只会处理其中的 18 个。它会抛出类似 'Connection was open already' 的错误。我还在 sql 代码的某处使用了 select 语句,其中包含这部分,例如:
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataReader dataReader = cmd.ExecuteReader();
while (dataReader.Read())
{
list[0].Add(dataReader["id"] + "");
list[1].Add(dataReader["code"] + "");
list[2].Add(dataReader["name"] + "");
}
dataReader.Close();
这有时也会抛出类似 'can only use one datareader' 的错误。
我认为对我来说解决方案是为 filesystemwatcher 处理的所有文件创建某种队列,然后一个一个地遍历这个队列。但是我该如何处理呢,因为 filesystemwatcher 必须一直监视目录。恐怕在处理队列时可能会遗漏一些文件。有什么用?
从这个优秀的 solution 中借鉴 class 在您的项目中的某处添加:
public class BackgroundQueue
{
private Task previousTask = Task.FromResult(true);
private object key = new object();
public Task QueueTask(Action action)
{
lock (key)
{
previousTask = previousTask.ContinueWith(t => action()
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.Default);
return previousTask;
}
}
public Task<T> QueueTask<T>(Func<T> work)
{
lock (key)
{
var task = previousTask.ContinueWith(t => work()
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.Default);
previousTask = task;
return task;
}
}
}
我提议对您的主模块进行以下更改:
// Place this as a module level variable.. so it doesn't go out of scope as long
// as the FileSystemWatcher is running
BackgroundQueue _bq = new BackgroundQueue();
然后进行以下更改以调用队列:
FileSystemWatcher watcher = new FileSystemWatcher
{
Path = directoryToWatch,
IncludeSubdirectories = true,
NotifyFilter = NotifyFilters.Attributes |
NotifyFilters.DirectoryName |
NotifyFilters.FileName,
EnableRaisingEvents = true,
Filter = "*.*"
};
watcher.Created += (OnDirectoryChange);
public void OnDirectoryChange(object sender, FileSystemEventArgs e)
{
// Using the shorthand lambda syntax
_bq.QueueTask(() => storage.Insert(Settings.Default.added_files, e.Name));
}
这应该对 FileSystemWatcher 抛出的每个更改进行排队。请记住此 SO Question 中有关 FileSystemWatcher 未捕获所有内容的评论。