Linq To Sql на удивление быстро получает данные.Это нормально, что он в 10 раз быстрее, чем ADO? - PullRequest
5 голосов
/ 18 февраля 2012

В настоящее время я изучаю Linq to Sql, и я очень удивлен эффективностью выбора данных. Я извлекаю объединенные данные из нескольких таблиц. Я выбираю около 40 тысяч строк. Сопоставление этих данных с объектами с использованием ADO раз около 35 с, NHbiernate раз около 130 с, а что подозрительно с использованием Linq To Sql всего 3,5 с. Кроме того, я хотел бы написать, что я использую немедленную загрузку, которая выглядит следующим образом:

THESIS th = new THESIS(connectionString);
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<NumericFormula>(x => x.RPN);
dlo.LoadWith<RPN>(x => x.RPNDetails);
dlo.LoadWith<RPNDetail>(x => x.Parameter);
th.LoadOptions = dlo;
th.Log = Console.Out;

Просматривая журналы во время итерации, я не вижу, чтобы Linq To Sql генерировал дополнительные запросы к базе данных.

Я очень удивлен огромными различиями в производительности, и мне интересно, может быть, я чего-то не понимаю.

Может ли кто-нибудь объяснить мне, почему это работает так быстро? Для измерения времени я использую класс Секундомер.

Код ADO.NET:

public static List<NumericFormulaDO> SelectAllNumericFormulas()
{
    var nFormulas = new List<NumericFormulaDO>();

    string queryString = @"
        SELECT *
        FROM    NumericFormula nf 
                Left Join Unit u on u.Unit_Id = nf.Unit_Id 
                Left Join UnitType ut on ut.UnitType_Id = u.UnitType_Id 
                Join RPN r on r.RPN_Id = nf.RPN_Id 
                Join RPNDetails rd on rd.RPN_Id = r.RPN_Id 
                Join Parameter par on par.Parameter_Id = rd.Parameter_Id where nf.NumericFormula_Id<=10000";

    using (var connection = new SqlConnection(connectionString))
    {
        var command = new SqlCommand(queryString, connection);
        connection.Open();
        using (var reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                var det = new RPNDetailsDO();
                det.RPNDetails_Id = Int32.Parse(reader["RPNDetails_Id"].ToString());
                det.RPN_Id = Int32.Parse(reader["RPN_Id"].ToString());
                det.Identifier = reader["Identifier"].ToString();
                det.Parameter.Architecture = reader["Architecture"].ToString();
                det.Parameter.Code = reader["Code"].ToString();
                det.Parameter.Description = reader["Description"].ToString();
                det.Parameter.Parameter_Id = Int32.Parse(reader["Parameter_Id"].ToString());
                det.Parameter.ParameterType = reader["ParameterType"].ToString();
                det.Parameter.QualityDeviationLevel = reader["QualityDeviationLevel"].ToString();

                if (nFormulas.Count > 0)
                {
                     if (nFormulas.Any(x => x.RPN.RPN_Id == Int32.Parse(reader["RPN_Id"].ToString())))
                     {
                         nFormulas.First(x=>x.RPN.RPN_Id == Int32.Parse(reader["RPN_Id"].ToString())).RPN.RPNDetails.Add(det);
                     }
                     else
                     {
                         NumericFormulaDO nFormula = CreatingNumericFormulaDO(reader, det);
                         nFormulas.Add(nFormula);
                         //System.Diagnostics.Trace.WriteLine(nFormulas.Count.ToString());
                     }
                }
                else
                {
                     NumericFormulaDO nFormula = CreatingNumericFormulaDO(reader, det);
                     nFormulas.Add(nFormula);
                     //System.Diagnostics.Trace.WriteLine(nFormulas.Count.ToString());
                }
            }
        }
    }

    return nFormulas;
}

private static NumericFormulaDO CreatingNumericFormulaDO(SqlDataReader reader, RPNDetailsDO det)
{
    var nFormula = new NumericFormulaDO();
    nFormula.CalculateDuringLoad = Boolean.Parse(reader["CalculateDuringLoad"].ToString());
    nFormula.NumericFormula_Id = Int32.Parse(reader["NumericFormula_Id"].ToString());
    nFormula.RPN.RPN_Id = Int32.Parse(reader["RPN_Id"].ToString());
    nFormula.RPN.Formula = reader["Formula"].ToString();
    nFormula.Unit.Name = reader["Name"].ToString();

    if (reader["Unit_Id"] != DBNull.Value)
    {
        nFormula.Unit.Unit_Id = Int32.Parse(reader["Unit_Id"].ToString());
        nFormula.Unit.UnitType.Type = reader["Type"].ToString();
        nFormula.Unit.UnitType.UnitType_Id = Int32.Parse(reader["UnitType_Id"].ToString());
    }
    nFormula.RPN.RPNDetails.Add(det);
    return nFormula;
}

LINQ to SQL Code:

