Alexey Suvorov dev blog

Мой разработческий блог

Archive for the ‘Uncategorized’ Category

Из TFS в git + удаляем dll и другие бинарники

with one comment

Я заметил что всегда пропускаю первую часть статьи, потому что там автор рассуждает о том, как он докатился до такой жизни. Но если я открыл статью с определённым заголовком — значит это для меня это уже не важно, я уже ищу решение проблемы а не мотивацию, так что к командам.

Понадобится:
https://github.com/git-tfs/git-tfs
Вот тут берём самую распоследнюю версию https://github.com/git-tfs/git-tfs/releases
Делаем так, чтобы в переменной PATH был путь к директории в которую распакован архив. Проверить можно тем, что команда в консоли
#>git tfs
должна выдать справку.
Команда на

X:\SomeTmpFolder1>git tfs clone http://tfs:8080/tfs/DefaultCollection "$/YourProject
/YourBranch" . --with-branches

Если проект имеет TFS бренчи, например на каждый релиз, то команда clone запускается для самого последнего (DEV или default или main или на что там фантазии хватило) бренча, все остальные бренчи, которые отходили от него будут перенесены git бренчами, для этого указывается —with-branches. У нас на проекте всё собралось само, на соседнем неправильно определились бренчи, так что как повезёт.
Сидим занимаемся своими делами несколько часов (в нашем случае было 7-8), если всё хорошо, то открываем получившийся репозиторий в любом клиенте и проверяем, что всё правильно перенеслось.

Если случилось так, что мы годами складывали в TFS бинарники и нугет пакеты и мы не боимся с ними расстаться (что очень рекомендуется), то выполняем несколько следующих шагов.
Переходим в папку где находятся только что созданный git репозитарий и выполняем
git filter-branch -f -d x:\tmp --index-filter "git rm --cached -f --ignore-unmatch '*.dll'; git rm --cached -f --ignore-unmatch '*.nupkg';" --tag-name-filter cat -- --all
x:\tmp можно так и оставить, главное, чтобы эта папка не существовала
Можно включить другие файлы по маске, думаю очевидно как.
Ждём ещё несколько часов (3-4 в нашем случае), после запускаем

git reflog expire --verbose --expire=0 --all
git gc --prune=0

Получили чистый от бинарников репозитарий, который можно пушать на сервер.

Информацию по удалению брал отсюда: http://blog.gbacon.com/2009/08/git-shrinking-subversion-import.html

Written by alexeysuvorov

16.04.2015 at 5:38 пп

Опубликовано в Uncategorized

Minification (proguard) и YouTube API

leave a comment »

При использовании продуктов google складывается такое ощущение, что у них куча разрозненных команд, каждая из которых делает что вздумается, а как потом заработает так и заработает. Одна команда написала API для взаимодействия с Youtube, в это время другая команда написала минификатор, которым google рекомендует пользоваться. В итоге в продакшене вдруг выясняется что приложение не может получить информацию из сервиса, получая от него 400 ошибку (в моём случае 400 из-за отсутствия part).

Настройки для proguard которые помогли в моём случае:

-keepattributes *Annotation* # Needed by google-api-client
-keepattributes Signature # Needed by google-api-client


# Needed by google-api-client to keep generic types and @Key annotations accessed via reflection
-keepclassmembers class * {
@com.google.api.client.util.Key ;
}


# Needed by Guava (google-api-client)
-dontwarn sun.misc.Unsafe

Взял тут http://stackoverflow.com/questions/5156994/android-youtube-gdata-api-not-working-after-running-proguard

Written by alexeysuvorov

29.01.2015 at 5:10 пп

Опубликовано в Uncategorized

Tagged with ,

Roslyn quoter

leave a comment »

Просто оставлю это здесь для тех кто работает с новым компайлером так же плотно как и я
http://roslynquoter.azurewebsites.net/
Вместо установки CTP новой студии и тысячи слов.

Written by alexeysuvorov

23.01.2015 at 2:54 пп

Опубликовано в Uncategorized

Tagged with ,

Итоги года

leave a comment »

photo

Я живу в полной уверенности что рассуждения о жизни в техническом блоге никто не читает, но т.к. с моей частотой публикаций этот блог вообще врятли кто-то читает, то я позволю себе немного «воды».

