вторник, 31 июля 2012 г.

EF Migrations: database schema maintenance burden solved? … Not really.

Just a couple of quick notes about EF Migrations [with ‘code first’ approach]. It sounded like EF Migrations were going to solve all the problems around database development process and allow all the magic to happen right inside Visual Studio. A lot of magic is happening under the hood indeed, but it does not seem to be able to let us forget about sql scripts once and forever (it’s not that I ever believed it really will Winking smile).

First comes a task of deploying a database. We can have a connection string in [web].config, and in case the database is empty, it will be populated by applying all migrations right from the start (starting from Initial). It works ok unless you have some [initial] data to be present in the database upon first launch of the application. You might argue that this is a problem of deployment, not EF Migrations itself, but wait a minute – how are we supposed to deploy a database without a [sql] script? Ok, EF Migrations give you an option for creating a sql script rather than applying a particular migration to an existing database. But there’s a trick – you still have to have a database in place in order to do this. I’ve found it a bit annoying [and frustrating] to have to create an empty database just for getting migration scripted – Update-Database does not work without a connection string. I wish it could fake it.

Ok, you’ve done your homework and now you have an empty database to deed Update-Database with. My first intention was to let EF Migrations to script the latest state of the schema for me [by applying all the migrations one by one]. I issued

Update-Database –script –ConnectionString “[my connection string]”

and got a script (finally!). But guess what? It was not working – due to the fact that EF Migrations does not put each migration into a separate batch, it got a number of very simple syntax errors (declaring a variable multiply times). It does not take long to fix it, but in general it does not look reliable enough for me to trust it.

I ended up with scripting only ‘Initial’ migration and letting EF Migrations do the rest of schema update (with initial data already in) upon first launch of the application. This leaves [me] an open question, however: what if we need to start developing ‘version Next’ and start with the database of the current one? Sure, we can script it into ‘Initial’ in new project. Or just make a branch and continue development having all the migrations from the previous version. Both options does not look great in terms of maintenance.

Despite these little issues, EF Migrations are a huge step ahead and I see that employing code-first approach simplifies life for both app and db developers – less sql to produce/review/maintain.

HTH,
AlexS

суббота, 21 июля 2012 г.

Web Deployment Tool – making it install application remotely

The key is in precisely following this instruction when configuring remote server (it took me quite a while to identify which tutorial/instruction is the one to follow). There are, of cause, some other issues that can stand on your way:

  • you’d want to install Web Deployment tool manually and do a complete install – otherwise you’ll spend a fair amount of time looking for missing options in IIS Manger console;
  • when setting up Management Service Delegation, make sure that you enable the following providers: contentPath, createApp, dbSqlite, iisApp, package, setAcl – otherwise you will have to carefully read error messages and reiterate over delegation config (by default only contentPath and iisApp are included);
  • I would definitely suggest using dedicated low privileged domain users (and you have to have a domain, of cause).

HTH,
AlexS

четверг, 22 марта 2012 г.

Пара заметок об Open XML

В очередном проекте в очередной раз нужно генерировать отчеты в Excel (ну ладно: наконец-то выпала возможность “попедалить”! Улыбка ). Из имеющихся в распоряжении средств: генерировать html (просто, но слишком коряво), office xml (по-моему до сих пор вполне пристойный вариант) и Open XML aka Office 2007+ – был выбран последний. Аргументы: по отзывам на habrahabr все должно быть просто и быстро, к тому же на выходе мы получаем актуальную версию формата.

