動態生成SQL語句,對資料操作
這篇主要是實現了實體類的資料庫CRUD的操作。
在目前的ORM對映框架中,對於操作資料庫的最後一個步驟就是動態生成操作資料庫的SQL語句,而這無非也就是利用實體屬性中的自定義對映機制來實現的,或者就是直接把實體類的屬性名對應表的欄位名,在本示例中,就是採用的後者。
第一、為了能夠儲存動態生成的SQL語句,新建了一個SQL語句的結構類,用來存放SQL語句以及引數:
/// <summary>
/// SQL結構類
/// </summary>
[Serializable]
class SqlStruct
{
public string SqlString { get; set; }
public ParamField[] ParamFields { get; set; }
}
/// <summary>
/// SQL引數
/// </summary>
[Serializable]
public class ParamField
{
public string ParamName { get; set; }
public string FieldName{ get; set; }
}
第二、實現構建SQL操作
根據使用者的CRUD操作,動態的構建一個SQL操作語句,並別存入快取中,以便下次執行相同的操作時直接從快取中獲取,提高效能,在這裡新建了一個構建SQL的類:
class SqlGenerator
{
private SqlGenerator() { }
private static SqlGenerator singleton = new SqlGenerator();
public static SqlGenerator Instance()
{
return singleton;
}
/// <summary>
/// 構建CRUD操作SQL語句
/// </summary>
/// <param name="activeRecord"></param>
/// <param name="sqlOperationType"></param>
/// <returns></returns>
public SqlStruct GenerateSql(dynamic activeRecord, SqlOperationType sqlOperationType)
{
SqlStruct sqlStruct;
string key = null;
if (activeRecord is Type)
{
TableMapAttribute attr = Attribute.GetCustomAttribute(activeRecord, typeof(TableMapAttribute)) as TableMapAttribute;
key = GenerateKey(attr.TableName, sqlOperationType);
}
else
{
key = GenerateKey(activeRecord.TableName, sqlOperationType);
}
// 獲取快取
sqlStruct = CacheProxy.GetChchedString(key) as SqlStruct;
if (sqlStruct != null)
{
return sqlStruct;
}
switch (sqlOperationType)
{
case SqlOperationType.SimpleSelect:
sqlStruct = new SqlStruct() { SqlString = GenerateSimpleSelectSql(activeRecord)};
break;
case SqlOperationType.SelectByKey:
sqlStruct = GenerateFindByKeySql(activeRecord);
break;
case SqlOperationType.Insert:
sqlStruct = GenerateInsertSql(activeRecord);
break;
case SqlOperationType.Update:
sqlStruct = GenerateUpdateSql(activeRecord);
break;
case SqlOperationType.Delete:
sqlStruct = GenerateDeleteSql(activeRecord);
break;
default:
sqlStruct = null;
break;
}
// 增加快取
CacheProxy.CacheObjectForEver(key, sqlStruct);
return sqlStruct;
}
/// <summary>
/// 構建根據主鍵ID來查詢資料的SQL
/// </summary>
/// <param name="activeRecord"></param>
/// <returns></returns>
private SqlStruct GenerateFindByKeySql(dynamic type)
{
TableMapAttribute attr = Attribute.GetCustomAttribute(type, typeof(TableMapAttribute)) as TableMapAttribute;
return new SqlStruct()
{
SqlString = string.Format("SELECT * FROM {0} WHERE {1} = :{1}", attr.TableName, attr.PrimaryKey),
ParamFields = new ParamField[] { new ParamField() { ParamName = ":" + attr.PrimaryKey, FieldName = attr.PrimaryKey } }
};
}
/// <summary>
/// 構建查詢SQL語句
/// </summary>
/// <param name="table"></param>
/// <returns></returns>
private string GenerateSimpleSelectSql(dynamic type)
{
TableMapAttribute attr = Attribute.GetCustomAttribute(type, typeof(TableMapAttribute)) as TableMapAttribute;
return new StringBuilder("SELECT * ").Append(" FROM ").Append(attr.TableName).ToString();
}
/// <summary>
/// 構建新增SQL語句
/// </summary>
/// <param name="activeRecord"></param>
/// <returns></returns>
private SqlStruct GenerateInsertSql(dynamic activeRecord)
{
string[] columns = activeRecord.Columns;
string[] parameters = new string[activeRecord.Columns.Length];
ParamField[] paramField = new ParamField[activeRecord.Columns.Length];
for (int i = 0; i < columns.Length; i++)
{
parameters[i] = ":" + columns[i];
paramField[i] = new ParamField() { ParamName = parameters[i], FieldName = columns[i] };
}
return new SqlStruct()
{
SqlString = new StringBuilder("INSERT INTO ").Append(activeRecord.TableName).Append("(").Append(string.Join(",", columns)).Append(") VALUES(").Append(string.Join(",", parameters)).Append(")").ToString(),
ParamFields = paramField
};
}
/// <summary>
/// 構建更新SQL語句
/// </summary>
/// <param name="activeRecord"></param>
/// <returns></returns>
private SqlStruct GenerateUpdateSql(dynamic activeRecord)
{
// 得到所有的列
NameValueCollection allColumns = new NameValueCollection();
for (int i = 0; i < activeRecord.Columns.Length; i++)
{
allColumns.Add(activeRecord.Columns[i], activeRecord.Columns[i]);
}
// 去除主鍵列
allColumns.Remove(activeRecord.PrimaryKey);
string[] setString = new string[allColumns.Count];
ParamField[] paramField = new ParamField[allColumns.Count + 1];
for (int i = 0; i < allColumns.Count; i++)
{
setString[i] = new StringBuilder(allColumns[i]).Append("=:").Append(allColumns[i]).ToString();
paramField[i] = new ParamField() { ParamName = ":" + allColumns[i], FieldName = allColumns[i] };
}
string whereString = "";
whereString = new StringBuilder(activeRecord.PrimaryKey).Append("=:").Append(activeRecord.PrimaryKey).ToString();
paramField[allColumns.Count ] = new ParamField(){ ParamName=":" + activeRecord.PrimaryKey, FieldName = activeRecord.PrimaryKey};
return new SqlStruct()
{
SqlString = new StringBuilder("UPDATE ").Append(activeRecord.TableName).Append(" SET ").Append(string.Join(",", setString)).Append(" WHERE ").Append(string.Join(" AND ", whereString)).ToString(),
ParamFields = paramField
};
}
/// <summary>
/// 構建刪除SQL語句
/// </summary>
/// <param name="activeRecord"></param>
/// <returns></returns>
private SqlStruct GenerateDeleteSql(dynamic activeRecord)
{
string whereString = "";
whereString = new StringBuilder(activeRecord.PrimaryKey).Append("=:").Append(activeRecord.PrimaryKey).ToString();
ParamField paramField = new ParamField() { ParamName = ":" + activeRecord.PrimaryKey, FieldName = activeRecord.PrimaryKey };
return new SqlStruct()
{
SqlString = new StringBuilder("DELETE FROM ").Append(activeRecord.TableName).Append(" WHERE ").Append(string.Join(" AND ", whereString)).ToString(),
ParamFields = new ParamField[] { paramField }
};
}
/// <summary>
/// 建立快取Key
/// </summary>
/// <param name="tableName"></param>
/// <param name="sqlOperationType"></param>
/// <returns></returns>
private string GenerateKey(string tableName, SqlOperationType sqlOperationType)
{
return new StringBuilder(tableName).Append("__").Append(sqlOperationType.ToString()).ToString();
}
其中CRUD操作的動作,是通過一個列舉獲取的:
public enum SqlOperationType { SimpleSelect, SelectByKey, Insert, Update, Delete}
三、實現BaseActiveRecord的CRUD方法
以上就是動態生成SQL操作語句的工具類了,下面來實現BaseActiveRecord基類中的CRUD操作。
由於在從資料庫獲取到的資料需要賦值到相應的實體類屬性,在基類中新建了一個索引方法,用於給實體類屬性賦值:
/// <summary>
/// 設定或獲取屬性值
/// </summary>
/// <param name="column">欄位名</param>
/// <returns></returns>
public dynamic this[string column]
{
get
{
return this.GetType().GetProperty(column.ToLower()).GetValue(this, null);
}
set
{
PropertyInfo info = this.GetType().GetProperty(column.ToLower());
Type type = info.PropertyType;
object propertyValue;
if (type.Equals(typeof(System.Int32)))
{
propertyValue = (System.Int32.Parse(value));
}
else if (type.Equals(typeof(System.DateTime)))
{
propertyValue = (System.DateTime.Parse(value));
}
else
{
propertyValue = value;
}
this.GetType().GetProperty(column.ToLower()).SetValue(this, propertyValue, null);
}
}
利用這個索引,到時我們就可以auth["first_name"]=value的形式來賦值了。
基類中的靜態方法New的作用是用來建立一個與資料庫對映的實體類,其中泛型T是一個實體型別,在這個方法中,建立一個實體類並放入快取中
public static dynamic New<T>()
{
Type type = typeof(T);
BaseActiveRecord obj = type.Assembly.CreateInstance(type.FullName) as BaseActiveRecord;
return obj.initiation();
}
下面是CRUD對應的方法實現,其中FindById和FindAll用的是靜態方法,泛型T是實體類型別。
public void Save()
{
this[PrimaryKey] = GetPrimaryKeyValue();
SqlStruct sqlStruct = SqlGenerator.Instance().GenerateSql(this, SqlOperationType.Insert);
DataHelper helper = DataHelper.Instance();
foreach (ParamField paramField in sqlStruct.ParamFields)
{
helper.AddParameter(paramField.ParamName, this[paramField.FieldName]);
}
helper.ExecuteNonQuery(sqlStruct.SqlString);
}
public void Delete()
{
SqlStruct sqlStruct = SqlGenerator.Instance().GenerateSql(this, SqlOperationType.Delete);
DataHelper helper = DataHelper.Instance();
foreach (ParamField paramField in sqlStruct.ParamFields)
{
helper.AddParameter(paramField.ParamName, this[paramField.FieldName]);
}
helper.ExecuteNonQuery(sqlStruct.SqlString);
}
public void Update()
{
SqlStruct sqlStruct = SqlGenerator.Instance().GenerateSql(this, SqlOperationType.Update);
DataHelper helper = DataHelper.Instance();
foreach (ParamField paramField in sqlStruct.ParamFields)
{
helper.AddParameter(paramField.ParamName, this[paramField.FieldName]);
}
helper.ExecuteNonQuery(sqlStruct.SqlString);
}
public static dynamic FindById<T>(dynamic id)
{
Type type = typeof(T);
SqlStruct sqlStruct = SqlGenerator.Instance().GenerateSql(type, SqlOperationType.SelectByKey);
DataHelper helper = DataHelper.Instance();
foreach (ParamField paramField in sqlStruct.ParamFields)
{
helper.AddParameter(paramField.ParamName, id);
}
DataTable table = helper.GetDataSet(sqlStruct.SqlString).Tables[0];
EntityClassGenerator classGenerator = new EntityClassGenerator();
// 根據Type型別動態構建一個實體
dynamic activeRecord = classGenerator.GenerateEntity(type);
foreach (DataRow row in table.Rows)
{
// 給屬性賦值
foreach (string column in activeRecord.Columns)
{
activeRecord[column] = row[column].ToString();
}
break;
}
return activeRecord;
}
public static dynamic FindAll<T>()
{
Type type = typeof(T);
SqlStruct sqlStruct = SqlGenerator.Instance().GenerateSql(type, SqlOperationType.SimpleSelect);
DataHelper helper = DataHelper.Instance();
DataTable table = helper.GetDataSet(sqlStruct.SqlString).Tables[0];
EntityClassGenerator classGenerator = new EntityClassGenerator();
// 根據Type型別動態構建一個實體
dynamic activeRecord;
List<BaseActiveRecord> activeRecords = new List<BaseActiveRecord>();
foreach (DataRow row in table.Rows)
{
// 給屬性賦值
activeRecord = classGenerator.GenerateEntity(type);
foreach (string column in activeRecord.Columns)
{
activeRecord[column] = row[column].ToString();
}
activeRecords.Add(activeRecord);
}
return activeRecords;
}
四、測試CRUD操作
在main函式中,編寫程式碼測試資料庫的CRUD操作,下面是測試的程式碼:
static void Main(string[] args)
{
// 新增
dynamic auth = Author.New<Author>();
auth.first_name = "Han";
auth.last_name = "MeiMei";
auth.Save();
int keyValue = auth.id;
dynamic auth1 = Author.New<Author>();
auth1.first_name = "Li";
auth1.last_name = "Lei";
auth1.Save();
// 更新
auth.first_name = "Jim";
auth.last_name = "Green";
auth.Update();
// 根據ID獲取
dynamic updateAuth = Author.FindById<Author>(keyValue);
// 獲取所有
dynamic allAuth = Author.FindAll<Author>();
// 刪除
auth1.Delete();
Console.ReadKey(true);
}
至此,利用C#的動態型別來實現與rails類似的超程式設計的示例已經能夠完整的執行起來了。由於是示例,在以上的所有方法都並未採取異常處理的機制,如果有興趣的朋友,可以根據程式碼進一步完善的。