2013-ый у меня прошёл почти полностью в стеке apple, 2014-ый целиком на андройде. Не то чтобы это был осознанный выбор, просто так сложились обстоятельства. Вышел очень неудачный айфон (IMHO естественно) и ещё более неудачная и бесполезная, для людей без айфона, макось. В это же время андройд более менее определился с концепцией своего развития и выпустил на рынок cardboard. Я считаю что 2015-ый год пройдёт в ключе VR и есть шанс успеть сделать что-то что не просто перекладывает данные из одного места в другое грея атмосферу и генерирую красивые отчётики (на самом деле я люблю энтерпрайз, а вот он меня не очень).

Итого за 2 последних года сменив 2 технологических стека я могу сказать что хорошо там, где нас нет. В дотнете одна беда, но зато большая — это Майкрософт с его постоянным непостоянством. У iOS их две — это жлобский apple и их дурной objective-c. Мои упражнения со swift-ом закончились на третий день, я зафайлил баг в эпловом багтрекере на совершенно базовый сценарий интеропа с cocoa и забил. А вот у андройда все хорошо. Правда это пока я пишу и запускают свои приложения на nexus, потому что на моём, пару лет назад случайно купленом, «флагмане» от известного производителя jiayu не работает решительно ничего. Похоже у них там какой-то свой openGl. Андройд студия которую так ругали в целом работает, ну может нужно перезапустить раз в пару дней. В общем и целом если бы не многословная java — можно было бы неплохо жить.

Собственно время покажет что принесёт наибольшие плоды, я предпочитаю смотреть на технологии широко, но если есть выбор, то делать на том, что лучше всего знаю.

Written by alexeysuvorov

10.01.2015 at 4:51 пп

Опубликовано в Uncategorized

Windows 2012 Datacenter активация вирутальных машин

leave a comment »

Версия Datacenter позволяет развёртывать неограниченное количество виртуальных машин, но если это Windows то он захочет активироваться. Просто оставлю здесь эту ссылку и эти ключи, потому что их поиск был непростым и что-то подсказывает, что они мне ещё пригодятся.

http://technet.microsoft.com/en-us/library/dn303421.aspx

Edition AVMA key
Datacenter Y4TGP-NPTV9-HTC2H-7MGQ3-DV4TW
Standard DBGBW-NPF86-BJVTX-K3WKJ-MTB6V
Essentials K2XGM-NMBT3-2R6Q8-WF2FK-P36R2

 

Upd1:

И это тоже пожалуй тут оставлю:

Решение при ошибке Windows Update, если машина не имеет доступа в интернет

http://blog.ronnypot.nl/?p=310

Written by alexeysuvorov

18.05.2014 at 9:00 пп

Детали реализации стека часть вторая

leave a comment »

Это перевод второй части статьи «Детали реализации стека». Оригинал можно найти по ссылке http://blogs.msdn.com/b/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx.
Перевод первой части.

Несколько человек спрашивали меня, в контексте моего предыдущего поста о значимых типах, почему же всё-таки значимые типы располагаются на стеке, а ссылочные нет.

Если коротко, то «потому что могут». И т.к. стек «дёшев» мы располагаем их на стеке, когда только это возможно.

Длинный ответ он … длинный.

Для начала в общих чертах определим, что мы называем кучей, а что стеком. Сначала куча.

CLR куча* — это чудо инженерной мысли с огромным количеством деталей. Описание далее это не то, как на самом деле куча работает, но его достаточно, чтобы получить общее представление.

Идея в том, что существуют большие блоки памяти, выделенные под экземпляры ссылочных типов. Эти блоки памяти могут иметь «дырки» потому что некоторые блоки памяти заняты «живыми» объектами, а некоторые готовы для использования под новые объекты. В идеальном случае нам бы хотелось, чтобы вся занятая память располагалась в одном месте непрерывным блоком, а всё остальное адресное пространство было бы свободно.

В такой ситуации при выделении новой порции памяти мы бы перемещали указатель на верхнюю границу занятой память вверх на необходимую величину и «отъедали» бы часть ранее свободной памяти. Эта только что зарезервированная память использовалась бы для вновь созданного объекта. Такая операция чрезвычайно дешева: просто передвигаем указатель и заполняем нулями кусок памяти если это необходимо.

Если же у нас есть «дырки» то мы должны хранить «свободный лист» — список свободных секций. Тогда мы можем искать в этом списке свободное место подходящего размера и заполнять его. Эта операция немного дороже потому что производится поиск по списку. Хотелось бы избегать такой ситуации потому что она не оптимальна.