Open XML SDK 2.0 “весит” немногим больше 100 Мб – по нынешним временам, конечно, не объем, но все-равно возникает вопрос: “А что туда такого напихали и почему так много?” В комплект входит “Open XML SDK 2.0 Productivity Tool for Offce”, который может по данному файлу сгенерировать код, который сгенерирует точно такой же файл. Вроде бы все просто, но когда начинаешь разбираться в коде, становится грустно.

  1. API – жуть какое неудобное.
    Вроде как обещается, что это высокоуровневое API, упрощающее создание/редактирование документов (цитирую: “The Open XML SDK 2.0 encapsulates many common tasks that developers perform on Open XML packages, so that you can perform complex operations with just a few lines of code.”).
    • Почему тогда изо всех углов торчит XML и API по работе с ним (все эти бесконечные Append, Text, AddNamespaceDeclaration)?
    • Зачем заставлять меня пользоваться специальными типами для всего, что только можно придумать (включая специальный тип UInt32Value)? В результате приходится пользоваться прямо-таки умопомрачительным количеством разнообразных типов.
    • В высокоуровневом API(!) для создания документов нужно не забыть закрыть пакет (о, вы не знали, что документ – это на самом деле пакет?) – иначе на выходе получим “битый” файл.
    • Как бы глупо это не звучало, но строго типизированное API “молча” позволяет вам сгенерировать невалидный документ. Вот так-то. Добиться этого довольно легко – просто попробуйте сохранить в ячейку какую-нибудь строку в обход таблицы SharedStrings. Для того, чтобы понять, как же это делать “по-правильному”, нужно потратить уйму времени и [внимательно] перечитать примеры кода (код, сгенерированный Productivity Tool-ом вам в этом не поможет).
  2. Убогая документация.
    • Есть примеры. Но почему-то код в примерах “ни разу не похож” на то, что генерирует тот самый Productivity Tool. Более того,
    • Описание классов поражают своей информативностью. Для большинства классов все описание – “When the object is serialized as xml, its qualified name is XXX”. В некоторых случаях еще ссылаются на раздел стандарта. Такая документация очень помогает разобраться в том, как мне сделать что-то.

Часть проблем мне удалось побороть, но добавление графиков пока “продолжает сопротивление” (количество всяких параметров/атрибутов и запутанность их значений навевают уныние).

Гораздо приятнее выглядит проект с издевательским (для формата Open XML) именем ClosedXML – поверх стандарта соорудили человеческое API, которое действительно позволяет достигнуть нужного результата буквально парой строк кода.

HTH,

AlexS

среда, 1 декабря 2010 г.

“В жизни всегда есть место для подвига” © или как реализовать Singleton по-новому

Тема реализации шаблона Singleton так или иначе затрагивает жизнь любого разработчика – толи на собеседовании спросят (или самом спросить придется), толи в реальном проекте найдется применение (не приведи Господи). О том, как этого зверя вырастить, написано немало – и в MSDN, и в прочих интернетах (последняя статься считается канонической и ранее располагалась по другому адресу). В какой-то момент казалось (мне по крайней мере), что придумать что-нибудь новое в этой области невозможно. Ошибся.

Недавно на одном из проектов заказчик прислал кусок [псевдо]кода, в котором кроме всего прочего содержалась крайне любопытная реализация привычного шаблона (тот еще затейник – архитектура решения тоже заставляем “много думать”). В чистом виде (убирая конкретику проекта и применяя привычные для .NET схемы именования и форматирования) это выглядит так:





public interface ISingleton
{

}

class Singleton : ISingleton
{
public delegate ISingleton InstanceGetterDelegate();

private static Singleton _singleton;
private static InstanceGetterDelegate _instanceGetter;
static Singleton()
{
var type = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType;
// assign the closure that executes on the first call of GetInstance() method
_instanceGetter = () =>
{
Singleton instance = null;
lock (type)
{
var rslt = Interlocked.CompareExchange<Singleton>(ref _singleton, null, null);
if (rslt == null) // indicates is first call
{
instance = new Singleton(/* init arguments go here*/);
Interlocked.CompareExchange<Singleton>(ref _singleton, null, instance);

// now replace with a closure that executes on all subsequent calls of GetInstance() method
InstanceGetterDelegate fn = () =>
{
return Interlocked.CompareExchange<Singleton>(ref _singleton, null, null);
};
Interlocked.Exchange<InstanceGetterDelegate>(ref _instanceGetter, fn);
}
else
{
instance = rslt; // was already set with singleton instance
}
}
return instance;
};
}
private Singleton() { } // disable default instance constructor
public static ISingleton GetInstance() { return _instanceGetter(); }
}


