微软的DAAB(现在叫Enterprise Library了)cache了stored procedure的parameter list,但是,实现一个功能完整的O/R mapping,还需要扩充到对数据库结构的cache,这样才能创建dynamic sql commands。
网上没有找到合适的代码,就自己写了一个,根据连接字符串,cache相应数据库,不过目前只做了工作中用到的部分,即Sql Server的table和column。
分享一下,也希望大家来完善它,方便我们的工作。若有意共享代码请赐函amy_seahawk@yahoo.com(也是msn),谢谢。
代码如下:
using System.Collections; using System.ComponentModel; using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Reflection; using System.Web; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.SessionState; namespace Athos.Data { #region class SpiColumn public class SpiColumn: IStateManager { private bool marked; private SpiTable table; private StateBag statebag; protected StateBag ViewState { get { return this.statebag; } } #region IStateManager public bool IsTrackingViewState { get { return this.marked; } } void System.Web.UI.IStateManager.LoadViewState(object state) { this.LoadViewState(state); } protected virtual void LoadViewState(object savedState) { object[] arrViewState; if (savedState != null) { arrViewState = ((object[]) savedState); if (arrViewState[0] != null) { ((IStateManager)ViewState).LoadViewState(arrViewState[0]); } } } object System.Web.UI.IStateManager.SaveViewState() { return this.SaveViewState(); } public virtual object SaveViewState() { object obj1; object[] arrViewState; obj1 = ((IStateManager)ViewState).SaveViewState(); if (obj1 != null) { arrViewState = new object[1]; arrViewState[0] = obj1; return arrViewState; } return null; } void System.Web.UI.IStateManager.TrackViewState() { this.TrackViewState(); } public virtual void TrackViewState() { this.marked = true; ((IStateManager)ViewState).TrackViewState(); } #endregion public SpiColumn() { this.statebag = new StateBag(); } internal void SetTable(SpiTable table) { this.table = table; } #region Column properties from SQL Database private string m_Description; public string Description { get { return m_Description; } set { m_Description=value; } } private string m_defaultValue; public string DefaultValue { get { return m_defaultValue; } set { m_defaultValue=value; } } private int m_identityseed; public int IdentitySeed { get { return m_identityseed; } set { m_identityseed = value; } } private int m_identityincrement; public int IdentityIncrement { get { return m_identityincrement; } set { m_identityincrement = value; } } private bool m_isidentity; public bool IsIdentity { get { return m_isidentity; } set { m_isidentity = value; } } private bool m_isidnotforrepl; public bool IsIDNotForRepl { get { return m_isidnotforrepl; } set { m_isidnotforrepl = value; } } private bool m_isnullable; public bool IsNullAble { get { return m_isnullable; } set { m_isnullable = value; } } private bool m_isprimarykey; public bool IsPrimaryKey { get { return m_isprimarykey; } set { m_isprimarykey = value; } } private int m_key_seq; public int Key_Seq { get { return m_key_seq; } set { m_key_seq = value; } } private int m_length; public int Length { get { return m_length; } set { m_length = value; } } private string m_name; public string Name { get { return m_name; } set { m_name = value; } } private string m_type; public string Type { get { return m_type; } set { m_type = value; } } private int m_xprec; public int Xprec { get { return m_xprec; } set { m_xprec = value; } } private int m_xscale; public int Xscale { get { return m_xscale; } set { m_xscale = value; } } #endregion } #endregion #region class SpiColumnCollection public sealed class SpiColumnCollection : ICollection, IStateManager { private SpiTable table; private ArrayList columns; private bool marked; #region .ctor() public SpiColumnCollection(SpiTable table, ArrayList columns) { this.table = table; this.columns = columns; } #endregion #region properties #region ICollection: Count public int Count { get { return this.columns.Count; } } #endregion public bool IsReadOnly { get { return false; } } #region ICollection: IsSynchronized public bool IsSynchronized { get { return false; } } #endregion public SpiColumn this[int index] { get { return ((SpiColumn) this.columns[index]); } } internal int indexof(string columnname) { for(int i=0;i<this.columns.Count;i++) { if(((SpiColumn) columns[i]).Name == columnname) return i; } return -1; } public SpiColumn this[String columnname] { get { int columnindex = indexof(columnname); if (columnindex < 0) return null; else return ((SpiColumn) this.columns[columnindex]); } } #region ICollection: SyncRoot public object SyncRoot { get { return this; } } #endregion #region IStateManager: LoadViewState/SaveViewState/TrackViewState/IsTrackingViewState bool System.Web.UI.IStateManager.IsTrackingViewState { get { return this.marked; } } #endregion #endregion public void Add(SpiColumn column) { this.AddAt(-1, column); } public void AddAt(int index, SpiColumn column) { if (index == -1) { this.columns.Add(column); } else { this.columns.Insert(index, column); } column.SetTable(this.table); if (this.marked) { column.TrackViewState(); } } public void Clear() { this.columns.Clear(); } #region ICollection: SyncRoot public void CopyTo(Array array, int index) { IEnumerator enumerator1; enumerator1 = this.GetEnumerator(); while (enumerator1.MoveNext()) { index = (index + 1); array.SetValue(enumerator1.Current, index); } } #endregion #region IStateManager: LoadViewState/SaveViewState/TrackViewState/IsTrackingViewState void System.Web.UI.IStateManager.LoadViewState(object state) { object[] arrViewState; int i; if (state != null) { arrViewState = ((object[]) state); if (arrViewState.Length == this.columns.Count) { for (i = 0; (i < arrViewState.Length); i = (i + 1)) { if (arrViewState[i] != null) { ((IStateManager) this.columns[i]).LoadViewState(arrViewState[i]); } } } } } object System.Web.UI.IStateManager.SaveViewState() { int intCount; object[] arrViewState; bool blOK; int i; intCount = this.columns.Count; arrViewState = new object[((uint) intCount)]; blOK = false; for (i = 0; (i < intCount); i++) { arrViewState[i] = ((IStateManager) this.columns[i]).SaveViewState(); if (arrViewState[i] != null) { blOK = true; } } if (!blOK) { return null; } return arrViewState; } void System.Web.UI.IStateManager.TrackViewState() { int intCount; int i; this.marked = true; intCount = this.columns.Count; for (i = 0; (i < intCount); i = (i + 1)) { ((IStateManager) this.columns[i]).TrackViewState(); } } #endregion #region IEnumerable: GetEnumerator public IEnumerator GetEnumerator() { return this.columns.GetEnumerator(); } #endregion public int IndexOf(SpiColumn column) { if (column != null) { return this.columns.IndexOf(column); } return -1; } public void Remove(SpiColumn column) { int num1; num1 = this.IndexOf(column); if (num1 >= 0) { this.RemoveAt(num1); } } public void RemoveAt(int index) { if ((index >= 0) && (index < this.Count)) { this.columns.RemoveAt(index); return; } throw new ArgumentOutOfRangeException("index"); } } #endregion #region class SpiTable public class SpiTable: IStateManager { #region properties private bool marked; private string tablename; public String TableName { get { return tablename; } set { tablename = value; } } private int keycount; public int KeyCount { get { return keycount; } set { keycount = value; } } private StateBag statebag; protected StateBag ViewState { get { return this.statebag; } } #endregion public SpiTable(String tablename) { this.statebag = new StateBag(); this.tablename = tablename; } #region IStateManager public bool IsTrackingViewState { get { return this.marked; } } void System.Web.UI.IStateManager.LoadViewState(object state) { this.LoadViewState(state); } protected virtual void LoadViewState(object savedState) { object[] arrViewState; if (savedState != null) { arrViewState = ((object[]) savedState); if (arrViewState[0] != null) { ((IStateManager)ViewState).LoadViewState(arrViewState[0]); } } } object System.Web.UI.IStateManager.SaveViewState() { return this.SaveViewState(); } public virtual object SaveViewState() { object obj1; object[] arrViewState; obj1 = ((IStateManager)ViewState).SaveViewState(); if (obj1 != null) { arrViewState = new object[1]; arrViewState[0] = obj1; return arrViewState; } return null; } void System.Web.UI.IStateManager.TrackViewState() { this.TrackViewState(); } public virtual void TrackViewState() { this.marked = true; ((IStateManager)ViewState).TrackViewState(); } #endregion private SpiDatabase database; internal void SetDatabase(SpiDatabase database) { this.database = database; } private SpiColumnCollection ColumnsCollection; private ArrayList fields; public virtual SpiColumnCollection Columns { get { if (this.ColumnsCollection == null) { this.fields = new ArrayList(); this.ColumnsCollection = new SpiColumnCollection(this, this.fields); if (true) //athos: later need to change to if(base.IsTrackingViewState)... { ((IStateManager)ColumnsCollection).TrackViewState(); } } return this.ColumnsCollection; } } /// <summary> /// Resolve at run time the appropriate set of SqlParameters for a stored procedure /// </summary> /// <param name="connection">A valid SqlConnection object</param> /// <param name="spName">The name of the stored procedure</param> /// <param name="includeReturnValueParameter">Whether or not to include their return value parameter</param> /// <returns>The parameter array discovered.</returns> public void DiscoverTableSchema() { #region get column list DataSet dsColumnList = Athos.Data.SqlHelper.ExecuteDataset ( database.ConnectionString, CommandType.Text, string.Concat ( @"SELECT c.[id], c.colid, c.[name], t.[name] type, c.length, c.isnullable, c.xprec, c.xscale, (select m.[text] from dbo.syscomments m where m.[id]=c.cdefault) defaultvalue, (select p.[value] from dbo.sysproperties p where p.id=c.id and p.smallid=c.colid) description FROM dbo.syscolumns c, dbo.systypes t WHERE (c.id = OBJECT_ID('", TableName, @"')) AND (c.name <> 'dtproperties') AND c.xusertype = t.xusertype ORDER BY c.colid" ) ); SpiColumn column; foreach(DataRow dr in dsColumnList.Tables[0].Rows) { column = new SpiColumn(); Columns.Add(column); column.DefaultValue = dr["defaultvalue"].ToString(); column.Description = dr["description"].ToString(); column.IsNullAble = Convert.ToBoolean(dr["isnullable"]); column.Length = Convert.ToInt32(dr["length"]); column.Name = dr["name"].ToString(); column.Type = dr["type"].ToString(); column.Xprec = Convert.ToInt32(dr["xprec"]); column.Xscale = Convert.ToInt32(dr["xscale"]); } #endregion #region get primary keys DataSet dsPKeys = Athos.Data.SqlHelper.ExecuteDataset ( database.ConnectionString, CommandType.Text, string.Concat("sp_pkeys '", TableName, "'") ); String strColName; foreach(DataRow dr in dsPKeys.Tables[0].Rows) { strColName = dr["COLUMN_NAME"].ToString(); foreach(SpiColumn col in Columns) { if(col.Name == strColName) { col.IsPrimaryKey = true; col.Key_Seq = Convert.ToInt32(dr["KEY_SEQ"]); } } } this.KeyCount = dsPKeys.Tables[0].Rows.Count; #endregion #region get identity column, if any DataSet dsIdentity = Athos.Data.SqlHelper.ExecuteDataset ( database.ConnectionString, CommandType.Text, string.Concat( @"declare @objname as nvarchar(776) set @objname = '", TableName, @"' declare @objid int select @objid = id from sysobjects where id = object_id(@objname) declare @colname sysname select @colname = name from syscolumns where id = @objid and colstat & 1 = 1 select 'Identity' = isnull(@colname,'No identity column defined.'), 'IdentitySeed' = ident_seed(@objname), 'IdentityIncrement' = ident_incr(@objname), 'NotForReplication' = ColumnProperty(@objid, @colname, 'IsIDNotForRepl')" ) ); String strIdentityCol = dsIdentity.Tables[0].Rows[0]["Identity"].ToString(); if(strIdentityCol != "No identity column defined.") { foreach(SpiColumn col in Columns) { if(col.Name == strIdentityCol) { col.IsIdentity = true; col.IdentityIncrement = Convert.ToInt32(dsIdentity.Tables[0].Rows[0]["IdentityIncrement"]); col.IdentitySeed = Convert.ToInt32(dsIdentity.Tables[0].Rows[0]["IdentitySeed"]); col.IsIDNotForRepl = (Convert.ToInt32(dsIdentity.Tables[0].Rows[0]["NotForReplication"])==1); } } } #endregion } } #endregion #region class SpiTableCollection public sealed class SpiTableCollection : ICollection, IStateManager { private SpiDatabase database; private ArrayList tables; private bool marked; #region .ctor() public SpiTableCollection(SpiDatabase database, ArrayList tables) { this.database = database; this.tables = tables; } #endregion #region properties #region ICollection: Count public int Count { get { return this.tables.Count; } } #endregion public bool IsReadOnly { get { return false; } } #region ICollection: IsSynchronized public bool IsSynchronized { get { return false; } } #endregion public SpiTable this[int index] { get { return ((SpiTable) this.tables[index]); } } internal int indexof(string tablename) { for(int i=0;i<this.tables.Count;i++) { if(((SpiTable) tables[i]).TableName == tablename) return i; } return -1; } public SpiTable this[String tablename] { get { int tableindex = indexof(tablename); if (tableindex < 0) return null; else return ((SpiTable) this.tables[tableindex]); } } #region ICollection: SyncRoot public object SyncRoot { get { return this; } } #endregion #region IStateManager: LoadViewState/SaveViewState/TrackViewState/IsTrackingViewState bool System.Web.UI.IStateManager.IsTrackingViewState { get { return this.marked; } } #endregion #endregion public void Add(SpiTable table) { this.AddAt(-1, table); } public void AddAt(int index, SpiTable table) { if (index == -1) { this.tables.Add(table); } else { this.tables.Insert(index, table); } table.SetDatabase(this.database); if (this.marked) { table.TrackViewState(); } } public void Clear() { this.tables.Clear(); } #region ICollection: SyncRoot public void CopyTo(Array array, int index) { IEnumerator enumerator1; enumerator1 = this.GetEnumerator(); while (enumerator1.MoveNext()) { index = (index + 1); array.SetValue(enumerator1.Current, index); } } #endregion #region IStateManager: LoadViewState/SaveViewState/TrackViewState/IsTrackingViewState void System.Web.UI.IStateManager.LoadViewState(object state) { object[] arrViewState; int i; if (state != null) { arrViewState = ((object[]) state); if (arrViewState.Length == this.tables.Count) { for (i = 0; (i < arrViewState.Length); i = (i + 1)) { if (arrViewState[i] != null) { ((IStateManager) this.tables[i]).LoadViewState(arrViewState[i]); } } } } } object System.Web.UI.IStateManager.SaveViewState() { int intCount; object[] arrViewState; bool blOK; int i; intCount = this.tables.Count; arrViewState = new object[((uint) intCount)]; blOK = false; for (i = 0; (i < intCount); i++) { arrViewState[i] = ((IStateManager) this.tables[i]).SaveViewState(); if (arrViewState[i] != null) { blOK = true; } } if (!blOK) { return null; } return arrViewState; } void System.Web.UI.IStateManager.TrackViewState() { int intCount; int i; this.marked = true; intCount = this.tables.Count; for (i = 0; (i < intCount); i = (i + 1)) { ((IStateManager) this.tables[i]).TrackViewState(); } } #endregion #region IEnumerable: GetEnumerator public IEnumerator GetEnumerator() { return this.tables.GetEnumerator(); } #endregion public int IndexOf(SpiTable table) { if (table != null) { return this.tables.IndexOf(table); } return -1; } public void Remove(SpiTable table) { int num1; num1 = this.IndexOf(table); if (num1 >= 0) { this.RemoveAt(num1); } } public void RemoveAt(int index) { if ((index >= 0) && (index < this.Count)) { this.tables.RemoveAt(index); return; } throw new ArgumentOutOfRangeException("index"); } } #endregion #region class SpiDatabase public class SpiDatabase: IStateManager { #region private members: marked, stagebag, ViewState, ConnectionString private bool marked; private StateBag statebag; protected StateBag ViewState { get { return this.statebag; } } private string connectionstring; public String ConnectionString { get { return connectionstring; } set { connectionstring = value; } } #endregion public SpiDatabase(string connectionstring) { this.statebag = new StateBag(); this.connectionstring = connectionstring; } #region IStateManager public bool IsTrackingViewState { get { return this.marked; } } void System.Web.UI.IStateManager.LoadViewState(object state) { this.LoadViewState(state); } protected virtual void LoadViewState(object savedState) { object[] arrViewState; if (savedState != null) { arrViewState = ((object[]) savedState); if (arrViewState[0] != null) { ((IStateManager)ViewState).LoadViewState(arrViewState[0]); } } } object System.Web.UI.IStateManager.SaveViewState() { return this.SaveViewState(); } public virtual object SaveViewState() { object obj1; object[] arrViewState; obj1 = ((IStateManager)ViewState).SaveViewState(); if (obj1 != null) { arrViewState = new object[1]; arrViewState[0] = obj1; return arrViewState; } return null; } void System.Web.UI.IStateManager.TrackViewState() { this.TrackViewState(); } public virtual void TrackViewState() { this.marked = true; ((IStateManager)ViewState).TrackViewState(); } #endregion private SpiDatabase database; internal void SetDatabase(SpiDatabase database) { this.database = database; } private SpiTableCollection TablesCollection; private ArrayList tables; public virtual SpiTableCollection Tables { get { if (this.TablesCollection == null) { this.tables = new ArrayList(); this.TablesCollection = new SpiTableCollection(this, this.tables); if (true) //athos: later need to change to if(base.IsTrackingViewState)... { ((IStateManager)TablesCollection).TrackViewState(); } } return this.TablesCollection; } } } #endregion #region class SpiDatabaseCache /// <summary> /// SpiDatabaseCache provides functions to leverage a static cache of procedure parameters, and the /// ability to discover parameters for stored procedures at run-time. /// </summary> public sealed class SpiDatabaseCache { #region private methods, variables, and constructors //Since this class provides only static methods, make the default constructor private to prevent //instances from being created with "new SpiDatabaseCache()" private SpiDatabaseCache() {} private static Hashtable schemaCache = Hashtable.Synchronized(new Hashtable()); public static bool CheckConnectionString(String strConnectionString) { SqlConnection sqlConn = new SqlConnection(strConnectionString); try { sqlConn.Open(); sqlConn.Close(); return true; } catch(Exception ex) { String strEx = ex.Message, strEy; strEy = strEx; strEx = strEy; return false; } } /// <summary> /// Resolve at run time the appropriate set of SqlParameters for a stored procedure /// </summary> /// <param name="connection">A valid SqlConnection object</param> /// <param name="spName">The name of the stored procedure</param> /// <param name="includeReturnValueParameter">Whether or not to include their return value parameter</param> /// <returns>The parameter array discovered.</returns> public static SpiDatabase DiscoverDatabaseSchema(SqlConnection sqlConn) { SpiDatabase database = new SpiDatabase(sqlConn.ConnectionString); DataSet dsTableList = Athos.Data.SqlHelper.ExecuteDataset(sqlConn, CommandType.Text, "SELECT name FROM sysobjects WHERE xtype='u' and name<>'dtproperties' ORDER BY name"); String strTableName = ""; SpiTable table; foreach(DataRow dr in dsTableList.Tables[0].Rows) { strTableName = dr["name"].ToString(); table = new SpiTable(strTableName); database.Tables.Add(table); table.DiscoverTableSchema(); } return database; } #endregion private methods, variables, and constructors #region caching functions /// <summary> /// Add Database Schema to the cache /// </summary> /// <param name="connectionString">A valid connection string for a SqlConnection</param> /// <param name="dsDatabaseSchema">A Database containing Database schema</param> public static void CacheDatabaseSchema(string connectionString, SpiDatabase dsDatabaseSchema) { if( connectionString == null || connectionString.Length == 0 ) throw new ArgumentNullException( "connectionString" ); string hashKey = connectionString; schemaCache[hashKey] = dsDatabaseSchema; } /// <summary> /// Retrieve a parameter array from the cache /// </summary> /// <param name="connectionString">A valid connection string for a SqlConnection</param> /// <param name="commandText">The stored procedure name or T-SQL command</param> /// <returns>An array of SqlParamters</returns> public static SpiDatabase GetCachedDatabaseSchema(string connectionString) { if( connectionString == null || connectionString.Length == 0 ) throw new ArgumentNullException( "connectionString" ); string hashKey = connectionString ; SpiDatabase cachedDatabaseSchema = schemaCache[hashKey] as SpiDatabase; if (cachedDatabaseSchema == null) { return null; } else { return (cachedDatabaseSchema); } } #endregion caching functions #region Database Schema Discovery Functions /// <summary> /// Retrieves the Database Schema /// </summary> /// <remarks> /// This method will query the database for this information, and then store it in a cache for future requests. /// </remarks> /// <param name="connectionString">A valid connection string for a SqlConnection</param> /// <returns>An DataSet showing Database Schema</returns> public static SpiDatabase GetDatabaseSchema(string connectionString) { if( connectionString == null || connectionString.Length == 0 ) throw new ArgumentNullException( "connectionString" ); using(SqlConnection connection = new SqlConnection(connectionString)) { return GetDatabaseSchemaInternal(connection); } } /// <summary> /// Retrieves the set of SqlParameters appropriate for the stored procedure /// </summary> /// <remarks> /// This method will query the database for this information, and then store it in a cache for future requests. /// </remarks> /// <param name="connection">A valid SqlConnection object</param> /// <param name="spName">The name of the stored procedure</param> /// <returns>An array of SqlParameters</returns> internal static SpiDatabase GetDatabaseSchema(SqlConnection connection) { if( connection == null ) throw new ArgumentNullException( "connection" ); using (SqlConnection clonedConnection = (SqlConnection)((ICloneable)connection).Clone()) { return GetDatabaseSchemaInternal(clonedConnection); } } /// <summary> /// Retrieves the DataSet of Database Schema appropriate /// </summary> /// <param name="connection">A valid SqlConnection object</param> /// <returns>An DataSet of Database Schema</returns> private static SpiDatabase GetDatabaseSchemaInternal(SqlConnection connection) { if( connection == null ) throw new ArgumentNullException( "connection" ); string hashKey = connection.ConnectionString; SpiDatabase cachedDatabaseSchema; cachedDatabaseSchema = schemaCache[hashKey] as SpiDatabase; if (cachedDatabaseSchema == null) { SpiDatabase dsDatabaseSchema = DiscoverDatabaseSchema(connection); schemaCache[hashKey] = dsDatabaseSchema; cachedDatabaseSchema = dsDatabaseSchema; } return cachedDatabaseSchema; } #endregion Database Schema Discovery Functions } #endregionusing System; }
athossmth原创,转载请注明。