Tuesday, December 29, 2009

Configuring Log4Net AdoNetAppender with a connection string from application configuration file

I read Ben Scheirman's "Changing log4net connection string at runtime" post and wanted to leave a comment describing solution I use to solve log4net ConnectionString configuring problem. But as I wanted to show some code, it might be better to make a blog post.

So the problem I faced with was:
How to configure log4net AdoNetAppender to use connection string specified in a <connectionStrings> section of an application configuration file?
There is no obvious way to do this in a current release of log4net. But such issue is already fixed in a log4net trunk, thus it will be possible to do so in the next release.

I don't really like to use the code which not yet officially released. So I use a simple workaround - a class inherited from AdoNetAppender with a new property ConnectionStringName. The code is pretty obvious:

using System.Configuration;

using log4net.Appender;
using log4net.Core;

/// <summary>
/// Allows to use a connection string specified in the 
/// <see cref="ConfigurationManager.ConnectionStrings"/> section of an application
/// configuration file.
/// </summary>
/// <remarks>
/// Such feature is already implemented in a log4net trunk (see
/// https://issues.apache.org/jira/browse/LOG4NET-88). But it is not yet released.
/// <para/>
/// TODO: Remove this class as soon as log4net 1.2.11 will be released.
/// </remarks>
public class Log4NetConnectionStringNameAdoNetAppender : AdoNetAppender
{
    private string connectionStringName;

    /// <summary>
    /// Gets or sets the name of the connection string from the application
    /// configuration file.
    /// </summary>
    /// <value>The name of the connection string.</value>
    public string ConnectionStringName
    {
        get
        {
            return this.connectionStringName;
        }
        
        set
        {
            this.connectionStringName = value;

            if (!string.IsNullOrEmpty(this.connectionStringName))
            {
                var settings =
                    ConfigurationManager.ConnectionStrings[this.connectionStringName];
                if (settings == null)
                {
                    string msg = string.Format(
                        "Unable to find [{0}] ConfigurationManager.ConnectionStrings item",
                        this.connectionStringName);

                    throw new LogException(msg);
                }

                this.ConnectionString = settings.ConnectionString;
            }
        }
    }
}

And the log4net configuration looks like this:
<appender name="DBAppender" type="NamespaceWhereClassLives.Log4NetConnectionStringNameAdoNetAppender,AssemblyContainingClass">
    <connectionStringName value="ConnectionStringNameFromConfigFile"/>
    <!-- Other parameters -->
</appender>
This looks like a hack, but it works for me pretty well.

Wednesday, October 14, 2009

На Украине? На Росії!


Вчора дізнався, що фінською правильно вживати "Я був на Росії" (Minä olin Venäjallä), але "Я живу в Україні" (Minä asun Ukrainassa).

На відміну від інших країн, для Росії, в фінській мові використовуються зовнішні місцеві відмінки. Приклади тут.

PS. Взагалі то я не дуже переймаюсь тим як саме кажуть росіяни - "на Украине" чи "в Украине". Мені це навіть імпонує трохи. Все таки підкреслюється унікальність нашої країни. Писав же Некрасов "Кому на Руси жить хорошо". Так от я подумав, може це "на" нам у спадок від Київської Русі дісталась?

Та не про те хотів сказати. Я не знаю як правильно вживати "в Україні" російською мовою. Але я знаю, що українською правильно казати: "Я живу в Україні".

За кого голосувати на виборах?

Послухайте чудову пісню Сашка Положинського. Мене дуже порадувала.

Wednesday, September 30, 2009

Майкл К. Физерс. Эффективная работа с унаследованным кодом



Останнім часом я зустрічав багато посилань на книгу "Эффективная работа с унаследованным кодом" із дуже позитивним відгуками. Тому сподівався, що книга мені сподобається. Але прочитавши її, мої очікування не виправдались. Навпаки, книга здалась мені дуже нудною. Не знаю чия це вина, автора, перекладачів, чи мого сприйняття. Багато сторінок, я просто переглядав по діагоналі. Часто ловив себе на тому, що прочитавши якийсь розділ нічого не зрозумів, бо думки гуляли деінде,  а перечитувати ніякого бажання немає. І взагалі не розумію, як можна стільки уваги приділити питанню - як розірвати залежність між кодом, і жодним словом не обмовитись про Dependecy Injection Containers (IOC). А, точно, це ж успадкований код. До того ж судячи з книги, більшість успадкованого коду - це код на C та C++. Але мене це мало цікавить.

