LINQ启用ADO.NET层运行速度较慢

时间:2014-05-09 03:03:00

标签: c# linq

我有一些数据访问代码,我Linq启用它来提高业务层的抽象级别,但性能对于大型数据集并不是很好。关于如何加快速度的任何想法?

public class StronglyTypedDataSource : IDisposable
{
    //
    private static readonly string _selectAll = "SELECT * FROM ";
    private static readonly string _insert = "INSERT INTO ";
    private static readonly string _update = "UPDATE ";
    private static readonly string _remove = "DELETE FROM ";
    private static readonly string _where = " WHERE ";
    private static readonly string _value = " VALUES ";
    private static readonly string _and = " AND ";
    private static readonly string _set = " SET ";
    private static readonly string _scopeIdentity = "SELECT LAST_INSERT_ID()";
    private static string _connectionString;       
    private MySqlConnection connection;
    private MySqlCommand command;
    private MySqlDataReader Reader;
    private int _indexer = 0;

    public StronglyTypedDataSource()
    {
        _connectionString = GetDefaultConnectionString();
    }

    /// <summary>
    /// Returns a record from a database table.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="predicate"></param>
    /// <param name="optionalTableName"></param>
    /// <returns></returns>
    public T Read<T>(Expression<Func<T, bool>> predicate, string optionalTableName = null) where T : class
    {

        T returnValue = default(T);
        CreateReadCommand<T>(predicate, optionalTableName);
        var holder = GetDataRows<T>(1).ToList();
        if(holder != default(T) && holder.Any())
            returnValue = holder[0];
        connection.Close();
        return returnValue;
    }

    /// <summary>
    /// Returns a sequence of records from a database table.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="optionalTableName"></param>
    /// <returns></returns>
    public IEnumerable<T> ReadAll<T>(string optionalTableName = null) where T : class
    {
        return ReadAll<T>(null, optionalTableName);
    }

    /// <summary>
    /// Returns a sequence of records from a database table.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="predicate"></param>
    /// <param name="optionalTableName"></param>
    /// <returns></returns>
    public IEnumerable<T> ReadAll<T>(Expression<Func<T, bool>> predicate, string optionalTableName = null) where T : class
    {          
        CreateReadCommand<T>(predicate, optionalTableName);
        var returnValues = GetDataRows<T>().ToList();
        connection.Close();
        return returnValues;
    }

    /// <summary>
    /// Delete records fromt a database table that matches the predicate.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="predicate"></param>
    /// <param name="optionalTableName"></param>
    public void Delete<T>(Expression<Func<T, bool>> predicate, string optionalTableName = null) where T : class
    {
        CreateReadCommand<T>(predicate, optionalTableName, false);
        command.ExecuteNonQuery();
    }

    /// <summary>
    /// Inserts a record into a database table.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="objectToSave"></param>
    /// <param name="optionalTableName"></param>
    /// <returns></returns>
    public int Save<T>(T objectToSave, string optionalTableName = null, bool update = false, Expression<Func<T, bool>> predicate = null) where T : class
    {
        if (!update)
            return InsertTableRow<T>(objectToSave, optionalTableName);
        else if (predicate != null)
            return UpdateTableRow<T>(objectToSave, predicate, optionalTableName);
        else
            return -1;
    }


    /// <summary>
    /// executes the specified by passing using the dictionary of parameters supplied.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="storedProcName"></param>
    /// <param name="optionalValues"></param>
    /// <returns></returns>
    public IEnumerable<T> ExecuteStoredProcdure<T>(string storedProcName, Dictionary<string, object> optionalValues) where T : class
    {          
        InitializeQueryEngine();
        command.CommandText = storedProcName;
        command.CommandType = CommandType.StoredProcedure;
        optionalValues = optionalValues ?? new Dictionary<string, object>();
        var optionalValuesEnumerator = optionalValues.GetEnumerator();
        while (optionalValuesEnumerator.MoveNext())
        {
            command.Parameters.AddWithValue(optionalValuesEnumerator.Current.Key, optionalValuesEnumerator.Current.Value);
        }
        return GetDataRows<T>();

    }

    private void InitializeQueryEngine()
    {
        connection = new MySqlConnection(_connectionString);
        command = connection.CreateCommand(); 
        if(connection.State != ConnectionState.Open)       
            connection.Open();
        _indexer = 0;
    }

