Как сопоставить DataReader со свойствами класса и поддерживать производительность? - PullRequest
7 голосов
/ 26 мая 2011

Преамбула:

  1. Все строки подключения данных, соединения и т. д. создаются с использованием DbProviderFactories.
  2. Код смешан C # и VB.Net из несколько библиотек.

Я сопоставляю DbDataReader с сущностями и у меня есть несколько тестов:

[0] retrieved 159180 records in 45135 ms
[1] retrieved 159180 records in 45008 ms
[2] retrieved 159180 records in 44814 ms
[3] retrieved 159180 records in 44987 ms
[4] retrieved 159180 records in 44914 ms
[5] retrieved 159180 records in 45224 ms
[6] retrieved 159180 records in 45829 ms
[7] retrieved 159180 records in 60762 ms
[8] retrieved 159180 records in 52128 ms
[9] retrieved 159180 records in 47982 ms  

Это значительное количество времени и крайне плохое, учитывая, что запрос из Sql Server Management Studio занимает всего 17 секунд. Мое избранное заявление:

«ВЫБРАТЬ * ИЗ tbl_MyTable»

Таблица содержит 43 поля и, вероятно, не проиндексирована так хорошо, как следовало бы; однако, выполняя select all , я не ожидал бы, что индексирование будет проблематичным. Итак ... вот что я делаю:

Определите сущность:

public class Concept
{
    #region Columns
    [DataParameter("ConceptID", DbType.Int32)]
    public Int32 ConceptID
    { get; set; }
    [DataParameter("ConceptName", DbType.String)]
    public string ConceptName
    { get; set; }
    [DataParameter("ConceptTypeID", DbType.Int32)]
    public Int32 ConceptTypeID
    { get; set; }
    [DataParameter("ActiveYN", DbType.Boolean)]
    public bool ActiveYN
    { get; set; }
    #endregion
}

Запрос DataReader:

for (int i = 0; i <= 99; i++)
{
    sw.Start();
    var results = session.QueryReader<Concept>(
        new SqlCommand(command), dr => new Concept());

    sw.Stop();

    Console.WriteLine("[{0}] retrieved {1} records in {2} ms", i, results.Count(), sw.ElapsedMilliseconds);
    sw.Reset();
}

... звонить:

Public Function QueryReader(Of TEntity As {Class, New})(ByVal Command As DbCommand, _
                                                        ByVal Projection As Func(Of DbDataReader, TEntity)) _
                                                        As IEnumerable(Of TEntity)

    Dim list As IEnumerable(Of TEntity)

    Command.Connection = dataReader.NewConnection
    Command.Connection.Open()

    Using _reader As DbDataReader = Command.ExecuteReader()
        list = _reader.Query(Of TEntity)(Projection).ToList()
    End Using

    Command.Connection.Close()

    Return list
End Function

... и метод расширения QueryReader<T>: изменить размещение нового TEntity () - спасибо @ Henk

public static IEnumerable<TEntity> Query<TEntity>(this DbDataReader Reader,
    Func<DbDataReader, TEntity> Projection)
    where TEntity : class, new()
{
    //   moving this reflection to another class
    Dictionary<string, PropertyInfo> props;

    while (Reader.Read())
    {
        TEntity entity = new TEntity();

        if (!entities.TryGetValue(typeof(TEntity).ToString(), out props))
        {
            //  reflection over TEntity
            props = (from p in entity.GetType().GetProperties()
                     from a in p.GetCustomAttributes(typeof(DataParameterAttribute), false)
                     select p)
                     .ToDictionary(p => p.Name);

            entities.Add(typeof(TEntity).ToString(), props);
        }

        foreach (KeyValuePair<string, PropertyInfo> field in props)
        {
            if (null != Reader[field.Key] && Reader[field.Key] != DBNull.Value)
            { field.Value.SetValue(entity, Reader[field.Key], null); }
        }

        yield return entity;
    }
}

Будем весьма благодарны за любые предложения по повышению производительности ...


Обновление

Я реализовал dapper-dot-net, как предложил @EtienneT - вот время получения:

[0] retrieved 159180 records in 6874 ms
[1] retrieved 159180 records in 6866 ms
[2] retrieved 159180 records in 6570 ms
[3] retrieved 159180 records in 6785 ms
[4] retrieved 159180 records in 6693 ms
[5] retrieved 159180 records in 6735 ms
[6] retrieved 159180 records in 6627 ms
[7] retrieved 159180 records in 6739 ms
[8] retrieved 159180 records in 6569 ms
[9] retrieved 159180 records in 6666 ms

1 Ответ

6 голосов
/ 26 мая 2011

Рассматривали ли вы микро ORM как dapper.net?

https://github.com/StackExchange/dapper-dot-net

Это сделано разработчиками StackOverflow и отображает SQL-запрос непосредственно на ваши объекты. Он генерирует и кэширует IL-код для сопоставления результатов SQL с вашими объектами. Таким образом, код IL генерируется только один раз для каждого типа. Никогда не использовал это, но если вам нужна производительность для сопоставления результатов SQL с объектами .net, вам нужна библиотека.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...