Question

In my project, I have the following PageCache entity, which is being stored in RavenDB:

public class PageCache
{
    private readonly IHtmlDocumentHelper htmlDocumentHelper;

    public string Id { get; set; }
    public string Url { get; set; }

    public PageCache(IHtmlDocumentHelper htmlDocumentHelper, string url)
    {
        this.htmlDocumentHelper = htmlDocumentHelper;
        this.Url = url;
    }
}

I am using Castle Windsor to inject the IHtmlDocumentHelper implementation at runtime. This member is used in methods defined inside the PageCache class, which I stripped from the above snippet for the sake of simplicity.

When I create a PageCache object using the constructor, everything works fine. But elsewhere in my code, I load PageCache objects back from RavenDB:

public PageCache GetByUrl(string url)
{
    using (var session = documentStore.OpenSession())
    {
        return session.Query<PageCache>()
                      .Where(x => x.Url == url)
                      .FirstOrDefault();
    }
}

My issue is that the objects I get back from RavenDB don't have the htmlDocumentHelper member set, rendering the PageCache methods that depend on it unuseable.

In other words: when I load objects back from documents stored in RavenDB, it won't use my constructor to build the objects, thus not initializing the private members through constructor injection.

Am I doing something wrong here? How would you solve such an issue?


I ended up using the solution proposed by Ayende below. The circular dependency issue I mentioned in the comments only appeared when I registered the DocumentStore in Windsor with UsingFactoryMethod(). The issue strangely disappeared when I used Windsor's DependsOn() and OnCreate() to configure and initialize the DocumentStore directly inside the Register().

My container is now being initialized as follows:

WindsorContainer container = new WindsorContainer();

container.Register(
    // Register other classes, such as repositories and services.
    // Stripped for the sake of clarity.
    // ...

    // Register the CustomJsonConverter:
    Component.For<CustomJsonConverter>().ImplementedBy<CustomJsonConverter>(),

    // The following approach resulted in an exception related to the circular
    // dependencies issue:
    Component.For<IDocumentStore>().UsingFactoryMethod(() =>
        Application.InitializeDatabase(container.Resolve<CustomJsonConverter>()))

    // Oddly enough, the following approach worked just fine:
    Component.For<IDocumentStore>().ImplementedBy<DocumentStore>()
        .DependsOn(new { Url = @"http://localhost:8080" })
        .OnCreate(new Action<IDocumentStore>(store =>
            store.Conventions.CustomizeJsonSerializer = serializer =>
                serializer.Converters.Add(container.Resolve<CustomJsonConverter>())))
        .OnCreate(new Action<IDocumentStore>(store =>
            store.Initialize()))
        .OnDestroy(new Action<IDocumentStore>(store =>
            store.Dispose()))
    );

Although it seems to be working fine, I feel odd having to call container.Resolve<CustomJsonConverter>() from inside the container.Register() method.

Is this a legal approach to register the dependencies?

Was it helpful?

Solution

Christian, We can't use your ctor, we don't know what to put in there.

Instead, you can use this technique to tell RavenDB how to create your objects: http://james.newtonking.com/projects/json/help/CustomCreationConverter.html

Then you can wire this in using documentStore.Conventison.CustomizeSerializer

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top