using System; using System.Data; using SubSonic.Utilities; using System.Text; using System.Collections.Generic; using System.Web.UI.WebControls; namespace SubSonic { [Serializable] public abstract class ActiveRecord : AbstractRecord, IActiveRecord where T : AbstractRecord, new() { private List loaders; public void RegisterSingleObjectLoader(SingleObjectLoader loader) { this.RegisterSingleObjectLoader(loader, null); } public void RegisterSingleObjectLoader(SingleObjectLoader loader, SingleObjectLoadedCallback callback) { if(this.loaders == null) { this.loaders = new List(); } this.loaders.Add(new LoaderCallbackPair(loader, callback)); } protected T LoadSingleObject(object id, FetchByDelegate fallbackLoader) where T : class, IActiveRecord { return LoadSingleObject(id, Loaders.ConvertToSingleObjectLoader(fallbackLoader)); } protected T LoadSingleObject(object id, SingleObjectLoader fallbackLoader) where T : class, IActiveRecord { return Loaders.LoadSingleObject(id, this.loaders, fallbackLoader, this); } protected ActiveRecord() { MarkNew(); } #region Fetchers /// /// Returns a record for this keyValue /// /// Key Value /// public static T FetchByID(int keyValue) { return fetchByID(keyValue); } /// /// Returns a record for this keyValue /// /// Key Value /// public static T FetchByID(int? keyValue) { return fetchByID(keyValue); } /// /// Returns a record for this keyValue /// /// Key Value /// public static T FetchByID(short keyValue) { return fetchByID(keyValue); } /// /// Returns a record for this keyValue /// /// Key Value /// public static T FetchByID(short? keyValue) { return fetchByID(keyValue); } /// /// Returns a record for this keyValue /// /// Key Value /// public static T FetchByID(long keyValue) { return fetchByID(keyValue); } /// /// Returns a record for this keyValue /// /// Key Value /// public static T FetchByID(long? keyValue) { return fetchByID(keyValue); } /// /// Returns a record for this keyValue /// /// Key Value /// public static T FetchByID(decimal keyValue) { return fetchByID(keyValue); } /// /// Returns a record for this keyValue /// /// Key Value /// public static T FetchByID(decimal? keyValue) { return fetchByID(keyValue); } /// /// Returns a record for this keyValue /// /// Key Value /// public static T FetchByID(Guid keyValue) { return fetchByID(keyValue); } /// /// Returns a record for this keyValue /// /// Key Value /// public static T FetchByID(Guid? keyValue) { return fetchByID(keyValue); } /// /// Returns a record for this keyValue /// /// Key Value /// public static T FetchByID(string keyValue) { return fetchByID(keyValue); } /// /// Returns a record for this keyValue /// /// Key Value /// private static T fetchByID(object keyValue) { if (keyValue == null) { return null; } //makes sure the table schema is loaded T item = new T(); //build the query Query q = new Query(BaseSchema).WHERE(BaseSchema.PrimaryKey.ColumnName, Comparison.Equals, keyValue); //load the reader using (IDataReader rdr = DataService.GetSingleRecordReader(q.BuildSelectCommand())) { if(rdr.Read()) { item.Load(rdr); } rdr.Close(); } if (!item._isNew && item.IsLoaded) return item; return null; } /// /// Returns all records for this table /// /// IDataReader public static IDataReader FetchAll() { Query q = new Query(BaseSchema); CheckLogicalDelete(q); IDataReader rdr = DataService.GetReader(q.BuildSelectCommand()); return rdr; } /// /// Returns all records for this table, ordered /// /// Generic Typed List /// Column to order by public static IDataReader FetchAll(OrderBy orderBy) { Query q = new Query(BaseSchema); q.OrderBy = orderBy; CheckLogicalDelete(q); return DataService.GetReader(q.BuildSelectCommand()); } /// /// Returns all records for the given column/parameter, ordered by the passed in orderBy /// The expression for this is always "column=parameter" /// /// Name of the column to use in parmeter statement /// Value of the column /// Column to order by /// Ordering of results /// IDataReader public static IDataReader FetchByParameter(string columnName, object oValue, OrderBy orderBy) { Query q = new Query(BaseSchema); q.OrderBy = orderBy; q.AddWhere(columnName, oValue); CheckLogicalDelete(q); return DataService.GetReader(q.BuildSelectCommand()); } /// /// Returns all records for the given column/parameter /// The expression for this is always "column=parameter" /// /// /// /// IDataReader public static IDataReader FetchByParameter(string columnName, object oValue) { Query q = new Query(BaseSchema); q.AddWhere(columnName, oValue); CheckLogicalDelete(q); return DataService.GetReader(q.BuildSelectCommand()); } /// /// Returns all records for the given column/parameter /// The expression for this is always "column=parameter" /// /// /// /// /// IDataReader public static IDataReader FetchByParameter(string columnName, Comparison comparison, object oValue) { Query q = new Query(BaseSchema); q.AddWhere(columnName, comparison, oValue); CheckLogicalDelete(q); return DataService.GetReader(q.BuildSelectCommand()); } /// /// Returns all records for the given column/parameter /// The expression for this is always "column=parameter" /// /// /// /// /// /// IDataReader public static IDataReader FetchByParameter(string columnName, Comparison comparison, object oValue, OrderBy orderBy) { Query q = new Query(BaseSchema); q.AddWhere(columnName, comparison, oValue); q.OrderBy = orderBy; CheckLogicalDelete(q); return DataService.GetReader(q.BuildSelectCommand()); } /// /// Returns all records for the given query /// /// Generic Typed List /// Query for complex records public static IDataReader FetchByQuery(Query query) { CheckLogicalDelete(query); return DataService.GetReader(query.BuildSelectCommand()); } /// /// Uses the passed-in object as a parameter set. Does not use the created/modified fields /// /// /// public static IDataReader Find(T item) { return Find(item, null); } public static IDataReader Find(T item, OrderBy orderBy) { //build the sql string and command statements at the same time Query q = new Query(BaseSchema); CheckLogicalDelete(q); //retrieve data from database foreach (TableSchema.TableColumn col in BaseSchema.Columns) { string columnName; object columnValue; columnName = col.ColumnName; columnValue = item.GetColumnValue(columnName); if (!Utility.IsAuditField(columnName)) { object defaultValue = String.Empty; switch (col.DataType) { case DbType.Boolean: defaultValue = false; break; case DbType.Currency: case DbType.Decimal: case DbType.Int16: case DbType.Double: case DbType.Int32: defaultValue = 0; break; case DbType.Date: case DbType.DateTime: defaultValue = new DateTime(1900, 1, 1); break; case DbType.Guid: defaultValue = Guid.Empty; break; } if (columnValue != null) { if (!columnValue.Equals(defaultValue)) { q.AddWhere(columnName, columnValue); } } } } if (orderBy != null) { q.OrderBy = orderBy; } return DataService.GetReader(q.BuildSelectCommand()); } #endregion #region Utility /// /// If this object has a logical delete column, this method will append in the required parameter to avoid returning /// deleted records /// /// internal static void CheckLogicalDelete(Query q) { q.CheckLogicalDelete(); } /// /// Returns an ordered ListItemCollection for use with DropDowns, RadioButtonLists, and CheckBoxLists /// Note: This method assumes that the column in the second position is the text value column /// /// public static ListItemCollection GetListItems() { //get the textColumn based on position //which should be the second column of the table string textColumn = BaseSchema.Columns[1].ColumnName; return GetListItems(textColumn); } /// /// Returns an ordered ListItemCollection for use with DropDowns, RadioButtonLists, and CheckBoxLists /// /// The name of the column which should be used as the text value column /// public static ListItemCollection GetListItems(string textColumn) { //T item = new T(); ListItemCollection list = new ListItemCollection(); string pkCol = BaseSchema.PrimaryKey.ColumnName; string textCol = BaseSchema.GetColumn(textColumn).ColumnName; //run a query retrieving the two columns Query q = new Query(BaseSchema); q.SelectList = pkCol + "," + textCol; q.OrderBy = OrderBy.Asc(textCol); IDataReader rdr = q.ExecuteReader(); while (rdr.Read()) { ListItem listItem = new ListItem(rdr[1].ToString(), rdr[0].ToString()); list.Add(listItem); } return list; } #endregion public void LoadByParam(string columnName, object paramValue) { MarkOld(); IDataReader rdr = null; try { rdr = new Query(BaseSchema).AddWhere(columnName, paramValue).ExecuteReader(); if (rdr.Read()) { Load(rdr); } else { //if no records, this is new still MarkNew(); } } finally { if (rdr != null) { rdr.Close(); } } } public void LoadByKey(object keyID) { MarkOld(); IDataReader rdr = null; try { Query q = new Query(BaseSchema).AddWhere(BaseSchema.PrimaryKey.ColumnName, keyID); //CheckLogicalDelete(q); rdr = q.ExecuteReader(); if (rdr.Read()) { Load(rdr); } else { //if no records, this is new still MarkNew(); } } finally { if (rdr != null) { rdr.Close(); } } } #region CommandMethods public QueryCommand GetSelectCommand() { return ActiveHelper.GetSelectCommand(this); } protected static string ParameterPrefix { get { return BaseSchema.Provider.GetParameterPrefix(); } } /// /// Made Public for use with transactions /// /// /// public QueryCommand GetInsertCommand(string userName) { //use the controller for this return ActiveHelper.GetInsertCommand(this, userName); } public QueryCommand GetUpdateCommand(string userName) { return ActiveHelper.GetUpdateCommand(this, userName); } public static QueryCommand GetDeleteCommand(object keyID) { return ActiveHelper.GetDeleteCommand(keyID); } public static QueryCommand GetDeleteCommand(string columnName, object oValue) { return ActiveHelper.GetDeleteCommand(columnName,oValue); } #endregion #region Persistence /// /// Executes before any operations occur within ActiveRecord Save() /// protected virtual void BeforeValidate() {} /// /// Executes on existing records after validation and before the update command has been generated. /// protected virtual void BeforeUpdate() {} /// /// Executes on existing records after validation and before the insert command has been generated. /// protected virtual void BeforeInsert() {} /// /// Executes after all steps have been performed for a successful ActiveRecord Save() /// protected virtual void AfterCommit() {} [Obsolete("Deprecated: Use BeforeInsert() and/or BeforeUpdate() instead.")] protected virtual void PreUpdate() {} [Obsolete("Deprecated: Use AfterCommit() instead.")] protected virtual void PostUpdate() {} /// /// Saves this object's state to the selected Database. /// public void Save() { Save(String.Empty); } /// /// Saves this object's state to the selected Database. /// /// public void Save(int userID) { Save(userID.ToString()); } /// /// Saves this object's state to the selected Database. /// /// public void Save(Guid userID) { string sUserID = userID.ToString(); Save(sUserID); } public virtual bool Validate() { ValidateColumnSettings(); return Errors.Count == 0; } /// /// Saves this object's state to the selected Database. /// /// public void Save(string userName) { BeforeValidate(); bool isValid = Validate(); if(isValid) { if (IsNew) BeforeInsert(); else if (IsDirty) BeforeUpdate(); PreUpdate(); //Deprecated! QueryCommand cmd = GetSaveCommand(userName); if (cmd == null) return; //reset the Primary Key with the id passed back by the operation object pkVal = DataService.ExecuteScalar(cmd); if(pkVal != null) { if(pkVal.GetType() == typeof(decimal)) { pkVal = Convert.ToInt32(pkVal); } //set the primaryKey, only if an auto-increment //if (table.PrimaryKey.AutoIncrement) if(BaseSchema.PrimaryKey.AutoIncrement || BaseSchema.PrimaryKey.DataType == DbType.Guid) { try { SetPrimaryKey(pkVal); } catch { //this will happen if there is no PK defined on a table. Catch this and notify throw new Exception("No Primary Key is defined for this table. A primary key is required to use SubSonic"); } } } //set this object as old MarkOld(); MarkClean(); AfterCommit(); PostUpdate(); //Deprecated! } else { //throw an Exception string notification = string.Empty; foreach(string message in Errors) { notification += message + Environment.NewLine; } throw new Exception("Can't save: " + notification); } } public QueryCommand GetSaveCommand() { return GetSaveCommand(string.Empty); } public QueryCommand GetSaveCommand(string userName) { if(IsNew) return GetInsertCommand(userName); if(IsDirty) return GetUpdateCommand(userName); return null; } /// /// If the record contains Deleted or IsDeleted flag columns, sets them to true. If not, invokes Destroy() /// /// Number of rows affected by the operation public static int Delete(object keyID) { return DeleteByParameter(BaseSchema.PrimaryKey.ColumnName, keyID, null); } /// /// If the record contains Deleted or IsDeleted flag columns, sets them to true. If not, invokes Destroy() /// /// The name of the column that whose value will be evaluated for deletion /// The value that will be compared against columnName to determine deletion /// Number of rows affected by the operation public static int Delete(string columnName, object oValue) { return DeleteByParameter(columnName, oValue, null); } /// /// If the record contains Deleted or IsDeleted flag columns, sets them to true. If not, invokes Destroy() /// /// The name of the column that whose value will be evaluated for deletion /// The value that will be compared against columnName to determine deletion /// The userName that the record will be updated with. Only relevant if the record contains Deleted or IsDeleted properties /// Number of rows affected by the operation public static int Delete(string columnName, object oValue, string userName) { return DeleteByParameter(columnName, oValue, userName); } /// /// If the record contains Deleted or IsDeleted flag columns, sets them to true. If not, invokes Destroy() /// /// The name of the column that whose value will be evaluated for deletion /// The value that will be compared against columnName to determine deletion /// The userName that the record will be updated with. Only relevant if the record contains Deleted or IsDeleted properties /// Number of rows affected by the operation private static int DeleteByParameter(string columnName, object oValue, string userName) { return ActiveHelper.Delete(columnName,oValue,userName); } /// /// Deletes the record in the table, even if it contains Deleted or IsDeleted flag columns /// /// Number of rows affected by the operation public static int Destroy(object keyID) { return ActiveHelper.DestroyByParameter(BaseSchema.PrimaryKey.ColumnName, keyID); } /// /// Deletes the record in the table, even if it contains Deleted or IsDeleted flag columns /// /// The name of the column that whose value will be evaluated for deletion /// The value that will be compared against columnName to determine deletion /// Number of rows affected by the operation public static int Destroy(string columnName, object oValue) { return ActiveHelper.DestroyByParameter(columnName, oValue); } /// /// Deletes the record in the table, even if it contains Deleted or IsDeleted flag columns /// /// The name of the column that whose value will be evaluated for deletion /// The value that will be compared against columnName to determine deletion /// Number of rows affected by the operation private static int DestroyByParameter(string columnName, object oValue) { return ActiveHelper.DestroyByParameter(columnName, oValue); } #endregion } }