Сборка мусора происходит в 3 этапа: разметка, сборка и сжатие**. В фазе «разметка» мы предполагаем, что все объекты «мертвы» (недостижимы из рутов прим. пер.). CLR знает какие из объектов гарантированно живы в момент начала сборки и помечает их живыми. Всё объекты на которые они ссылаются также помечается как живые и т.д. пока всё транзитивное замыкание живых объектов не будет помечено. В фазе сборки все «мёртвые» объекты превращаются в «дырки». В фазе сжатия блоки реорганизуются таким образом, чтобы живые объекты составляли непрерывный блок памяти без «дырок».

Описанная модель усложняется тем что таких областей три: CLR коллектор реализует поколения. Сначала объекты находятся в куче с «коротким временем жизни». Если они выживают*** то со временем они переносятся в кучу со средним временем жизни и если там они выживают достаточно долго, то переносятся в кучу с долгим временем жизни. GC очень часто запускается на куче с коротким временем жизни и очень редко на куче с долгим временем жизни; идея в том, чтобы сэкономить на постоянной проверке долгоживущих объектов живы они до сих пор или нет. Но мы также хотим, чтобы короткоживущие объекты быстро освобождали память. У GC есть огромное множество точно выверенных политик обеспечивающих высокую производительность. Они определяют баланс между состоянием, когда память похожа на швейцарский сыр и временем, затраченным на фазу сжатия. Очень большие объекты хранятся в специальной куче с совершенно другой политикой сжатия. И т.д. и т.п. Я не знаю всех деталей и к счастью мне это и не нужно. (И конечно же я не стал усложнять деталями, не относящимися к данной статье такими как «привязка объектов»****, финализация, слабые ссылки и т.п.)

Теперь сравним это со стеком. Стек, как и куча, это большой кусок памяти с указателем на верхнюю границу. Но что по-настоящему делает этот кусок памяти стеком — это то, что память внизу стека всегда живёт дольше чем память наверху стека; стек строго упорядочен. Объект, который должен умереть первым — наверху, объект который должен умереть последним — внизу. Основываясь на этом мы знаем, что стек никогда не будет иметь дырок и ему никогда не нужно будет сжатие. Мы также знаем, что память в стеке всегда освобождается сверху и нам не нужно обслуживать список свободных секторов. Мы знаем, что все что лежит в стеке ниже гарантированно живо и нам не нужно ничего помечать и собирать.

Выделение памяти на стеке это всего лишь перемещение указателя — ровно также, как и в наилучшем (и достаточно типичном) случае при выделении память в куче. Но из-за всех этих свойств стека освобождение память — это тоже всего лишь перемещение указателя! И это именно то где мы экономим кучу времени. У меня сложилось мнение что множество людей думают, что выделение на стеке дёшевы, а в куче — дороги. Но на самом деле это практически одинаковые по времени операции, обычно. Но вот процесс освобождение памяти — это само освобождение памяти, дефрагментация и перемещение объектов из поколения в поколение, всё вместе это очень значительные перемещения блоков памяти в сравнении с тем, что мы видим на стеке.

Очевидно, что лучше использовать стек нежели кучу если Вы можете. Но когда же Вы можете? Только когда все условия чтобы стек работал выполняются. Локальные переменные и параметры значимых типов — самые «сладкие» случаи, когда все условия соблюдены. Локальные данные вызывающих функции, расположенные внизу стека, живут гарантированно дольше нежели локальные данные, расположенные вверху стека вызываемых функций. Локальные переменные значимых типов передаются по значению, а не по ссылке, это гарантирует что только локальная переменная указывает на данный кусок памяти и не нужно что-либо высчитывать чтобы определить время жизни объекта. И существует только один способ передать ссылку на значимую локальную переменную — это ref или out, которые передаются в функции, расположенные выше на стеке. Локальные переменные, расположенные ниже так и так живы пока функция выше по стеку не вернёт управление, поэтому время жизни переданных по ссылке объектов не изменится.

Немного дополнений:
Абзац выше объясняет почему мы не можем создать поле типа ref int. Если бы Вы могли сохранить ссылку на объект с коротким временем жизни внутри поля долго живущего объекта, если бы вдруг это случилось, то стек потерял бы свои преимущества и значимые типы стали бы просто ещё одной разновидностью ссылочных типов, нуждающихся в сборке мусора.

Замыкания анонимных функций и замыкания блоков операторов, компилятор реализует через поля скрытых классов. Теперь, я думаю, Вы понимаете почему запрещено замыкать ref и out переменные.

