C#有一个好的leveldb端口吗
本文关键字:leveldb 有一个 | 更新日期: 2023-09-27 18:24:35
我希望在我的纯C#项目中使用leveldb。
我在谷歌上搜索了一个C#版本的leveldb,但没有幸运。
有人能告诉我在哪里可以找到leveldb的C#版本吗?
感谢
我不知道,但我一直在C#项目中使用它。如果您熟悉C++,那么您可以制作自己的CLI包装器(应该不会有那么多麻烦),将其构建为DLL,然后可以像其他程序集引用一样在C#项目中加载该DLL。
leveldb有一个windows端口,将其导入Visual Studio有点棘手,但如果您遇到问题,我可以上传我的Visual Studio 2010解决方案(这是75%的工作),并准备好构建整个解决方案(CLI包装器除外)。我可以把它放在github或其他什么东西上,我实际上无论如何都在计划这样做,但我会为你加快它的速度。
正如我所说的,我一直在用这种方法进行我的C#项目,而且效果很好。但是,如果您有非常高的性能要求,那么我建议批量处理"工作",以减少P/Invoke。
示例
请注意,我还没有编译这段代码,但我只是把它作为一个例子发布。您的头文件可能如下所示:
#pragma once
#include <exception>
#include "leveldb'db.h"
using namespace System::Runtime::InteropServices;
// Create the namespace
namespace LevelDBWrapperNS
{
// Note that size_t changes depending on the target platform of your build:
// for 32-bit builds, size_t is a 32-bit unsigned integer.
// for 64-bit builds, size_t is a 64-bit unsigned integer.
// There is no size_t equivalent in C#, but there are ways to
// mimic the same behavior. Alternately, you can change the
// size_t to unsigned long for 32-bit builds or unsigned long long (64-bit)
// Declare the leveldb wrapper
public ref class LevelDBWrapper
{
private:
leveldb::DB* _db;
public:
LevelDBWrapper(const std::string dataDirectory);
~LevelDBWrapper();
// A get method which given a key, puts data in the value array
// and sets the valueSize according to the size of the data it
// allocated. Note: you will have to deallocate the data in C#
void Get(const char* key, const size_t keySize, char* value, size_t &valueSize);
// A put method which takes in strings instead of char*
bool Put(const std::string key, const std::string value);
// A put method which takes in char* pointers
bool Put(const char* key, const size_t keySize, const char* value, const size_t valueSize);
// A delete method
bool Delete(const char* key, const size_t keySize);
private:
void Open(const char* dataDirectory);
};
}
您的cpp文件将沿着以下行:
#include "LevelDBWrapper.h"
// Use the same namespace as the header
namespace LevelDBWrapperNS
{
LevelDBWrapper::LevelDBWrapper(const std::string dataDirectory)
{
Open(dataDirectory.c_str());
}
LevelDBWrapper::~LevelDBWrapper()
{
if(_db!=NULL)
{
delete _db;
_db= NULL;
}
// NOTE: don't forget to delete the block cache too!!!
/*if(options.block_cache != NULL)
{
delete options.block_cache;
options.block_cache = NULL;
}*/
}
bool LevelDBWrapper::Put(const char* key, const size_t keySize, const char* value, const size_t valueSize)
{
leveldb::Slice sKey(key, keySize);
leveldb::Slice sValue(value, valueSize);
return _db->Put(leveldb::WriteOptions(), sKey, sValue).ok();
}
void LevelDBWrapper::Open(const char* dataDirectory)
{
leveldb::Options options;
// Create a database environment. This will enable caching between
// separate calls (and improve performance). This also enables
// the db_stat.exe command which allows cache tuning. Open
// transactional environment leveldb::Options options;
options.create_if_missing = true;
// Open the database if it exists
options.error_if_exists = false;
// 64 Mb read cache
options.block_cache = leveldb::NewLRUCache(64 * 1024 * 1024);
// Writes will be flushed every 32 Mb
options.write_buffer_size = 32 * 1024 * 1024;
// If you do a lot of bulk operations it may be good to increase the
// block size to a 64k block size. A power of 2 block size also
// also improves the compression rate when using Snappy.
options.block_size = 64 * 1024;
options.max_open_files = 500;
options.compression = leveldb::kNoCompression;
_db = NULL;
// Open the database
leveldb::Status status = leveldb::DB::Open(options, dataDirectory, &_db);
// Check if there was a failure
if(!status.ok())
{
// The database failed to open!
if(status.ToString().find("partial record without end")!=std::string::npos)
{
// Attempting to recover the database...
status = leveldb::RepairDB(dataDirectory, options);
if(status.ok())
{
// Successfully recovered the database! Attempting to reopen...
status = leveldb::DB::Open( options, dataDirectory, &_db);
}
else
{
// Failed to recover the database!
}
}
// Throw an exception if the failure was unrecoverable!
if(!status.ok())
{
throw std::runtime_error(std::string("Unable to open: ") + std::string(dataDirectory) +
std::string(" ") + status.ToString());
}
}
}
}
这应该会让你朝着正确的方向前进。
获取示例
好的,Get看起来像这样:
// Returns a buffer containing the data and sets the bufferLen.
// The user must specify the key and the length of the key so a slice
// can be constructed and sent to leveldb.
const unsigned char* Get(const char* key, const size_t keyLength, [Out]size_t %bufferLen);
来源如下:
const unsigned char* LevelDBWrapper::Get(const char* key, const size_t keyLength, [Out]size_t %bufferLen)
{
unsigned char* buffer = NULL;
std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), Slice(key, keyLength), &value);
if(s.ok())
{
// we found the key, so set the buffer length
bufferLen = value.size();
// initialize the buffer
buffer = new unsigned char[bufferLen];
// set the buffer
memset(buffer, 0, bufferLen);
// copy the data
memcpy(memcpy((void*)(buffer), value.c_str(), bufferLen);
}
else
{
// The buffer length is 0 because a key was not found
bufferLen = 0;
}
return buffer;
}
请注意,不同的数据可能有不同的编码,所以我觉得在非托管代码和托管代码之间传递数据最安全的方法是使用指针和UnmanagedMemoryStream
。以下是如何在C#中获得与密钥相关的数据:
UInt32 bufferLen = 0;
byte* buffer = dbInstance.Get(key, keyLength, out bufferLen);
UnmanagedMemoryStream ums = new UnmanagedMemoryStream(buffer, (Int32)bufferLen, (Int32)bufferLen, FileAccess.Read);
// Create a byte array to hold data from unmanaged memory.
byte[] data = new byte [bufferLen];
// Read from unmanaged memory to the byte array.
readStream.Read(data , 0, bufferLen);
// Don't forget to free the block of unmanaged memory!!!
Marshal.FreeHGlobal(buffer);
同样,我还没有编译或运行代码,但它应该会让你走上正轨。
据我所见,您也可以使用LMDB(闪电存储器映射数据库,http://symas.com/mdb/)它似乎与LevelDB非常相似,还附带了.Net包装器(https://github.com/ilyalukyanov/Lightning.NET)不知道它有多好用,还没有用过。。。
我没有用过它,但我看到了leveldb sharp。
我不知道这里的故事,但在微软的官方Rx Js页面上有这个项目。