Есть ли хороший порт leveldb для C#?

Я хочу использовать leveldb в моем чистом проекте C#.

Я погуглил версию leveldb для С#, но мне не повезло.

Кто-нибудь может сказать мне, где я могу найти версию leveldb для С#?

Спасибо


person Jack    schedule 15.02.2012    source источник
comment
Здесь обсуждается использование порта leveldb для Windows в вашем проекте C#: groups. google.com/forum/#!topic/leveldb/RxSaQYVDoaI   -  person Brian Snow    schedule 15.02.2012
comment
Теперь есть: original_wrapper, db или клонировать в github: wrapper, сама база данных   -  person ren    schedule 03.10.2014


Ответы (4)


Не то, чтобы я знал, но я использовал его в своем проекте С#. Если вы знакомы с C++, вы можете создать свою собственную оболочку CLI (это не должно вызвать особых затруднений), собрать ее как DLL, а затем загрузить эту DLL в свой проект C#, как и любую другую ссылку на сборку.

Существует порт для leveldb для Windows, и немного сложно добавить его в Visual Studio, но если у вас возникли проблемы, я могу загрузить свое решение Visual Studio 2010 (что составляет 75% успеха) со всей настройкой и готовностью к сборке (кроме оболочки CLI). Я могу разместить его на github или что-то в этом роде, что я в любом случае планирую сделать, но я ускорю это для вас.

Как я уже сказал, я использовал этот подход для своего проекта C#, и он отлично работает. Однако, если у вас действительно высокие требования к производительности, я бы рекомендовал группировать «работу», чтобы уменьшить P/вызывает.

Пример

Обратите внимание, что я не компилировал этот код, а просто публикую его в качестве примера. Ваш заголовочный файл может выглядеть так:

#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());
            }
        }
    }
}

Это должно привести вас в правильном направлении.

Получить пример

OK, 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);

Опять же, я не компилировал и не запускал код, но он должен указать вам правильный путь.