Sunday, September 27, 2009

High/Low generator with SQL

In current project, we use High/Low generator strategy to generate identifiers for entities. (Fabio Maulo wrote a good explanation why using identity is a bad thing). The question I sometimes see in NHibernate forums is what to do if some other applications also use the same database. Usual answer is - implement some service which would generate id, and use it in that applications.
Yesterday I met similar issue. What I needed to do is to insert some fake data using SQL only. For that purpose I wrote such stored procedure:

IF OBJECT_ID('dbo.GenerateHiLoIdRange') IS NOT NULL
  DROP PROCEDURE dbo.GenerateHiLoIdRange
GO
CREATE PROCEDURE dbo.GenerateHiLoIdRange (
    @IdsCount INT
)
AS
/* =============================================================== */
/* == Generates a range of identifiers using High/Low strategy. == */
/* =============================================================== */
BEGIN
    SET NOCOUNT ON;
    
    IF @IdsCount <= 0
    BEGIN
        RAISERROR('@IdsCount should be positive. But was %d', 16, -1, @IdsCount)
        RETURN(-100);
    END

    DECLARE @NextHi INT
    DECLARE @MaxLo  INT
    SET @MaxLo = 100  -- Hardcoded value used over all tables in a database.
 
    BEGIN TRANSACTION UpdateHiLoTable
        
        SET @NextHi = (
            SELECT next_hi 
            FROM hibernate_unique_key WITH (UPDLOCK, ROWLOCK)
        )
        
        UPDATE hibernate_unique_key 
        SET next_hi = @NextHi + 1 + (@IdsCount - 1) / @MaxLo
        WHERE next_hi = @NextHi
        
    COMMIT TRANSACTION UpdateHiLoTable
    
    SELECT @NextHi * @MaxLo + IntValue
    FROM   dbo.Integers(@IdsCount - 1)
END
GO

Main points of procedure:
  • It uses hard coded MaxLo value (100) , which is used over all entities in our application.
  • Calculates new NextHi value based on requested identifiers count and updates hibernate_unique_key table in transaction.
  • To return a result set containing generated identifiers procedure needs some sort of "Integers" table. In my case i simulate such table with a table-valued function.
IF OBJECT_ID('dbo.Integers') IS NOT NULL
  DROP FUNCTION dbo.Integers
GO
CREATE FUNCTION dbo.Integers (
    @MaxValue INT
)
RETURNS @Integers TABLE (IntValue INT NOT NULL)
AS
/* =============================================================== */
/* ==   Returns the [0,  @MaxValue] range of integer values.    == */
/* =============================================================== */
BEGIN
    IF @MaxValue > 9999
    BEGIN
      RETURN;
    END;
    
    DECLARE @Digits TABLE
    (
        Digit INT NOT NULL PRIMARY KEY
    )

    INSERT INTO @Digits (Digit) VALUES (0)
    INSERT INTO @Digits (Digit) VALUES (1)
    INSERT INTO @Digits (Digit) VALUES (2)
    INSERT INTO @Digits (Digit) VALUES (3)
    INSERT INTO @Digits (Digit) VALUES (4)
    INSERT INTO @Digits (Digit) VALUES (5)
    INSERT INTO @Digits (Digit) VALUES (6)
    INSERT INTO @Digits (Digit) VALUES (7)
    INSERT INTO @Digits (Digit) VALUES (8)
    INSERT INTO @Digits (Digit) VALUES (9)
    
    INSERT INTO @Integers
    SELECT Number
    FROM  
        (SELECT 
            Thousands.Digit * 1000 + 
            Hundreds.Digit * 100 + 
            Tens.Digit * 10 + 
            Ones.Digit AS Number
         FROM   
            @Digits Thousands
            CROSS JOIN @Digits Hundreds
            CROSS JOIN @Digits Tens
            CROSS JOIN @Digits Ones
        ) Integers
    WHERE Number <= @MaxValue
    ORDER BY Number
        
    RETURN;
END
GO
Note that number of integers in a function is limited to 10000. But that is what I need.

Now I can generate identifiers in t-sql batches:

CREATE TABLE #TempId (Id INT NOT NULL)

