Type-Specific Identifier Generation

In the previous article, Global Identifier generation conventions were introduced. Any customization made by using those conventions changes the behavior for all stored entities. Now we will show how to override the default ID generation in a more granular way, for particular types of entities.

To override default document identifier generation algorithms, you can register custom conventions per an entity type. You can include your own identifier generation logic.

RegisterAsyncIdConvention

DocumentConventions RegisterAsyncIdConvention<TEntity>(Func<string, TEntity, Task<string>> func);
Parameters
func Func<string, TEntity, Task<string>> Identifier generation function that supplies a result in async way for given database name (string) and entity object (TEntity).
Return Value
DocumentConventions Current DocumentConventions instance.

Note

This method applied to both synchronous and asynchronous operations

Database name parameter

The database name parameter is passed to the register convention methods to allow users to make Id generation decision per database.

Example

Let's say that you want to use semantic identifiers for Employee objects. Instead of employee/[identity] you want to have identifiers like employees/[lastName]/[firstName] (for the sake of simplicity, let us not consider the uniqueness of such identifiers). What you need to do is to create the convention that will combine the employee prefix, LastName and FirstName properties of an employee.

store.Conventions.RegisterAsyncIdConvention<Employee>(
    (dbname, employee) =>
        Task.FromResult(string.Format("employees/{0}/{1}", employee.LastName, employee.FirstName)));

Now, when you store a new entity:

using (var session = store.OpenSession())
{
    session.Store(new Employee
    {
        FirstName = "James",
        LastName = "Bond"
    });

    session.SaveChanges();
}

the client will associate the employees/Bond/James identifier with it.

Inheritance

Registered conventions are inheritance-aware so all types that can be assigned from registered type will fall into that convention according to inheritance-hierarchy tree.

Example

If we create a new class EmployeeManager that will derive from our Employee class and keep the convention registered in the last example, both types will use the following:

using (var session = store.OpenSession())
{
    session.Store(new Employee // employees/Smith/Adam
    {
        FirstName = "Adam",
        LastName = "Smith"
    });

    session.Store(new EmployeeManager // employees/Jones/David
    {
        FirstName = "David",
        LastName = "Jones"
    });

    session.SaveChanges();
}

If we register two conventions, one for Employee and the second for EmployeeManager then they will be picked for their specific types.

store.Conventions.RegisterAsyncIdConvention<Employee>(
    (dbname, employee) =>
        Task.FromResult(string.Format("employees/{0}/{1}", employee.LastName, employee.FirstName)));

store.Conventions.RegisterAsyncIdConvention<EmployeeManager>(
    (dbname, employee) =>
        Task.FromResult(string.Format("managers/{0}/{1}", employee.LastName, employee.FirstName)));

using (var session = store.OpenSession())
{
    session.Store(new Employee // employees/Smith/Adam
    {
        FirstName = "Adam",
        LastName = "Smith"
    });

    session.Store(new EmployeeManager // managers/Jones/David
    {
        FirstName = "David",
        LastName = "Jones"
    });

    session.SaveChanges();
}