    /// <summary>
    ///  Parses the expression tree.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="predicate"></param>
    /// <param name="optionalTableName"></param>
    /// <param name="isSelect"></param>
    protected internal void CreateReadCommand<T>(Expression<Func<T, bool>> predicate, string optionalTableName = null, bool isSelect = true)
    {          

        InitializeQueryEngine();
        string parametizedCommandString = String.Empty;
        string whereClause = predicate != null ? AnalyzePredicate(predicate) : String.Empty;          

        parametizedCommandString = ((isSelect ? _selectAll : _remove) + (String.IsNullOrEmpty(optionalTableName) ? typeof(T).Name : optionalTableName) + (whereClause ?? string.Empty));

        command.CommandText = parametizedCommandString;            
    }
    private static Expression<Func<T, bool>> ToExpression<T>(BinaryExpression binaryExpression)
    {
        return Expression.Lambda<Func<T, bool>>(binaryExpression, new ParameterExpression[]{ Expression.Parameter(typeof(T)) });
    }
    private string AnalyzePredicate<T>(Expression<Func<T, bool>> predicate, bool isFirstParse = true)
    {
        string whereClause = String.Empty;
        BinaryExpression expression = (BinaryExpression)predicate.Body;
        if (!(expression.NodeType == ExpressionType.Equal || expression.NodeType == ExpressionType.AndAlso))
            throw new NotSupportedException("Only == and && operator is supported");           

        if (expression.Left is BinaryExpression)
        {
            var leftExpression = (BinaryExpression)expression.Left;              
            whereClause += AnalyzePredicate<T>(ToExpression<T>((BinaryExpression)expression.Left), _indexer == 0);             
        }            

        if (expression.Right is BinaryExpression)
        {
            var rightExpression = (BinaryExpression)expression.Right;
            whereClause += AnalyzePredicate<T>(ToExpression<T>((BinaryExpression)expression.Right), _indexer == 0);
        }

        if (expression.Left is MemberExpression)
        {
            whereClause += ConvertExpressionToSQL<T>(expression, isFirstParse);
            _indexer++;
        }

        return whereClause;
    }

    private string ConvertExpressionToSQL<T>(BinaryExpression expression, bool isFirstParse)
    {
        string whereClause = String.Empty;
        if (expression.NodeType == ExpressionType.Equal)
        {
            var parsedExpression = GetLeftRightExpresion(expression);
            whereClause += (isFirstParse ? _where : _and) + parsedExpression.Item1.Member.Name + "= " + "@" + parsedExpression.Item1.Member.Name;
            if (command.Parameters != null && !command.Parameters.Contains("@" + parsedExpression.Item1.Member.Name))
                command.Parameters.AddWithValue("@" + parsedExpression.Item1.Member.Name, parsedExpression.Item2);
        }

        return whereClause;
    }

    private Tuple<MemberExpression, object> GetLeftRightExpresion(BinaryExpression expression)
    {
        object value = null;
        if (expression.Right is ConstantExpression)
            value = ((ConstantExpression)expression.Right).Value;
        else
            value = Expression.Lambda(expression.Right).Compile().DynamicInvoke();
        return new Tuple<MemberExpression, object>((MemberExpression)expression.Left, value);


    }

    protected internal IEnumerable<T> GetDataRows<T>(int maxRows = -1) where T : class
    {         
        Reader = command.ExecuteReader();
        int number_of_returned_rows = 0;
        List<T> resrtul = new List<T>();
        if (Reader.HasRows)
        {
            var returnValueProperties = typeof(T).GetProperties();
            var returnValuePropertiesEnumerator = returnValueProperties.GetEnumerator();
            while (Reader.Read())
            {
                if (maxRows != -1 && number_of_returned_rows >= maxRows)
                    break;
                T returnValue = (T)Activator.CreateInstance(typeof(T));
                while (returnValuePropertiesEnumerator.MoveNext())
                {
                    var currentProperty = (PropertyInfo)returnValuePropertiesEnumerator.Current;
                    if (Attribute.IsDefined(currentProperty, typeof(GCHDBIgnoreAttribute)))
                        continue;
                    if (Reader[currentProperty.Name].GetType().FullName == "MySql.Data.Types.MySqlDateTime")
                        currentProperty.SetValue(returnValue, DateTime.Parse(Reader[currentProperty.Name].ToString()), null);
                    else if (Reader[currentProperty.Name].GetType().FullName == "System.DBNull")
                        continue;
                    else
                        currentProperty.SetValue(returnValue, Reader[currentProperty.Name], null);
                }
                number_of_returned_rows++;                    
                yield return returnValue;
                returnValuePropertiesEnumerator.Reset();
            } 
         }
        else
            yield return default(T);
    }