Конечно, мы не хотели создавать уродливое и странное правило типа «вы можете использовать в замыкании любую локальную переменную кроме параметров функции, переданных через ref и out». Но т.к. мы хотели использовать оптимизацию, располагая значения на стеки, то мы вынуждены были добавить такое на первый взгляд странно ограничение в язык. Это, впрочем, как и всегда, искусство находить компромиссы.

Кстати, CLR позволяет возвращать ref типы. Теоретически Вы могли бы создать метод «ref int Foo(){…}» который возвращает ссылку на целочисленную переменную. Если бы по какой-то странной причине мы бы решили разрешить такое в C#, то мы вынуждены были бы подкорректировать компилятор и проверять, что возвращаемая ссылка присваивается либо переменной в куче, либо переменной, расположенной в стеке ниже.

Вернёмся к нашим баранам. Локальные переменные расположены на стеке потому что могут. Они могут потому что 1 — «нормальные» локальные переменные имеют строго определённое время жизни и 2 — значимые типы всегда копируются по значению и 3 — хранить ссылку на локальные переменные можно только в каком-либо контейнере, время жизни которого, больше чем время жизни локальной переменной. На контрасте время жизни ссылочных типов определяется количеством живых ссылок, копируются по ссылке и эти ссылки могут храниться где угодно. Эта дополнительная свобода, которую ссылочные типы дают Вам прося в замен время на более сложную и дорогую стратегию сборки мусора.

Но опять же — это детали реализации. Использование стека для локальных переменных, это просто оптимизация которую CLR делает за Вас. Основная же фишка значимых типов — это то, что что объекты таких типов копируются по значению, а не то что их память может быть оптимизированы средой исполнения.

(*) От переводчика: в .net есть ещё куча для внутренних CLR объектов, но обычно её не рассматривают, так что в данном случае имеется в виду именно куча, которая собирается GC и в которой хранятся экземпляры объектов, созданных пользователем.
(**) От переводчика: сжатие в данном контексте эквивалентно дефрагментации
(***) От переводчика: не собраны при сборке мусора
(****) От переводчика: в оригинале pinning — специальные режим GC при котором он не перемещает объекты (минует фазу сжатия).

Written by alexeysuvorov

30.04.2014 at 11:46 пп

Опубликовано в Uncategorized

Wolfram

leave a comment »

То чувство, когда интеграция с facebook, machine learning и 3д чарты у тебя уже в BCL 🙂

Written by alexeysuvorov

24.04.2014 at 12:41 пп

Опубликовано в Uncategorized

Не найден Microsoft.Office.Interop.Word

leave a comment »

У меня потерялся весь Interop. Потерялся очень странно, потому что я видел его в GAC в списке сборок, но физически на диске сборки отсутствовали. Переустановка офиса не помогла, не помогло и добавление Office tools for visual studio к сетапу студии потому что как оказалось отношения к interop это не имеет вообще.
Далее 3 шага, каждый из которых может привести к решению проблемы, упорядоченные по возрастанию кривизны решения:

  • Установить Microsoft Office 2010: Primary Interop Assemblies Redistributable скачав его отсюда
  • Если не помог инсталлер и сборки не появились в add reference в студии, то скачиваем lessmsi или аналогичную тулу которая умеет распотрошить msi. Открываем msi скаченный на предыдущем шаге и достав сборки инсталлируем их в gac вручную
  • Мне не помог и вариант #2 так что я просто сложил нужные сборки (office.dll и Microsoft.office.interop.word.dll) в директорию к своему скрипту

В общем и целом опыт очень неприятный.

Written by alexeysuvorov

06.06.2013 at 7:20 дп

Опубликовано в .net, Uncategorized

Tagged with

RavenDB в реальной жизни

6 комментариев

Intro

Хочу поделиться опытом полученным при внедрении nosql в небольшой реальный проект. Взаимодействие с базой в этом проекте ограничивалось тремя операциями : быстрое создание большого количества записей без контента, проход по записям и загрузка контента, изменение полей большого количества записей отвечающих условиям.

Столкнувшись с невозможностью продолжать разработку на mongodb из за глобального лока, который тот ставит при записи на всю базу (при активной записи база практически недоступна на чтение) выбор пал на ravendb. Вначале Ravendb производит ощущение сырого продукта: LINQ поддерживается только для совсем простых случаев, из инструментов мониторинга – веб страничка с silverlight приложением, очень строгие лимиты на количество выбираемых за раз объектов и чтений/записей в рамках сессии, информацию приходится собирать из конференций и половина решений уже устарела, но потом всё встаёт на свои места.

Выборка коллекции документов по коллекции ID

