Native interop
RocksDB-Sharp is a managed wrapper around the C ABI exposed by RocksDB (include/rocksdb/c.h). This page covers how the native binary is located and loaded at runtime, and how to call into the raw C API when you need a function that the high-level binding doesn't expose yet.
How the native binary is loaded
The NuGet package ships precompiled binaries inside runtimes/<rid>/native/:
runtimes/
├── win-x64/native/rocksdb.dll
├── linux-x64/native/librocksdb.so
├── linux-arm64/native/librocksdb.so
├── osx-x64/native/librocksdb.dylib
└── osx-arm64/native/librocksdb.dylib
At runtime, the AutoNativeImport helper (declared in Native.Load.cs / AutoNativeImport.cs) picks the right file for the current RuntimeInformation.RuntimeIdentifier and loads it via NativeLibrary.Load (or the equivalent on older runtimes). You don't need to do anything to make this work — the first call into Native.Instance triggers the load.
If you publish your app:
- Framework-dependent (
dotnet publish): theruntimesfolder is copied next to your binary. Works out of the box. - Self-contained (
dotnet publish -r <rid>): the right RID-specific binary is included in the output. Works out of the box. - Single-file (
PublishSingleFile=true): add<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>to your csproj so the native binary is extracted at startup.
Custom load locations
If you need to load the native binary from a non-standard place — embedded as a resource, on a special disk, etc. — you can take ownership of the load before any call into Native.Instance. See Native.Load.cs for the helper hooks.
Calling the raw C API
Every function in rocksdb_c.h is declared on Native (see Native.cs). The mid-level layer (Native.Wrap.cs / Native.Marshaled.cs) wraps most of them in friendlier signatures.
using RocksDbSharp;
using var db = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), path);
// Mid-level — use the wrapped function:
ulong seq = Native.Instance.rocksdb_get_latest_sequence_number(db.Handle);
For low-level calls that take an errptr, you handle marshalling yourself:
unsafe
{
IntPtr err;
UIntPtr keylen = (UIntPtr)key.Length;
IntPtr valuePtr = Native.Instance.rocksdb_get(
db.Handle,
ReadOptions.Default.Handle,
key,
keylen,
out UIntPtr valueLength,
out err);
if (err != IntPtr.Zero)
{
string message = Marshal.PtrToStringAnsi(err);
Native.Instance.rocksdb_free(err);
throw new RocksDbException(message);
}
// Use the value, then free it:
Native.Instance.rocksdb_free(valuePtr);
}
The high-level db.Get(...) does exactly this for you.
Mixing layers
Every high-level type (RocksDb, Iterator, WriteBatch, Snapshot, …) exposes its underlying IntPtr Handle. You can hand those into a low-level call and mix freely:
using var db = RocksDb.Open(options, path);
using var batch = new WriteBatch();
batch.Put("k", "v");
// Drop down for a per-write option that isn't on WriteOptions yet:
var wo = Native.Instance.rocksdb_writeoptions_create();
try
{
Native.Instance.rocksdb_write(db.Handle, wo, batch.Handle);
}
finally
{
Native.Instance.rocksdb_writeoptions_destroy(wo);
}
Handle ownership
The high-level types call *_destroy in their Dispose. Don't destroy their handles yourself, and don't Dispose them while you're still using their Handle via the low-level API.
Exception model
The high-level API throws:
RocksDbException— wraps RocksDB's own error messages (theerrptrstrings).RocksDbSharpException— binding-side errors (e.g. attempting CF operations on a DB opened without column families).DllNotFoundException— the native binary couldn't be located at runtime. See Installation troubleshooting.
Building from source
If you want to build the native binary yourself (e.g. for a custom architecture, or to apply a patch), see build-native/ and build-codegen/ in the main repository. The repository's Azure Pipelines build script rebuilds the native binaries on every official RocksDB release.