Sunday, January 29, 2012

MicroORM performance investigations

Hi All.

I would like to share some investigations on few MicroORM performance.
These investigations are inspired mostly by dapper-dot-net extensive performance.
I was choosing a fast ORM for my load-critical application. Dapper's select extreme performance was demonstrated. But what about inserts? Dapper do not explicitly support inserts, you can write a SQL query by yourself, but with the risk of getting column name or parameter wrong. After some browsing I found Dapper-Extensions and PetaPoco that seem to have an ability to insert/update an entity without specifying all columns explicitly. I also wanted to know what Entity Framework 4.2 performance is.

Tuesday, April 12, 2011

log4net in .NET 4.0 C# applications (.NET client profile)

Today's morning I decided to implement logging in my WPF C# 4.0 application.
I choosed log4net http://logging.apache.org/log4net/ as one of the most popular loggers.
After I included log4net.dll as a reference, I got a compilation error

Could not resolve assembly "System.Web". The assembly is not in the currently targeted framework ".NETFramework,Version=v4.0,Profile=Client". Please remove references to assemblies not in the targeted framework or consider retargeting your project.

The exception is pretty understandable, is says that your target Framework must be Full .NET 4.0 Framework, not limited client profile as now. System.Web assembly is a server component and Microsoft has not included it in lightweight .NET Client profile.
But what if I don't want to target my solely client application for Full .NET 4.0 Framework profile? Yes, the solution is simple. Just recompile log4net without System.Web references, and you're done!

I hadn't found recompiled log4net, and decided to do it by myself. After removing references to System.Web and solving a couple of small problems described here or here, I finally managed to compile it.
Compiled release version can be found here (in my Google Docs), so you can start using it. It misses some stuff related to web, but usable in overall.



Example of simple C# logger follows:


public class Logger
{
       static log4net.ILog _log;

       public static void LogException(Exception ex)
       {
             if (_log == null)
             {
                    log4net.Config.XmlConfigurator.Configure();
                    _log = log4net.LogManager.GetLogger(typeof(Logger));
             }
             _log.Error(ex);
       }
}



To have it writing to a text file Log/log.txt (related to your executable), you should make your app.config file look like (max log file size is 10 Mbytes, once you hit the limit, new file log.txt.1 will be created and so on):


xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
             <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net"/>
       configSections>


       <log4net>
             <root>
                    <level value="All" />
                    <appender-ref ref="LogFileAppender" />
             root>

             <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender" >
                    <param name="File" value="Log\log.txt" />
                    <param name="AppendToFile" value="true" />
                    <rollingStyle value="Size" />
                    <maxSizeRollBackups value="10" />
                    <maximumFileSize value="10MB" />
                    <staticLogFileName value="true" />
                    <layout type="log4net.Layout.PatternLayout">
                           <conversionPattern value="%date %-5level - %message%newline" />
                    layout>
             appender>
       log4net>
configuration>



Thursday, March 31, 2011

How to specify connection string to SQL CE 4.0 for DbContext

Recently I've started playing with Entity Framework 4.1 RC.

I've faced a question on how to specify DbContext's connection string to SQL CE 4.0 at runtime. I managed to do it in app.config like this:



   
       
          name="MyContext"
          providerName="System.Data.SqlServerCe.4.0"
          connectionString="Data Source=C:\database.sdf;Encryption mode=platform default;password=123;" />
   


Assuming that my context is named MyContext.

But it appeared to be a difficult task if I don't want to store my connection string in config file. I wished to construct it on runtime to set a password. Passing Provider in connection string did not help.

