Search code examples
c#marten

Calling AddSubClassHierarchy() in constructor causes no results in Query<T>


When I read the Maven docs regarding Document Hierarchy, I thought that calling AddSubClassHierarchy() where the generic argument is the baseclass, would automatically map all the subtypes. Docs reads as:

// Alternatively, you can use the following:
// _.Schema.For<ISmurf>().AddSubClassHierarchy();
// this, however, will use the assembly
// of type ISmurf to get all its' subclasses/implementations. 
// In projects with many types, this approach will be undvisable.

That sounded to me that Reflection would be used, so I don't have to specify every single subclass. My hierarchy is CoreObject-->Actor-->Customer.

But when I do add the AddSubClassHierarchy, the Query<T> fails and returns nothing:

        store = DocumentStore.For(_ =>
        {
            // Marten will create any new objects that are missing,
            // attempt to update tables if it can, but drop and replace
            // tables that it cannot patch. 
            _.Connection("host=localhost;database=marten;password=root;username=postgres");
            _.Schema.For<Customer>().Index(x => x.PopulationRegistryNumber);
            _.Schema.For<CoreObject>().AddSubClassHierarchy(); // CoreObject is base class to Actor, that is base class for Customer
            _.AutoCreateSchemaObjects = AutoCreate.CreateOrUpdate;
        });

and then when I try:

        using (var session = store.OpenSession())
        {
            list = session.Query<Actor>().Where(a => a.Username == "asdasd").ToList();
        }

it fails and returns no items. All other Query<T> fails too, like this one:

        using (var session = store.LightweightSession())
        {
            List<Customer> list = session.Query<Customer>().ToList();
            return list;
        }

List will contain 0 elements. If I remove the _.Schema.For<CoreObject>().AddSubClassHierarchy();, I get results from the session.Query<Customer>().

Edit

I tried to do what Jeremy said; defined the hierarchy manually, like this:

public MartenDbHandler()
{
    StoreOptions so = new StoreOptions();
    // here it is: CoreObject-->Actor-->Customer
    so.Schema.For<CoreObject>().AddSubClassHierarchy(typeof(Actor), typeof(Customer));
    so.Connection("host=localhost;database=marten;password=root;username=postgres");
    so.AutoCreateSchemaObjects = AutoCreate.CreateOrUpdate;
    SetTableMeta(so);
    store = new DocumentStore(so);
}

at first, there was no change. I needed to clear and insert the objects again. What I then realize is that all objects end up in one table: mt_doc_coreobject

And when I then search, it works:

    List<Actor> list3 = martenDbHandler.Select<Actor>(c => c.Username == "YS3M");
    Console.WriteLine($"SELECT Actor {list3.Count}"); // = 1

But I realize of course that placing all objects in one table is an easy fix, but I think I read a number of other caveats, that I'm not sure if I interpret correctly:

There's a couple things to be aware of with type hierarchies:

  • A document type that is either abstract or an interface is automatically ssumed to be a hierarchy
  • If you want to use a concrete type as the base class for a hierarchy, you will need to explicitly configure that by adding the subclasses as shown above
  • At this point, you can only specify "Searchable" fields on the top, base type
  • The subclass document types must be convertable to the top level type. As of right now, Marten does not support "structural typing", but may in the future
  • Internally, the subclass type documents are also stored as the parent type in the Identity Map mechanics.
  • Many, many hours of banging my head on my desk were required to add this feature.

The worst part seemed to be the "At this point, you can only specify "Searchable" fields on the top, base type", but I did a Query<> on the Username, which does not exist in base type CoreObject (it exists in Actor). So I'm not sure what that means?

Update

What Marten must mean with "searchable" is "indexable"? Because I cannot longer create an index on a property that exists in the subclass, it seems.


Solution

  • So, I think I have the answer:

    Adding the following line, will assume that all subclasses are persisted in a top-level table, so all Actor and Customer will all be persisted in the mt_doc_coreobject table, instead of their own specific tables.

    So, if you add the line below, Marten will assume this is the case, but if the objets where already persisted in their own tables (before adding the line below), no results will be found.

    So, this line:

    so.Schema.For<CoreObject>().AddSubClassHierarchy();
    

    will then require that you re-insert the objects, and they then appear in mt_doc_coreobject.