/// <summary> /// Implements the Find method defined by the IBehavior interface. Finds the data /// represented by a given find type and extra parameters in a way that allows /// behaviors support /// </summary> /// <param name="type">The type. Changing the type modifies the way the find is performed. /// Each type correspond to a custom finder method. For more info on custom finders methods, /// see the comments for the InitCustomFinders method of this class. For a concrete example of /// a custom finder being defined, see the EFBusiness class</param> /// <param name="parameters">Custom parameters. Not used by default, but can be used /// to pass special parameters to custom finders to further customize their functionality</param> /// <returns>An IQueryable that contains the results of the Find</returns> public IQueryable <T> Find(string type = "all", BusinessParameters parameters = null) { IQueryable <T> results; // Call the BeforeFind method of any attached behaviors (if there are any) foreach (FindBehaviorDelegate <T> bf in _beforeFinds) { bf(type, parameters); } // Execute the custom finder method corresponding the type passed as a parameter. // This call perfomrs the actual find functionality if (_finds.Keys.Contains(type)) { results = _finds[type](parameters); } else { throw new Exception("Attempted to use a non defined find type on a behaviorable business"); } // Call the AfterFind method of any attached behaviors. The AfterFind // can then modify the results of the Find if necessary foreach (FindBehaviorDelegate <T> bf in _afterFinds) { results = bf(type, parameters, results); } return(results as IQueryable <T>); }
/// <summary> /// Implements the Save method defined by the IBusiness interface. Create or update data in the storage solution in /// a way that supports behavior /// </summary> /// <param name="toSave">The data that will be saved</param> /// <param name="parameters">Custom parameters. Not used by default, but can be used /// to pass special parameters to extended Save methods to further customize their functionality</param> /// <returns>True if the save operation succeeded, else false</returns> public bool Save(T toSave, BusinessParameters parameters = null) { // Call the BeforeSave method of any attached behaviors (if there are any) bool interruptSave = false; foreach (SaveBehaviorDelegate <T> bs in _beforeSaves) { bool?result = bs(toSave, parameters); if (result == false) { // if the BeforeSave method of the current behavior returned false, the //current save operation is cancelled. Every BeforeSave calls on any behaviors // attached after the current one will not execute. None of the AfterSaves defined // on attached behaviors will execute. return(false); } else if (result == null) { //If the BeforeSave Method returned null, the save wil be "soft" cancelled. This means the data //isn't saved in the normal way, but the BeforeSave of behaviors attached to the business //after the current one will still be executed. The AfterSave of attached behaviors will also // be executed (unless another BeforeSave further down the line returns false) interruptSave = true; } } //If the save hasn't been interrupted be a BeforeSave returning null, perform the save if (!interruptSave) { if (!SaveHelper(toSave, parameters)) { // Save failed, return false return(false); } } // Call the AfterSave method of any attached behaviors (if there are any) interruptSave = false; foreach (SaveBehaviorDelegate <T> asd in _afterSaves) { bool?success = asd(toSave, parameters); if (success == false) { // if the AfterSave method of the current behavior returned false, the Save method ends // here. In other words, the AfterSave method for the behaviors attached after the current one // will not be executed. return(false); } else if (success == null) { // if the AfterSave method of the current behavior returned null, the other AfterSave for behaviors // attached after the current one are still executed, however the Save method will return false instead // of true as if the Save failed. interruptSave = true; } } return(interruptSave ? false : true); }
/// <summary> /// Obtains a value stored deeply into an hiarchy of BusinessParameters objects. Takes a serie of keys delimited by /// dots, such as this: /// /// key1.key2.key3 /// /// This means it will find the value stored in key one at the top level. This value should be a second BusinessParameters. /// The method will then find the value under key2 in the second BusinessParameters. This should be a third BusinessParameters /// object. The method will then find the value under key3 in the third BusinessParameters and return it. /// /// This simplify fetching deep hiarchical values because there is no need to check wheter all those keys exist manuallyl, /// the GetDeepValue methods handles it automatically. /// </summary> /// <param name="key">A serie of keys delimited by dots.</param> /// <returns>The value under the key, or null is such a value does not exists.</returns> public dynamic GetDeepValue(string key) { string[] keyParts = key.Split('.'); BusinessParameters parameters = this; // Iterate through every keys separated by dots. int max = keyParts.Count() - 1; for (int i = 0; i <= max; ++i) { // Get the value under the current key or null if it doesn't exists var tmp = parameters.ContainsKey(keyParts[i]) ? parameters[keyParts[i]] : null; if (tmp == null) { //The value specified by the dot separated keys does not exist so return null return(null); } else if (i == max) { // This is the last key thus return the value return(tmp); } else if (tmp is BusinessParameters) { // This is before the last key so go one level // deeper in the hiearchie. parameters = tmp; } else { return(null); } } return(null); }
/// <summary> /// Save a value in the hiearchy of BusinessParameters objects using a key made of several dot separated keys. /// like as this one: /// /// key1.key2.key3 /// /// The method starts at the top level. It checks if there is already a second BusinessParameters object under the key1 key. /// If not, a second BusinessParameters object is created and set under key one, if there was already one, then the already /// existing BusinessParameters is used. Then, the method checks if there is already a third BusinessParameters object /// under the key2 keyof the second BusinessParameters object. If not, a third business parameter is created and set under /// the key2, if there was already one the existing BusinessParameters is used. Then, the method saves the new value under key3 /// in the third BusinessParameters. /// /// The advantage of the static method vs the non static method is that it can start from a null value. /// </summary> /// <param name="parameters">The top level of the BusinessParameters hiearchy. It can be null. If null, a new /// hiearchy is created from scratch</param> /// <param name="key">A key made of several dot separted keys</param> /// <param name="value">The value that will be set</param> /// <returns>The new hiearchy containing the new value</returns> static public BusinessParameters SetDeepValue(BusinessParameters parameters, string key, dynamic value) { // No top of hiearchy passed as a parameter, so start a new hiearchy. if (parameters == null) { parameters = new BusinessParameters(); } string[] keyParts = key.Split('.'); BusinessParameters originalParams = parameters; // For every part of the key int max = keyParts.Count() - 1; for (int i = 0; i <= max; ++i) { if (i == max) { // Last part of the key, set value under the last dot delimited key parameters[keyParts[i]] = value; } else { //If there isn't already a BusinessParameters object under the current key, create a new // one and set it under the current key if (!parameters.ContainsKey(keyParts[i]) || !(parameters[keyParts[i]] is BusinessParameters)) { parameters[keyParts[i]] = new BusinessParameters(); } // go one level deeper into the hiearchy parameters = parameters[keyParts[i]]; } } return(originalParams); }
public void SetDeepValue(string key, dynamic value) { BusinessParameters.SetDeepValue(this, key, value); }
/// <summary> /// Inheriting businesses should implement the DeleteHelper method in order to provide the actual deleting functionality. /// It is better to implement the delete functionality in this method than by overriding the Delete method because it means /// behaviors are treated correctly whithout having to write any extra code. On the other hand, overriding Save can be /// useful in cases where behavior support must be unconditionaly removed, or need a special treatment not offered by /// GenericBusiness' implementation. /// </summary> /// <param name="toDelete">The data to be deleted</param> /// <param name="parameters">Custom parameters. Not used by default, but can be used /// to pass special parameters to extended Delete methods to further customize their functionality</param> /// <returns>True if the delete operation succeeded, else false</returns> protected abstract bool DeleteHelper(T toDelete, BusinessParameters parameters = null);
/// <summary> /// Inheriting businesses should implement the SaveHelper method in order to provide the actual saving functionality. /// It is better to implement the save functionality in this method than by overriding the Save method because it means /// behaviors are treated correctly whithout having to write any extra code. On the other hand, overriding Save can be /// useful in cases where behavior support must be unconditionaly removed, or need a special treatment not offered by /// GenericBusiness' implementation. /// </summary> /// <param name="toSave">The data to be saved</param> /// <param name="parameters">Custom parameters. Not used by default, but can be used /// to pass special parameters to extended Save methods to further customize their functionality</param> /// <returns>True if the save operation succeeded, else false</returns> protected abstract bool SaveHelper(T toSave, BusinessParameters parameters = null);
/// <summary> /// Finds the first element obtained by a given find type. This is useful in cases where a find operation can only possibly /// return one value /// </summary> /// <param name="type">The type. Indicates which custom finder will be used by the find method</param> /// <param name="parameters"></param> /// <returns>Custom parameters. Not used by default, but can be used /// to pass special parameters to custom finders to further customize their functionality</returns> public virtual T FindFirst(string type, BusinessParameters parameters = null) { return((this.Find(type, parameters) as IQueryable <T>).FirstOrDefault()); }