THESIS th = new THESIS(connectionString);
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<NumericFormula>(x => x.RPN);
dlo.LoadWith<RPN>(x => x.RPNDetails);
dlo.LoadWith<RPNDetail>(x => x.Parameter);
th.LoadOptions = dlo;
th.Log = Console.Out;
var nFormulas = 
    th.NumericFormulas.ToList<NumericFormula>();

Код NHibernate:

IQueryable<NumericFormulaDO> nFormulas =
    session.Query<NumericFormulaDO>()
        .Where(x=>x.NumericFormula_Id <=10000);

List<NumericFormulaDO> nForList =
    new List<NumericFormulaDO>();

nForList = nFormulas.ToList<NumericFormulaDO>();

В отношении ваших комментариев вы можете видеть, что в ADO я использую SqlReader, а в LINQ я пытаюсь использовать немедленное выполнение.

Конечно, возможно, что мой "алгоритм" отображения в части ADO не очень хорош, но NHibernate намного медленнее, чем ADO (в 4 раза медленнее), поэтому я задаюсь вопросом, все ли в порядке в части LINQ to SQL, потому что я думаю в NHibernate все хорошо и, в конце концов, гораздо медленнее, чем небольшая запутанная часть ADO.

Спасибо, ребята, за ответы.

Ответы [ 2 ]

7 голосов
/ 18 февраля 2012

LINQ-to-SQL использует ADO.NET и имеет дополнительные накладные расходы, поэтому нет: он не должен быть быстрее, если он не выполняет ту же работу.Было упоминание о доступе через ординалы против имен, но, откровенно говоря, это влияет на микросекунды, а не секунды.Это не объясняет порядок изменения величины.

Единственный способ ответить на этот вопрос - отследить, что делает LINQ-to-SQL.К счастью, это просто - вы можете просто сделать:

dbContext.Log = Console.Out;

, который будет записывать TSQL, выполняемый на консоль.Тогда есть два варианта:

  1. вы обнаружите, что TSQL не делает то же самое (возможно, это не полная загрузка)
  2. вы обнаружите, что TSQL действителен (=делает то же самое), но имеет лучший план - в этом случае ... «заимствуйте» его: p

Как только у вас будет TSQL для сравнения, протестируйте , что side-рядом, так что вы тестируете ту же работу.Если вам нужно удобство без накладных расходов, я бы посмотрел на «изящный» - убирает скучную тяжелую работу по отображению читателей на объекты, но очень оптимизированную.

0 голосов
/ 20 февраля 2012

Переписать код ADO.NET на основе приведенных выше замечаний, это должно быть намного быстрее. Вы все еще можете улучшить, используя порядковое значение вместо имен столбцов и читая поля в том же порядке, что и в запросе, но это микрооптимизации.

Я также удалил пару дубликатов. Да Вы также можете проверить, как повысить производительность приведения типов и преобразований , поскольку маршрут Parse (ToString) очень неэффективен и может вызвать очень странные проблемы при работе с системами, работающими на разных языках. Существует также вероятность потери данных при выполнении этих преобразований, когда используются десятичные числа, числа с плавающей запятой или числа с двойными числами, поскольку не все их значения могут быть правильно переведены в строки (или не могут выполнить возврат в обратном направлении).

public static List<NumericFormulaDO> SelectAllNumericFormulas()
{
    var nFormulas = new Dictionary<int, NumericFormulaDO>();

    string queryString = @"
    SELECT *
    FROM    NumericFormula nf 
            Left Join Unit u on u.Unit_Id = nf.Unit_Id 
            Left Join UnitType ut on ut.UnitType_Id = u.UnitType_Id 
            Join RPN r on r.RPN_Id = nf.RPN_Id 
            Join RPNDetails rd on rd.RPN_Id = r.RPN_Id 
            Join Parameter par on par.Parameter_Id = rd.Parameter_Id where nf.NumericFormula_Id<=10000";

    using (var connection = new SqlConnection(connectionString))
    {  
        connection.Open();
        using (var command = new SqlCommand(queryString, connection));
        using (var reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                var det = new RPNDetailsDO();
                det.RPNDetails_Id = (int) reader.GetValue("RPNDetails_Id");
                det.RPN_Id = (int) reader.GetValue("RPN_Id");
                det.Identifier = (string) reader.GetValue("Identifier");
                det.Parameter.Architecture = (string)reader.GetValue("Architecture");
                det.Parameter.Code = (string)reader.GetValue("Code");
                det.Parameter.Description = (string)reader.GetValue("Description");
                det.Parameter.Parameter_Id = (int) reader.GetValue("Parameter_Id");
                det.Parameter.ParameterType = (string)reader.GetValue("ParameterType");
                det.Parameter.QualityDeviationLevel = (string)reader.GetValue("QualityDeviationLevel");

                NumericFormulaDO parent = null;
                if (!nFormulas.TryGetValue((int)reader.GetValue("RPN_Id"), out parent)
                {
                    parent = CreatingNumericFormulaDO(reader, det);
                    nFormulas.Add(parent.RPN.RPNID, parent);
                }
                else
                {
                    parent.RPN.RPNDetails.Add(det);
                }
            }
        }
    }

    return nFormulas.Values.ToList();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...