Есть кое что, что nosql умеет лучше всего – операция вставки. В моём случае даже если есть разинца с mongodb по скорости, то она незаметна. Задача была обрабатывать массив данных из внешнего хранилища, соответственно входящие данные могли уже быть в базе и это нужно было верифицировать. Тут появилась первая проблема: ravendb не поддерживает .Contains() в LINQ. Решение нашлось достаточно быстро:

string req = string.Join(" OR ", items.Select(x => x.ToString()).ToArray());
string query = string.Format("PropertyName:{0}",req);
IDocumentStore store = ...//resolve initialized doc store
using(var s = store.OpenSession()){
    IEnumerable<SomeEntity> items = s.Advanced.LuceneQuery<SomeEntity>().Where(query);
}

Размер порции ID который приходил не верификацию позволял делать запрос через форматирование строки, но данное решение далеко не универсально. Сразу немного строгой типизации:

public static string CreateQueryString<T, U>(Expression<Func<T, U>> action, U par) 
where T : class 
{
    var expression = (MemberExpression)action.Body;
    string name = expression.Member.Name;
    return string.Format("{0}:{1}",name, par);
}
public static string CreateQueryString<T, U>(Expression<Func<T, U>> action, IEnumerable< U> par) 
where T : class 
{
    var expression = (MemberExpression)action.Body;
    string name = expression.Member.Name;
    string req = string.Join(" OR ", par.Select(x => x.ToString()).ToArray());
    return string.Format("{0}:({1})",name, req);
}
//somewhere
string query = CreateQueryString((SomeEntity o) => o.PropertyName, tmpId);
IEnumerable<SomeEntity> items = s.Advanced.LuceneQuery<SomeEntity>().Where(query);

Выборка большого количества записей

Особенность ravndb – ограничение на количество возвращаемых объёктов. Ограничение понятное и разумное, особенно для web приложений, но для задачи заполнения пустых объектов контентом загружаемым отдельно это неудобно, поэтому я использовал ленивый Enumerable чтобы затягивать записи по мере необходимости. CollectionEmumerable принимает функцию, которая возвращает данные и количество объектов возвращаемых за раз. Код:

