How to make RavenDB DocumentStore available to cal

2019-06-12 11:29发布

问题:

I have an MVC4 application using RavenDB as a datastore. The application has MVC/Web, Domain, Data, and Security layers.

I am writing custom membership and role providers that need to initialize the database and access the DocumentStore. I'm writing these class from the Security layer, and would like to use a singleton DocumentStore (set in the application), but I can't figure out how to access it.

Other, examples I see of writing custom providers for RavenDB create new DocumentStore instances within the Provider.Initialize() methods, but that seems to break the rule of having a single DocumentStore per server.

Currently, I create a single instance of the RavenDB DocumentStore in Application_Start(). I have a base controller in the MVC/Web layer that handles the DocumentStore.Session(s).

Is there a way of accomplishing this? Should I move my security logic into the MVC/Web layer to simplify things?

回答1:

You probably want to use some sort of dependency injection, either by using automatic tools like TinyIoC or by doing this manually.

For example, see how RaccoonBlog does this. First, it creates a DocumentStore instance at Application_Start, and stores that reference in static variables of various base classes:

https://github.com/ayende/RaccoonBlog/blob/master/RaccoonBlog.Web/Global.asax.cs#L66

It then injects a new Session object whenever a new session starts:

https://github.com/ayende/RaccoonBlog/blob/master/RaccoonBlog.Web/Global.asax.cs#L31

And this item is being pulled from the Controller base-class:

https://github.com/ayende/RaccoonBlog/blob/master/HibernatingRhinos.Loci.Common/Controllers/RavenController.cs#L16

I would have done this a bit nicer, by using proper IoC or at least without using the Items dictionary, but you get the point.

Just hold a DocumentStore somewhere public and inject open sessions to base classes



回答2:

I came up with my own solution using a singleton pattern.

What I did was create a singleton, that exposes a public IDocumentStore property, in the data layer of my application. It utilizes a static constructor which runs upon first request for static property (executed in Application_Start), and in turn instantiates an IDocumentStore object. The initial instance is then then returned for each reference to DocStore.Instance in a base controller and in other layers of my application (like the security layer f.ex.)

public sealed class DocStore
{
    protected static readonly IDocumentStore instance;

    static DocStore()
    {
        // instantiate documentStore
        instance = new DocumentStore { ConnectionStringName = Constants.ConnectionStrings.XXXXX };
        instance.Initialize();

        // instantiate tenants
        try
        {
            instance.DatabaseCommands.EnsureDatabaseExists(Constants.Tenants.XXXXX);
        }
        catch (Exception ex)
        {

            //TODO: catch exception
            throw ex;
        }

        // initialize indexed
        try
        {
            InitializeIndexes(instance);
        }
        catch (Exception ex)
        {

            //TODO: catch exception
            throw ex;
        }
    }

    private DocStore()
    {

    }

    public static IDocumentStore Instance
    {
        get { return instance; }
    }

    private static void InitializeIndexes(IDocumentStore store)
    {
        // builds all indexes defined in XXXXX.Data.dll
        var dataCatalog = new CompositionContainer(new AssemblyCatalog(typeof(DocStore).Assembly));
        IndexCreation.CreateIndexes(dataCatalog,
                                    store.DatabaseCommands.ForDatabase(Constants.Tenants.XXXXX),
                                    store.Conventions);
    }
}