-
Notifications
You must be signed in to change notification settings - Fork 11
Type Handlers
ℹ️ Not sure if you need a type handler? Learn about the different ways to extend mapping in Nevermore.
Type Handlers are used to help Nevermore when mapping data between SQL result sets/parameters and CLR types.
When your data is stored in the
JSON
blob, it's serialized withNewtonsoft.Json
, so controlling how it's serialized or mapping it to different types is easy - just write aJsonConverter
and register it with theJsonSerializerSettings
onRelationalStoreConfiguration
. You only need a Type Handler for data that's stored in a column, or data you're using as parameters to queries.
Nevermore natively knows how to deal with most standard types: ints and strings, datetimes, and even enums (they are stored as strings).
For other types, you can register an ITypeHandler
. For example, suppose we want to store a Uri
. Here's a type handler:
public class UriTypeHandler : ITypeHandler
{
public bool CanConvert(Type objectType)
{
return objectType == typeof(Uri);
}
public object ReadDatabase(DbDataReader reader, int columnIndex)
{
if (reader.IsDBNull(columnIndex))
return default(Uri);
var text = reader.GetString(columnIndex);
return new Uri(text);
}
public void WriteDatabase(DbParameter parameter, object value)
{
parameter.DbType = DbType.String;
parameter.Value = ((Uri) value)?.ToString();
}
}
When reading, you're given the DbDataReader
, and the index of the column that you should read from. This allows you to use whichever method on DbDataReader
makes the most sense to you.
When writing, you're given the DbParameter
. You should set the value on the DbParameter
, and also set the DbType
.
You can optionally add a Priority
to your type handler. This allows you to provide handlers for specific types, with a fallback for unmapped types. For example, you might add support for a handler that knows how to store all IFormattable
types, but set it with a higher priority number so it runs last (Priority 0 means first, Priority 1 second, etc.)
Your TypeHandler
should then be registered with the store configuration:
store.Configuration.TypeHandlerRegistry.Register(new UriTypeHandler());
ℹ️ Tip:
If you want to control both JSON and SQL reading/writing, your type handler can also be a
JsonConverter
!
Your type handler will be called everywhere that we need to convert to/from the CLR types you support, and SQL types. Essentially, any time we find ourselves with a DbDataReader
trying to figure out how to map a column to a type (e.g., for a property, a contructor parameter, etc.) we will call your type handler.
That includes:
- When reading documents with
Load
,TableQuery
, and so on as we translate result set columns to properties - When writing documents with
Insert
,Update
, etc. as we translate property values to column parameters - When reading tuples, plain old classes, etc. using
Stream
CanConvert
is typically called once, and cached. Nevermore remembers which type handler to use for each type, to speed up queries.
You cannot supply type handlers for basic types like string
or DateTime
. These fields are very common, and Nevermore builds expression trees that know how to deal with them as efficiently as possible.
With a type handler, you return an instance of the type. Nevermore then uses IPropertyReaderWriter
when setting the value your type handler returns to set it on the property.
In pseudocode:
propertyReaderWriter.Write(documentInstance, typeHandler.ReadDatabase(reader, index));
If you want to control how the value is set on the target object property, you need to use an IPropertyReaderWriter
and set it as part of the DocumentMap
. Otherwise, Nevermore will simply call the property setter and pass the value your TypeHandler
returns.
Overview
Getting started
Extensibility
Misc