SELECT * FROM hibernate_unique_key

INSERT INTO #TempId
  EXEC dbo.GenerateHiLoIdRange 250

SELECT Id AS Id, 
    'PersonName' +  CAST(ROW_NUMBER() OVER(ORDER BY Id) - 1 AS VARCHAR(255)) AS PersonName
FROM #TempId

DROP TABLE #TempId

In my sandbox database I see such results:

Monday, June 29, 2009

Firefox - пожирач пам'яті



Це через кілька хвилин після того як, здавалося б, закрив firefox. Куди стільки пам'яті можна жерти?

Sunday, June 28, 2009

Linq в .Net Framework 2.0

Проблема: Клієнти живуть на Windows 2000, на яку можна поставити лише .Net Framework 2.0. Але в проекті хочеться використовувати Linq. Чи це можливо?

Рішення
: Можна! Завантажуємо чудову бібліотеку LinqBridge і з радістю використовуємо Linq.

Що саме містить ця бібліотека:
  • Власну реалізацію всіх Linq операторів для Enumerable класу із Framework 3.5, що саме і дозволяє використовувати Linq to Objects в .Net Framework 2.0.
  • Реалізує generic делегати Func та Action.
  • Реалізує ExtensionAttribute, який дозволяє використовувати extension methotds в .Net Framework 2.0.
Однак це не стосується Linq to Sql та Linq to Xml, які ця бібліотека не реалізує.

Детально про LinqBridge, і про те чому це можливо читайте в статті автора, якому я дуже вдячний за чудову роботу.

Sunday, February 15, 2009

Харпер Лі. Убить пересмешника.

В романі Харпер Лі, очима маленької дівчинки, показано життя Америки на початку минулого століття.

Читається дуже легко, чимось нагадало "Пригоди Тома Сойєра" Марка Твена.

Вернор Виндж. Пламя над бездной.

В романі автор пропонує дуже цікавий погляд на будову всесвіту. Він поділяється на зони - від центра до краю. В центрі - безодня, в якій розум неможливий. Чим ближче до краю, тим технологічнішими можуть бути цивілізації. В Краї, швидкість світла не є обмеженням. За краєм цивілізації стають Силами (я так і не зрозумів що це таке).

Розпочинається книга, тим, що представники людської цивілізації, десь в Краї, знайшли та розворушили якийсь архаїчний архів, і цим розбудили Зло, яке собі спокійно дрімало мільйони років. Корабель, в якому, як виявилось була протидія, встиг втекти і потрапив на планету в повільній зоні.

Надалі, однією з основних сюжетних ліній є опис цивілізації собакоподібних зграй, які населяють ту планету, де приземлився космічний корабель з утікачами. Ці собаки можуть бути розумними лише коли об'єднані в зграю. І вся зграя - це одна особистість.

Щоб активувати протидію Злу, споряджена експедиція. Пригоди та стосунки членів екіпажу: жінки, чоловіка, який певним чином пов'язаний із Силою, та двох розумних рослин на візках складають іншу сюжетну лінію.

І все це переплітається повідомленнями із груп новин пародійної всесвітньої мережі "мільйонів неправд".

Мені роман сподобався своєю незвичністю. Тепер собак шкода, як же їм, бідним "синглетам" живеться.

Monday, February 2, 2009

Герберт Уэллс. Машина времени.

Ця книга давно була в мене в readlist'і. Нарешті дійшли руки її прочитати. Перед тим як її завантажити, прочитав кілька дуже схвальних відгуків. І мабуть тому від книги очікував більше. Але прочитав із задоволенням, швидко. Перед сном і з ранку в метро.
Головною темою книги є питання соціальної нерівності, яке автор показав в незвичному ракурсі.

PS. Я думаю, що людству до 8000 року не дотягнути.

Sunday, February 1, 2009

Стивен Хокинг и Леонард Млодинов. Кратчайшая история времени.

Прочитав чергову книгу найвідомішого (принаймні для мене) фізика сучасності.

В цій книзі автор намагається, іще доступніше ніж в своїх попередніх книгах, пояснити будову всесвіту, з точки зору сучасної фізики.

І дійсно складні речі в книзі пояснені дуже доступно і з цікавими прикладами. Прочитав із великим задоволенням.

P.S. Але така хитрість автора зі мною не спрацювала. Все рівно нічого не розумію.