    protected internal int InsertTableRow<T>(T row, string optionalTableName = null)
    {

        var returnValueProperties = typeof(T).GetProperties();
        var returnValuePropertiesEnumerator = returnValueProperties.GetEnumerator();
        StringBuilder parametizedCommandString = new StringBuilder(_insert + (String.IsNullOrEmpty(optionalTableName) ? row.GetType().Name : optionalTableName) + "( ");
        var placeHolders = new List<string>();

        InitializeQueryEngine();

        while (returnValuePropertiesEnumerator.MoveNext())
        {
            if (Attribute.IsDefined((PropertyInfo)returnValuePropertiesEnumerator.Current, typeof(GCHDBIgnoreAttribute)))
                continue;
            var currentPropertyName = ((PropertyInfo)returnValuePropertiesEnumerator.Current).Name;
            parametizedCommandString.Append(currentPropertyName);
            parametizedCommandString.Append(", ");
            placeHolders.Add("@" + currentPropertyName);
        }
        parametizedCommandString.Remove(parametizedCommandString.Length - 2, 1);           
        parametizedCommandString.Append(" )" + _value + "( ");

        for (int i = 0; i < placeHolders.Count; i++)
        {
            parametizedCommandString.Append(placeHolders[i]);
            parametizedCommandString.Append(", ");
        }
        parametizedCommandString.Remove(parametizedCommandString.Length - 2, 1);
        parametizedCommandString.Append(" )");
        returnValuePropertiesEnumerator.Reset();
        for (int i = 0; i < placeHolders.Count; i++)
        {                
            returnValuePropertiesEnumerator.MoveNext();
            if (Attribute.IsDefined((PropertyInfo)returnValuePropertiesEnumerator.Current, typeof(GCHDBIgnoreAttribute)))
                continue;
            command.Parameters.AddWithValue(placeHolders[i], ((PropertyInfo)returnValuePropertiesEnumerator.Current).GetValue(row, null));
        }
        command.CommandText = parametizedCommandString.ToString();
        command.ExecuteNonQuery();
        command.Parameters.Clear();
        command.CommandText = _scopeIdentity;
        return Convert.ToInt32(command.ExecuteScalar());

    }

    protected internal int UpdateTableRow<T>(T row, Expression<Func<T, bool>> predicate, string optionalTableName)
    {
        var returnValueProperties = typeof(T).GetProperties();
        var returnValuePropertiesEnumerator = returnValueProperties.GetEnumerator();
        StringBuilder parametizedCommandString = new StringBuilder(_update + (String.IsNullOrEmpty(optionalTableName) ? row.GetType().Name : optionalTableName) + _set);
        var placeHolders = new List<string>();

        InitializeQueryEngine();

        while (returnValuePropertiesEnumerator.MoveNext())
        {
            if (Attribute.IsDefined((PropertyInfo)returnValuePropertiesEnumerator.Current, typeof(GCHDBIgnoreAttribute)))
                continue;
            var currentPropertyName = ((PropertyInfo)returnValuePropertiesEnumerator.Current).Name;
            parametizedCommandString.Append(currentPropertyName + "= @" + currentPropertyName);
            parametizedCommandString.Append(", ");
            placeHolders.Add("@" + currentPropertyName);
        }
        parametizedCommandString.Remove(parametizedCommandString.Length - 2, 1);            
        parametizedCommandString.Append(AnalyzePredicate<T>(predicate));
        returnValuePropertiesEnumerator.Reset();
        for (int i = 0; i < placeHolders.Count; i++)
        {
            returnValuePropertiesEnumerator.MoveNext();
            if (Attribute.IsDefined((PropertyInfo)returnValuePropertiesEnumerator.Current, typeof(GCHDBIgnoreAttribute)))
                continue;
            if(command.Parameters != null && !command.Parameters.Contains(placeHolders[i]))
             command.Parameters.AddWithValue(placeHolders[i], ((PropertyInfo)returnValuePropertiesEnumerator.Current).GetValue(row, null));

        }
        command.CommandText = parametizedCommandString.ToString();
        command.ExecuteNonQuery();
        command.Parameters.Clear();

        return 0;
    }

    protected internal string GetDefaultConnectionString()
    {
        return ConfigurationManager.ConnectionStrings["TSIF_DefaultConnectionString"].ConnectionString;             
    }

    public void Dispose()
    {
        if (connection != null && connection.State == ConnectionState.Closed)
        {
            connection.Dispose();               
        }
    }

    ~StronglyTypedDataSource()
    {
        Dispose();
    }
}   

1 个答案:

答案 0 :(得分:2)

在紧密循环中使用反射总是很慢。 GetDataRows显然是你瓶颈的根源。

var returnValueProperties = typeof(T).GetProperties();
var returnValuePropertiesEnumerator = returnValueProperties.GetEnumerator();

以上几行将会非常缓慢,但你会紧紧围绕它们。

然后使用PropertyInfo.SetValue更糟糕。

显然你在Linq Expresssions中有一些技巧。因此,您应该使用Linq表达式替换该ENTIRE函数,并将其编译为委托,更重要的是,您应该高速缓存LINQ编译的结果。

考虑使用静态辅助类,使用静态构造函数初始化映射委托。

然而。这并不能解决您重新发明轮子的根本问题。您可以使用DbExecuter这样的库作为读者逻辑。哎呀,你可以轻松地使用EntityFramework或Linq to SQL。