问题
I have a class that I want to use for a document key in RavenDB:
public class DocumentKey
{
public string Namespace { get; set; }
public string Id { get; set; }
}
I've also implemented the ITypeConverter
(not the .NET one, the RavenDB-specific one) interface to convert from the reference type to a string (because in the database, the keys are all really just strings).
Finally, I've added the implementation of ITypeConverter
to the IDocumentStore
implementation through the List<ITypeConverter>
exposed through the Conventions.IdentityProviders
property.
However, the signature on the LoadAsync<T>
overloads on the IAsyncDocumentSession
implementation look like this (removed the signatures which take multiple ids for brevity. Also, the same as Load
on the IDocumentSession
interface):
LoadAsync<T>(string id);
LoadAsync<T>(ValueType id);
I really don't want to use value types for my keys for the following reasons:
- I have an abstraction which doesn't have a constraint on the type of the key. Creating separate structures to mirror this just to have value types are very inconvenient.
- I don't have complete control over the type by being restricted to a value type. The value type has a default constructor which defaults the values in a way that I don't want to have to deal with everywhere else in my code.
How can I use a reference type as a document key in RavenDB?
回答1:
Because all document identifiers are stored ultimately stored as strings in RavenDB, the key is using the overload that takes a string:
LoadAsync<T>(string id);
From the IAsyncDocumentSession
interface, you can use the Conventions
(exposed by Advanced.DocumentStore.Conventions
), specifically the FindFullDocumentKeyFromNonStringIdentifier
delegate, which has the following signature:
string FindFullDocumentKeyFromNonStringIdentifier(
object id, Type type, bool allowNull);
Here is what the parameters do:
id
- This is the object that is being used as the identifier for the document. In the above example, it would be theDocumentKey
instance. Since this is typed as anobject
(and not a ValueType), a reference type will be accepted here.type
- The Type instance that represents the type of the item that theid
belongs to. When callingLoadAsync<T>
, this istypeof(T)
.allowNull
- This is passed as theallowNull
parameter in the implementation ofITypeConverter.ConvertFrom
that is added to theIdentityProviders
exposed throughConventions
.
This can all be wrapped up in an extension method on IAsyncDocumentSession
(or modified for IDocumentSession
if you want) that is strongly typed, like so:
static Task<T> LoadAsync<T, TId>(this IAsyncDocumentSession session, TId id)
{
// Validate parameters.
if (session == null) throw new ArgumentNullException("session");
// Check if the id is null.
if ((object) id == null) throw new ArgumentNullException("id");
// Get the string id.
string stringId = session.Advanced.DocumentStore.Conventions.
FindFullDocumentKeyFromNonStringIdentifier(id, typeof(T), true);
// Load using the string id.
return session.LoadAsync<T>(stringId);
}
Note that the if ((object) id == null) comparison can have a performance impact in this scenario.
来源:https://stackoverflow.com/questions/14005053/how-to-use-a-reference-type-as-a-key-in-a-document-in-ravendb