我有一些数据访问代码,我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();
}
}
答案 0 :(得分:2)
在紧密循环中使用反射总是很慢。 GetDataRows
显然是你瓶颈的根源。
var returnValueProperties = typeof(T).GetProperties();
var returnValuePropertiesEnumerator = returnValueProperties.GetEnumerator();
以上几行将会非常缓慢,但你会紧紧围绕它们。
然后使用PropertyInfo.SetValue
更糟糕。
显然你在Linq Expresssions中有一些技巧。因此,您应该使用Linq表达式替换该ENTIRE函数,并将其编译为委托,更重要的是,您应该高速缓存LINQ编译的结果。
考虑使用静态辅助类,使用静态构造函数初始化映射委托。
然而。这并不能解决您重新发明轮子的根本问题。您可以使用DbExecuter这样的库作为读者逻辑。哎呀,你可以轻松地使用EntityFramework或Linq to SQL。