The solution is to create Sql CE connection by the function from System.Data.SqlServerCe.dll (can be found in C:\Program Files\Microsoft SQL Server Compact Edition\v4.0\Desktop as follows:


            var connection = System.Data.SqlServerCe.SqlCeProviderFactory.Instance.CreateConnection();
            connection.ConnectionString = "Data Source=C:\database.sdf;Encryption mode=platform default;password=123;";


            
            var db = new MyContext(connection); // MyContext: DbContext




You also need to modify MyContext's constructor to pass connection to protected constructor of DbContext.

Monday, December 14, 2009

Append data to Oracle BLOB

Hello all, since now I am going to use English in my blog.
My first "technical" post is about appending data to Oracle blob chunk-by-chunk.
Such kind of tasks often arises when you are going to store large file in database.
BLOBs can store binary data (up to 4 GB) in database tables (see http://download.oracle.com/docs/cd/B10501_01/appdev.920/a96591/adl01int.htm#119741 to deep into the BLOB details).
Often you must upload this file chunk-by-chunk to gain complete control over the process.
I am using Devart dotConnect component for Oracle (http://www.devart.com/dotconnect/oracle/). Component looks very nice, but I must admit that sometimes strange errors happen with this component. But updates are regular so I think if you find a bug you can report it and you will have it fixed in next version.

Ok, let's start with database definition and insert a record with empty BLOB into the table:

CREATE TABLE DOCUMENTS.FILES
(
FID NUMBER(10) NOT NULL,
FDATA BLOB NOT NULL
)
LOGGING
NOCOMPRESS
NOCACHE
NOPARALLEL
MONITORING
/

SET DEFINE OFF;
INSERT INTO DOCUMENTS.FILES
(
FID,
FDATA
)
VALUES
(
1,
EMPTY_BLOB()
)
/

-- (Here we are working with tablespace DOCUMENTS).

Okay, then we need to create a stored procedure that will take BLOB as a parameter and append it to BLOB stored in the table.
There is Oracle package dbms_lob. You can read more about its functions here http://www.psoug.org/reference/dbms_lob.html In the procedure we will use dbms_lob.append function that appends source BLOB to the end of destination BLOB:


CREATE OR REPLACE 
PROCEDURE clobtableappend (p_Value BLOB)
is
    dest_lob BLOB;
begin
  SELECT
    FDATA INTO dest_lob
  FROM
    DOCUMENTS.FILES
  WHERE FID = 1
  FOR UPDATE;
  
  dbms_lob.append(dest_lob, p_Value);
  
  COMMIT;
end;
/


Ok, then download latest version of dotConnect (http://www.devart.com/dotconnect/oracle/download.html), install it, create a project in Visual Studio, reference Devart.Data, Devart.Data.Oracle assemblies from GAC, and we are ready to write the code. I created a console application:


            string strConn = "User Id=***;Password=***;Server=***;Persist Security Info=True;direct=True;Unicode=true;sid=***;";
            OracleConnection conn = new OracleConnection(strConn);
            conn.Open();
            
            DirectoryInfo directoryInfo = new DirectoryInfo(@"D:\temp");

            OracleCommand command;

            var files = directoryInfo.GetFiles();
            for (int i = 0; i < files.Length; i++)
            {
                var stream = files[i].OpenRead();
                byte[] buf = new byte[stream.Length];
                stream.Read(buf, 0, buf.Length);

                command = new OracleCommand("DOCUMENTS.ClobTableAppend", conn);
                command.CommandType = CommandType.StoredProcedure;
                var param = command.Parameters.Add("p_Value", OracleDbType.Blob);
                param.Direction = ParameterDirection.Input;
                param.Value = buf;
                
                command.ExecuteNonQuery();
            }
            

            command = conn.CreateCommand();
            command.CommandText = "SELECT FDATA FROM DOCUMENTS.FILES WHERE FID = 1";
            using (var reader = command.ExecuteReader())
            {
                reader.Read();
                var bb = reader.GetOracleLob(0);

                byte[] b = new byte[bb.Length];
                bb.Read(b, 0, b.Length);

                File.WriteAllBytes("C:\\blob.txt", b);
            }



The algorithm is simple:
1. We enumerate all files in D:\temp directory.
2. For each file:
3.    We read it.
4.    We append it into database BLOB by calling the stored procedure.
5. After all files are appended, we read them back and write in c:\blob.txt.
I recommend you to use text files to control correctness of the program.

Monday, November 23, 2009

Оформление списка требований к программной системе в документе Word. Между прочим, эту статью не приняли на хабр. Почему?

Занимался следующей задачей: оформлением списка требований заказчика в документе Word. Что требовалось:

1. Представлять требования в виде идентификаторов типа RF001, RF002 (функциональные требования), RUI001, RUI002 (требования к пользовательскому интерфейсу).
2. Поддерживать их сквозную нумерацию по документу.
3. Делать на них ссылки в пределах документа.

Сквозной список требований обладает преимуществом удобного добавления нового требования при разработке документа.
Как правило, во всех вариантах документов, которые я видел, использовалось статическое задание номеров требований, что очень неудобно при разработке (обычно требования группируются по типу функциональности, например:
- Функциональные требования к модулю 1.
- Функциональные требования к модулю 2.
...
- Функциональные требования к модулю N.

и в случае статической нумерации добавление нового требования к модулю 2 оказывается непростой задачей).


Проблема осложняется тем, что:

1. Word не содержит средства для такой автоматической нумерации, как, например, для рисунков.
2. Формат номера должен быть трехзначный (в данном случае это так. У вас он может быть двухзначный - если число требований не превышает 99 штук).
3. Добавление текстового идентификатора RF, RUI и т.д. перед каждым номером.

Решается задача при помощи полей (fields) MS Word. Дальнейшие рассуждения для английского Word 2007.

Итак:
1. Надо перейти на ленту References и нажать кнопку Insert caption.




2. Далее в открывшемся окне определить новую метку (Label). Т.е. это префикс перед номером требования (RF, RUI).






3. Теперь для создания поля перейдите на ленту Insert и вставьте поле, как показано на рисунке:



4. В открывшемся окне нажимается кнопка Formula и вставляется следующий текст, собственно определяющий формат:



Для деталей по формату обратитесь к справке Word. Кратко скажу, что формат SEQ RF \# RF000 означает:

- SEQ - тип поля
- первое упоминание RF - имя метки (см. шаг 2).
- второе упоминание RF - то, что будет показано перед номером.
- 000 - формат номера (три цифры перед точкой).

5. Теперь вы можете делать ссылки на требования: На ленте Insert кнопка Cross reference:



Вуаля!

P.S. Напоследок хочу отметить, как добавляются новые требования в следующих версиях документа. Для этого я создаю отдельный раздел под названием, например, "Требования итерации 2", куда и добавляю новые требования. Подойдет ли вам такой метод - не знаю. Я использовал это в небольших проектах (до 1000 ч/часов девелопинга) - зарекомендовал себя отлично.

Начнем?

Ну вот, я сделал это. Преодолев лень, я создал мой блог.
Велком мир