6

I have a threadsafe object that is expensive to create and needs to be available through my application (a Lucene.Net IndexReader).

The object can become invalid, at which point I need to recreate it (IndexReader.IsCurrent is false, need a new instance using IndexReader.Reopen).

I'd like to able to use an IoC container (StructureMap) to manage the creation of the object, but I can't work out if this scenario is possible. It feels like some kind of "conditional singleton" lifecycle.

Does StructureMap provide such a feature? Any alternative suggestions?

Gareth D
  • 885
  • 1
  • 9
  • 22

2 Answers2

3

I would probably use a scope of PerRequest and not return the IndexReader directly. Instead, I'd return an abstraction of the IndexReader which would perform a check on a static reference held on the class level.

Then, when your property on the shim/proxy/abstraction is accessed, it would check the static reference (you would make it thread-safe, of course) and re-get the IndexReader if needed before delivering it back to the user.

casperOne
  • 73,706
  • 19
  • 184
  • 253
  • I agree with casperOne. Think about hiding the instance behind an interface / facade so you can more easily implement strategies such as object pooling. – Steven Feb 09 '11 at 19:26
  • I considered this, but it feels a shame to be removing resposibiliy for object creation and lifetime management away from the IoC container. It will definitely work for me and will be my fall back solution if I don't come up with anything more IoC centric. – Gareth D Feb 09 '11 at 19:39
1

In the end I have gone for a simple proxy object that wraps the actual IndexReader and manages the Reopening. As I need to use the same instance of this across requests I am using StructureMap to provide a singleton instance of it. Code below.

I've investigated creating a custom StructureMap ILifecycle to handle this situation, but didn't get to far, see this question.

public class IndexReaderProxy
{
    private IndexReader _indexReader;
    private readonly object _indexReaderLock = new object();

    public IndexReaderProxy(Directory directory, bool readOnly)
    {
        _indexReader = IndexReader.Open(directory, readOnly);
    }

    public IndexReader GetCurrentIndexReader()
    {
        ReopenIndexReaderIfNotCurrent();
        return _indexReader;
    }

    private void ReopenIndexReaderIfNotCurrent()
    {
        if (_indexReader.IsCurrent()) return;
        lock (_indexReaderLock)
        {
            if (_indexReader.IsCurrent()) return;
            var newIndexReader = _indexReader.Reopen();
            _indexReader.Close();
            _indexReader = newIndexReader;
        }
    }
}

And the StructureMap registration:

For<IndexReaderProxy>().Singleton().Use(
            new IndexReaderProxy(FSDirectory.Open(new DirectoryInfo(LuceneIndexPath)), true)
        );
Community
  • 1
  • 1
Gareth D
  • 885
  • 1
  • 9
  • 22
  • Content should probably be merged with the question. – casperOne Feb 10 '11 at 17:16
  • The first if (_indexReader.IsCurrent()) return; outside of the lock can throw a Lucene.Net.Store.AlreadyClosedException, which should be caught. When this thread can enter the lock, the IndexReader has been opened again on the current index. – ENOTTY Jun 24 '13 at 05:59