Поэтому я провел небольшое исследование, чтобы найти лучший способ реализации поиска и записи по индексу Lucene.Net из веб-приложения. Я изложил следующие требования:
. Я нашел несколько полезных ресурсов и пару хороших вопросов здесь по SO, например этот
Следуя этому сообщению в качестве руководства, я решил попробовать одноэлементный шаблон с параллельным словарем оболочки, созданной для управления индексом.
Чтобы упростить задачу, я представлю, что управляю только одним индексом, и в этом случае оболочка может стать синглтоном. В итоге это выглядит так:
public sealed class SingleIndexManager
{
private const string IndexDirectory = "C:\\IndexDirectory\\";
private const string IndexName = "test-index";
private static readonly Version _version = Version.LUCENE_29;
#region Singleton Behavior
private static volatile SingleIndexManager _instance;
private static object syncRoot = new Object();
public static SingleIndexManager Instance
{
get
{
if (_instance == null)
{
lock (syncRoot)
{
if (_instance == null)
_instance = new SingleIndexManager();
}
}
return _instance;
}
}
#endregion
private IndexWriter _writer;
private IndexSearcher _searcher;
private int _activeSearches = 0;
private int _activeWrites = 0;
private SingleIndexManager()
{
lock(syncRoot)
{
_writer = CreateWriter(); //hidden for sake of brevity
_searcher = new IndexSearcher(_writer.GetReader());
}
}
public List Search(Func> searchMethod)
{
lock(syncRoot)
{
if(_searcher != null && !_searcher.GetIndexReader().IsCurrent() && _activeSearches == 0)
{
_searcher.Close();
_searcher = null;
}
if(_searcher == null)
{
_searcher = new IndexSearcher((_writer ?? (_writer = CreateWriter())).GetReader());
}
}
List results;
Interlocked.Increment(ref _activeSearches);
try
{
results = searchMethod(_searcher);
}
finally
{
Interlocked.Decrement(ref _activeSearches);
}
return results;
}
public void Write(List docs)
{
lock(syncRoot)
{
if(_writer == null)
{
_writer = CreateWriter();
}
}
try
{
Interlocked.Increment(ref _activeWrites);
foreach (Document document in docs)
{
_writer.AddDocument(document, new StandardAnalyzer(_version));
}
}
finally
{
lock(syncRoot)
{
int writers = Interlocked.Decrement(ref _activeWrites);
if(writers == 0)
{
_writer.Close();
_writer = null;
}
}
}
}
}
Теоретически предполагается, что это позволит создать потокобезопасный одноэлементный экземпляр -для индекса (, названного здесь «индекс -тест» ), где у меня есть два общедоступных метода, Search()
и Write()
, которые могут вызываться из веб-приложения ASP.NET, не беспокоясь о безопасности потоков? (Если это неверно, сообщите мне ).
Была одна вещь, которая меня сейчас немного беспокоит:
Как изящно закрыть эти экземпляры на Application_End
в файле Global.asax.cs, чтобы, если я хочу перезапустить свое веб-приложение в IIS,Я не собираюсь получать кучу отказов write.lock и т. д.?
Все, о чем я могу думать, это:
public void Close()
{
lock(syncRoot)
{
_searcher.Close();
_searcher.Dispose();
_searcher = null;
_writer.Close();
_writer.Dispose();
_writer = null;
}
}
и вызывая это в Application_End
, но если у меня есть какие-либо активные искатели или писатели, приведет ли это к повреждению индекса?
Любая помощь или предложения очень ценятся. Благодарю.