И вот тут, что называется, “накрывает”. Я бы назвал этот способ “JavaScript-style Singleton” (конечно же за использования замыканий). В целом (за исключение блокировки по Type (о чем я уже писал) и использования рефлексии в статическом конструкторе) реализация выглядит любопытной и уж во всяком случае расширяет горизонт.



HTH, AlexS

суббота, 27 ноября 2010 г.

sqlite: приятные мелочи в области агрегирующих функций

Продолжаю очень приятно удивляться sqlite. Очередным поводом стали агрегирующие функции:

  • есть две функции для вычисления сумм: традиционная sum() и чуть менее традиционная total() – обе суммируют не-NULL значения, но первая (в соответствии со стандартом SQL) возвращает пустой набор данных в случае, если таких значений не было, а вот вторая всегда возвращает 0.0 (что в некоторых случаях может быть весьма удобно). Кроме того, функция total() всегда возвращает double и не подвержена арифметическому переполнению;
  • есть крайне полезная функция group_concat(field[, separator]), которая объединяет значения поля в одну строку. Разделитель по-умолчанию – запятая. Причем в варианте с разделителем по-умолчанию можно использовать еще и DISTINCT:
    SELECT group_concat(DISTINCT user_name) FROM …. GROUP BY …
    Замечу, что в SQL Server-е (горячо и искренне мною любимом) нет простого способа сделать тоже самое – только через custom aggregate (т.е. специальную сборку, которую писать/разворачивать).

В общем: чем больше смотрю - тем больше нравится.

HTH, AlexS

вторник, 16 ноября 2010 г.

Эволюция MS Full-Text Search: доступ к анализатору полнотекстовых запросов

Так и работает эволюция: незаметно и по чуть-чуть. Недавно на StackOverflow задали вопрос (в вольном переводе):

Можно ли получить доступ к стеммеру, который используется в SQL Server FullText Search?

Моей первой мыслью было “нельзя”. Но на всякий случай решил свериться с MSDN. Так вот оказалось, что “почти можно”: в SQL Server 2008 в FullText Search помимо радикального изменения архитектуры, внесли менее заметные, но полезные изменения. В частности, была добавлена следующая системная табличная функция:

sys.dm_fts_parser('query_string', lcid, stoplist_id, accent_sensitivity)

Она-то и позволяет обратиться к стеммеру (точнее к синтаксическому анализатору полнотекстовых запросов). Вот как это работает (лучше один раз увидеть, как говорится):

select * from sys.dm_fts_parser('FORMSOF(inflectional, Worked)', 1033, 0, 0)

Т.е. мы запросили все словоформы слова “worked” в английской локали (locId = 1033). И вот какие ключевые слова будут соответствовать этому запросу:

image

Было бы еще хорошо иметь возможность заглянуть внутрь самого индекса (как это делает Luke для Lucene), но даже с этими средствами жить  становится намного веселее.

HTH, AlexS

четверг, 29 июля 2010 г.

Чем точнее указан путь – тем быстрее можно по нему идти ((С) Кэп)

Недавно попросили помочь с оптимизацией обработки XML в SQL Server-е. Запрос выглядит примерно следующим образом:

SELECT
  nref.value('./../../@id', 'int') AS articleid,
  nref.value('type[1]', 'nvarchar(256)') AS type,
  nref.value('theme[1]', 'varchar(256)') AS theme,
  nref.value('score[1]', 'varchar(32)') AS score
INTO #tmpTheme
FROM @bulk_xml.nodes('//theme') AS R(nref)

Очень простое действие – указание точного XPath выражения вместо обобщенного ('//theme') позволяет ускорить работу данного запроса в 1.5 – 2 раза.

HTH,

AlexS