public class CollectionEmumerable<T> : IEnumerable<T>
{
    private readonly Func<int, int, IEnumerable<T>> _f;
    private readonly int _size;
    public CollectionEmumerable(Func<int, int, IEnumerable<T>> f, int size = 50)
    {
        _f = f;
        _size = size;
    }
    public IEnumerator<T> GetEnumerator()
    {
        return new CollectionEmumerator<T>(_f, _size);
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
public class CollectionEmumerator< U> : IEnumerator< U> {
    private readonly Func<int, int, IEnumerable< U>> _f;
    private List< U> _items;
    private int _skip;
    private readonly int _take;
    private int _curr;
    public CollectionEmumerator(Func<int, int, IEnumerable< U>> f, int take) {
        _f = f;
        _take = take;
        LoadNext();
    }
    private void LoadNext() {
        _items = _f(_skip, _take).ToList();
        _skip += _take;
        _curr = -1;
    }
    public void Dispose() { }
    public bool MoveNext() {
        _curr++;
        if (_curr == _take) { 
            LoadNext();
            _curr = 0;
        }
        return _items.Count > 0 && _curr < _items.Count;
    }
    public void Reset() {
        _skip = 0;
        LoadNext();
    }
    public U Current {
        get { return _items[_curr]; }
    }
    object IEnumerator.Current {
        get { return Current; }
    }
}

Использование:

IDocumentStore store = ...//resolve initialized doc store
var f = new Func<int, int, IEnumerable<SomeEntity>>((skip, take) => {
    using(var s = store.OpenSession()){
        IEnumerable<SomeEntity> res = s.Query<SomeEntity>()
                                       .Where(x=>x.SomeProperty == "123")
                                       .Skip(skip)
                                       .Take(take);
        return res;
    }
});
IEnumerable<SomeEntity> items = new CollectionEmumerable<EstateType>(f);

Открывать сессию на каждую выборку необходимо из за ограничения в 30 roundtrip(буду признателен если кто то напишет в комментариях как грамотно перевести на русский это слово) в рамках одной сессии. В моём случае порядок был неважен, потому что мне нужны все данные соответствующие условию, но если важен порядок, то выборку нужно делать по индексу.

Удаление большого количества записей

Тут действуют те же правила – не больше 30 удалений в сессию, при этом до того как удалить объекты их ещё нужно загрузить в память. Неудобно. Медленно. Удалить большое количество объектов можно с использованием индекса по этому объекту. Тут я бы хотел сделать небольшое отступление по поводу индексов.

В ravendb индекс на выборку нужно использовать всегда. Если нет индекса соответствующего выборке – база создаст его. Если не вдаваться в детали, то ravendb удобно представлять в ванильных инженерных мечтах как 2 независимых хранилища: непосредственно документы и хранилище с индексами. Индексы можно создавать через клиента:

var def = new IndexDefinitionBuilder<SomeEntity> 
          { 
              Map = docs => from doc in docs select new { doc.SomeProperty } 
          };
ds.DatabaseCommands.PutIndex("some/indexname",def);

Я не считаю создание индексов в коде приложения хорошей практикой, они должны быть известны на момент разработки и код их создания должен быть вынесен в отдельную утилиту, так что я использую динамическое создание индексов в целях тестирования. Как проверять наличие индекса правильно я так и не нашёл, так что опять же для тестов я проверяю так:

if (!ds.DatabaseCommands.GetIndexNames(0, 1000).Contains("some/indexname")) { 
   //then create index 
}

Вернёмся к удалению

var ds = ...//resolve initialized doc store
using (var s = ds.OpenSession())
{
    IndexQuery query = new IndexQuery(); //all
    s.Advanced.DatabaseCommands.DeleteByIndex("Aggregator/Status",query);
    s.SaveChanges();
}

Или удаление с использованием условия:

var ds = ...//resolve initialized doc store
using (var s = ds.OpenSession())
{
    //delete entity with id  1 and 2 and 3
    IndexQuery query = CreateQuery((SomeEntity t)=>t.Id, new []{1,2,3}); 
    s.Advanced.DatabaseCommands.DeleteByIndex("SomeEntity/allEntityIndex",query);
    s.SaveChanges();
}
//Где то в коде
public static IndexQuery CreateQuery<T, U>(Expression<Func<T, U>> action, U par) 
    where T : class 
{
    return new IndexQuery { Query = CreateQueryString(action, par) };
}
public static IndexQuery CreateQuery<T, U>(Expression<Func<T, U>> action, IEnumerable< U> par) 
    where T : class 
{
    return new IndexQuery { Query = CreateQueryString(action, par) };
}

Ещё одна особенность работы с индексами – индексы не пересчитываются мгновенно. Для уверенности что выберутся актуальные данные есть набор функций WaitForNonStaleResults*. В тестах для уверенности что мой индекс по которому я удаляю/модифицирую коллекцию готов я использую следующий код:

var ds = ...//resolve initialized doc store
using (var s = ds.OpenSession()) {
    //will wait 1h for index           
    var tmp = s.Advanced.LuceneQuery<SomeEntity>().WaitForNonStaleResults(TimeSpan.FromHours(1d)).First();
    //TestEntity index is ready
}

Обновление большого количества документов

Тут те же проблемы что и с удалением. Загружать документы для модификации долго, за сессию не больше 30 документов. В ravendb есть специальный механизм для таких случаев:

//using (var s = ds.OpenSession()) e.t.c.
//where SomeEntity.PropertyName == 1
IndexQuery q = CreateQuery((SomeEntity t) => t.PropertyName, 1);
PatchRequest[] p = new [] { 
                           //SomeEntity.AnotherProperty = "new value"
                           CreatePatch((SomeEntity t) => t.AnotherProperty, "new value")
                          };
s.Advanced.DatabaseCommands.UpdateByIndex("some/indexName", q, p, false);
s.SaveChanges();
//Где то в коде
public static PatchRequest CreatePatch<T, U>(Expression<Func<T, U>> action, U par) 
    where T : class 
{
    var expression = (MemberExpression)action.Body;
    string name = expression.Member.Name;
    var res = new PatchRequest {
        Type = PatchCommandType.Set,
        Name = name,
        Value = RavenJToken.FromObject(par)
    };
    return res;
}

Итого

Помоему разработчикам ravendb просто нехватает времени, чтобы имплементировать нормальную поддержку LINQ и интерес к этой БД cущественно ниже чем к mongodb из за моноплотформенности, хотя я считаю что идеи реализованные в ravendb очень грамотные и со своей стороны буду продолжать использовать именно ravendb в собственных проектах (по крайней мере до тех пор пока mongodb не избавится от невменяемого глобального лока)

Written by alexeysuvorov

21.10.2011 at 10:47 дп

Опубликовано в Uncategorized