person Kiril    schedule 15.02.2012
comment
спасибо за Ваш ответ!!! То, что мне нужно, очень просто, мне просто нужен простой интерфейс для ввода и получения {key, value}, вот так просто. Я ничего не знаю о P/Invokes или CLI, не могли бы вы рассказать мне больше, КАК ЭТО СДЕЛАТЬ? Спасибо - person Jack; 17.02.2012
comment
Насколько вам комфортно с C++? Если вы уже делали что-то на C++, я приведу пример того, как будет работать оболочка. Если вы раньше ничего не делали на C++, то будет сложно разобраться... - person Kiril; 17.02.2012
comment
Я изучил C, хорош в Objective-C, Java и C#. Я ничего не писал на C++, но если бы вы могли привести мне пример обёртки, думаю, я смогу её понять. По крайней мере, это лучшее начало для меня. Пожалуйста, дайте мне пример и позвольте мне попробовать. - person Jack; 17.02.2012
comment
большое спасибо. Где я могу найти его тогда? - person Jack; 17.02.2012
comment
ОК, я добавил обновление... не скомпилировал его, но оно должно привести вас в правильном направлении. Если вы опубликуете еще один вопрос на SO с конкретными проблемами, с которыми вы столкнулись, пожалуйста, также оставьте комментарий здесь, потому что я с большей вероятностью это замечу. Я также отслеживаю теги leveldb, поэтому продолжайте отмечать вопросы, связанные с leveldb, этим тегом. - person Kiril; 17.02.2012
comment
Действительно спасибо за вашу помощь. Я попробую сейчас. Также благодарю за предполагаемую постоянную помощь мне. - person Jack; 17.02.2012
comment
Обратите внимание, что этот класс будет в вашем проекте Visual Studio C++, который затем будет встроен в DLL с флагом /clr: Свойства конфигурации -> Общие -> Оптимизация всей программы -> выберите Поддержка общеязыковой среды выполнения /clr. Кроме того, он должен быть встроен в DLL: Свойства конфигурации -> Общие -> Целевое расширение: .dll. Затем вы можете взять DLL и загрузить ее в свой проект C# и создать экземпляр LevelDBWrapper, как и любой другой объект C#: LevelDBWrapper db = new LevelDBWrapper("c:/path/to/db"); - person Kiril; 17.02.2012
comment
спасибо за инструкцию по настройке проекта. Могу ли я узнать, куда я должен поместить dll для leveldb? Я не имею в виду dll, которую будет создавать ваш код. Я получил libleveldb.dll из порта leveldb для Windows. - person Jack; 20.02.2012
comment
Вы сами строите порт для Windows? DLL не будет работать легко, поэтому я бы порекомендовал вам создать порт Windows в .lib и статически связать с ним ваш проект C++/CLI. Это делается в конфигурации вашего проекта C++/CLI: укажите путь к папке /leveldb/include в дополнительных каталогах включения, в Linker->General установите зависимости дополнительных библиотек, чтобы они указывали на выходной каталог порта leveldb windows (т.е. где leveldb .lib находится после сборки порта Windows) и добавьте leveldb.lib в Linker->Input->Additional Dependencies. - person Kiril; 20.02.2012
comment
После этого из вашего порта C++/CLI вы можете просто #include "leveldb\db.h" точно так же, как я продемонстрировал в примере кода. - person Kiril; 20.02.2012
comment
Кстати, какой порт Windows вы используете? Это официальный порт Windows или другой? Если это не официальный порт, то мой совет по поводу DLL может быть неприменим. Если это не официальный порт, вам придется поместить DLL в ту же папку, где ваша оболочка C++/CLI для leveldb выводит свою DLL: это означает, что вам нужно будет ссылаться на свой leveldbWrapper. dll в вашем проекте C# и всегда иметь leveldb.dll в папке вашего исполняемого файла C# (иначе ваш исполняемый файл не будет работать). - person Kiril; 20.02.2012
comment
официальный. Ваш код put действительно работает!! Не могли бы вы поделиться со мной своим методом получения? если бы возвращался массив байтов, это было бы лучше всего! - person Jack; 21.02.2012
comment
@Jack Я немного опубликую пример Get. Примечание: убедитесь, что вы удалили options.block_cache в деструкторе LevelDBWrapper, иначе вы получите утечку памяти. - person Kiril; 21.02.2012
comment
Спасибо за код!!! но как я могу преобразовать unsigned char* в byte[]? - person Jack; 23.02.2012
comment
@ Джек unsigned char* преобразуется в byte[] в последнем фрагменте кода в моем ответе (этот код будет использоваться в вашем приложении C #). Я также нашел более элегантное решение, которое могло бы сделать его гораздо более бесшовным, и оно может оставаться непосредственно в вашей оболочке C++/CLI вокруг leveldb: stackoverflow.com/questions/6050990/ - person Kiril; 23.02.2012
comment
Спасибо за ваш код снова и снова. Я попробовал ваш последний фрагмент кода, который переводит char в byte[]. Но я получаю сообщение об ошибке: Указатели ошибок и буферы фиксированного размера могут использоваться только в небезопасном контексте. Я не могу использовать байтовый буфер. Я могу использовать буфер IntPtr, но тогда будет та же ошибка для метода db.Get. - person Jack; 07.03.2012
comment
Джек, я бы порекомендовал вам опубликовать еще один вопрос об ошибке и связать его здесь, чтобы я мог его посмотреть. Небезопасный контекст означает, что вы должны использовать unsafe ключевое слово. - person Kiril; 07.03.2012
comment
могу я вам написать по электронной почте или что-то в этом роде? - person Jack; 07.03.2012
comment
@Jack, отправьте мне электронное письмо по адресу [email protected], затем я перешлю его на свой личный адрес электронной почты, и мы начнем оттуда. Mailinator автоматически удалит вашу электронную почту через 2-3 часа. - person Kiril; 07.03.2012
comment
@ Джек, я получил твое электронное письмо и отправил тебе электронное письмо для подтверждения, дай мне знать, если ты его получил. Я также добавил вас в свой G+. - person Kiril; 07.03.2012

Насколько я вижу, вы также можете использовать LMDB (база данных с отображением памяти Lightning, http://symas.com/mdb/ ), который очень похож на LevelDB и также поставляется с оболочкой .Net (https://github.com/ilyalukyanov/Lightning.NET) Не знаю, насколько хорошо это работает, но еще не использовал...

person Jacob    schedule 10.10.2013

Я не использовал его, но вижу leveldb-sharp.

person Matt Cruikshank    schedule 09.04.2013
comment
похоже, что сайт meebey сейчас не работает, вы можете использовать репозиторий git, расположенный по адресу github.com/meebey/leveldb -резкий - person Behrooz; 21.12.2014
comment
Я использовал его (и создал свою собственную вилку), это здорово - person Behrooz; 21.12.2014

Я не знаю, что здесь происходит, но этот проект есть на официальной странице Microsoft Rx-Js здесь.

person xanadont    schedule 25.10.2012
comment
Фактический репозиторий находится здесь: github.com/Reactive-Extensions/LevelDB - person Florian Fida; 11.09.2016