/// <summary> /// Perform CRUD action for the item(s) in the params list. /// An <see cref="IEnumerable{T}"/> of *modified* items is returned; the modification is to update the primary key to the correct new value for inserted items. /// If the input item does not support field writes/inserts as needed then an <see cref="ExpandoObject"/> corresponding to the updated item is returned instead. /// </summary> /// <param name="action">The ORM action</param> /// <param name="connection">The connection to use</param> /// <param name="items">The item or items</param> /// <returns>The list of modified items</returns> /// <remarks>Here and in <see cref="UpsertItemPK"/> we always return the modified original object where possible</remarks> internal Tuple <int, IEnumerable <T> > ActionOnItemsWithOutput(OrmAction action, DbConnection connection, IEnumerable <object> items) { List <T> modifiedItems = null; if (action == OrmAction.Insert) { modifiedItems = new List <T>(); } int count = 0; int affected = 0; ValidateAction(items, action); foreach (var item in items) { if (Validator.ShouldPerformAction(item, action)) { object result; affected += ActionOnItem(out result, action, item, connection); if (action == OrmAction.Insert) { var modified = result ?? item; if (IsGeneric && !(modified is T)) { modified = New(modified, false); } modifiedItems.Add((T)modified); } Validator.HasPerformedAction(item, action); } count++; } return(new Tuple <int, IEnumerable <T> >(affected, modifiedItems)); }
/// <summary> /// Save, Insert, Update or Delete an item. /// Save means: update item if PK field or fields are present and at non-default values, insert otherwise. /// On inserting an item with a single PK and a sequence/identity the PK field of the item itself is /// a) created if not present and b) filled with the new PK value, where this is possible (examples of cases /// where not possible are: fields can't be created on POCOs, property values can't be set on immutable items /// such as anonymously typed objects). /// </summary> /// <param name="modified">The modified item with PK added, if <see cref="OrmAction.Insert"/></param> /// <param name="originalAction">Save, Insert, Update or Delete</param> /// <param name="item">item</param> /// <param name="connection">The connection to use</param> /// <returns>The number of items affected</returns> /// <remarks> /// It *is* technically possibly (by writing to private backing fields) to change the field value in anonymously /// typed objects - http://stackoverflow.com/a/30242237/795690 - and bizarrely VB supports writing to fields in /// anonymously typed objects natively even though C# doesn't - http://stackoverflow.com/a/9065678/795690 (which /// sounds as if it means that if this part of the library was written in VB then doing this would be officially /// supported? not quite sure, that assumes that the different implementations of anonymous types can co-exist) /// </remarks> private int ActionOnItem(out object modified, OrmAction originalAction, object item, DbConnection connection) { OrmAction revisedAction; DbCommand command = CreateActionCommand(originalAction, item, out revisedAction); command.Connection = connection; if (revisedAction == OrmAction.Insert && PrimaryKeyInfo.SequenceNameOrIdentityFunction != null) { // *All* DBs return a huge sized number for their identity by default, following Massive we are normalising to int var pk = Convert.ToInt32(Scalar(command)); modified = UpsertItemPK( item, pk, // Don't create clone items on Save as these will then be discarded; but do still update the PK if clone not required originalAction == OrmAction.Insert); return(1); } else { modified = null; return(Execute(command)); } }
#pragma warning restore CS1998 #endregion #region ORM actions /// <summary> /// Save, Insert, Update or Delete an item. /// Save means: update item if PK field or fields are present and at non-default values, insert otherwise. /// On inserting an item with a single PK and a sequence/identity the PK field of the item itself is /// a) created if not present and b) filled with the new PK value, where this is possible (examples of cases /// where not possible are: fields can't be created on POCOs, property values can't be set on immutable items /// such as anonymously typed objects). /// </summary> /// <param name="originalAction">Save, Insert, Update or Delete</param> /// <param name="item">item</param> /// <param name="connection">The connection to use</param> /// <param name="cancellationToken">Async <see cref="CancellationToken"/></param> /// <returns>The number of items affected; the modified item with PK added, if <see cref="OrmAction.Insert"/></returns> /// <remarks> /// It *is* technically possibly (by writing to private backing fields) to change the field value in anonymously /// typed objects - http://stackoverflow.com/a/30242237/795690 - and bizarrely VB supports writing to fields in /// anonymously typed objects natively even though C# doesn't - http://stackoverflow.com/a/9065678/795690 (which /// sounds as if it means that if this part of the library was written in VB then doing this would be officially /// supported? not quite sure, that assumes that the different implementations of anonymous types can co-exist) /// </remarks> private async Task <Tuple <int, object> > ActionOnItemAsync(OrmAction originalAction, object item, DbConnection connection, CancellationToken cancellationToken = default) { OrmAction revisedAction; DbCommand command = CreateActionCommand(originalAction, item, out revisedAction); command.Connection = connection; if (revisedAction == OrmAction.Insert && PrimaryKeyInfo.SequenceNameOrIdentityFunction != null) { // *All* DBs return a huge sized number for their identity by default, following Massive we are normalising to int var pk = Convert.ToInt32(await ScalarAsync(command, cancellationToken).ConfigureAwait(false)); var modified = UpsertItemPK( item, pk, // Don't create clone items on Save as these will then be discarded; but do still update the PK if clone not required originalAction == OrmAction.Insert); return(new Tuple <int, object>(1, modified)); } else { int n = await ExecuteAsync(command, cancellationToken).ConfigureAwait(false); return(new Tuple <int, object>(n, null)); } }
/// <summary> /// Perform CRUD action for the item(s) in the params list. /// An <see cref="IEnumerable{T}"/> of *modified* items is returned; the modification is to update the primary key to the correct new value for inserted items. /// If the input item does not support field writes/inserts as needed then an <see cref="ExpandoObject"/> corresponding to the updated item is returned instead. /// </summary> /// <param name="action">The ORM action</param> /// <param name="connection">The connection to use</param> /// <param name="items">The item or items</param> /// <returns>The list of modified items</returns> /// <remarks>Here and in <see cref="UpsertItemPK"/> we always return the modified original object where possible</remarks> internal IEnumerable <T> ActionOnItems(OrmAction action, DbConnection connection, IEnumerable <object> items) { return(ActionOnItemsWithOutput(action, connection, items).Item2); }
/// <summary> /// Is the passed in item valid against the current validator? /// </summary> /// <param name="item">The item</param> /// <param name="action">Optional action type (defaults to <see cref="OrmAction.Save"/>)</param> /// <returns></returns> abstract public List <object> IsValid(object item, OrmAction action = OrmAction.Save);
/// <summary> /// This is called one item at time, after processing for that specific item. /// </summary> /// <param name="item">The item for which the action has just been performed. /// The type of this is NOT normalised, and depends on what you pass in.</param> /// <param name="action">The ORM action</param> /// <returns></returns> virtual public void HasPerformedAction(dynamic item, OrmAction action) { }
/// <summary> /// This is called one item at time, just before the processing for that specific item. /// <see cref="OrmAction"/> is performed iff this returns true. If false is returned, no processing /// is done for this item, but processing still continues for all remaining items. /// </summary> /// <param name="item">The item for which the action is about to be performed. /// The type of this is NOT normalised, and depends on what you pass in.</param> /// <param name="action">The ORM action</param> /// <returns></returns> virtual public bool ShouldPerformAction(dynamic item, OrmAction action) { return(true); }
/// <summary> /// If prevalidation is enabled Mighty calls this one item at a time before any database actions are done; /// if any item fails, no actions are done for any item. /// This also called by the <see cref="MightyOrm"/>.IsValid method. /// The default implementation of this method directly calls the `Validate` method and so ignores the <paramref name="action"/> parameter, /// but your own override can change this. /// </summary> /// <param name="action">You can choose to ignore this and do the same validation for every action.</param> /// <param name="item"> /// The item to validate. NB this can be whatever you pass in as input objects, and therefore is NOT restricted to items of the generic type /// even for generically typed <see cref="MightyOrm{T}"/>. /// Not necessarily a representation of the item for action: e.g. for delete only, it might be a representation of just the PK depending on how `Delete` was called. /// Despite this, you can write fairly stright-forward validators; have a look at the table classes in the Mighty docs examples. /// </param> /// <param name="reportError"> /// Your code should call this function, e.g. <paramref name="reportError"/>("My error text") to add errors to the error list. /// You may choose to add strings, or a more complex object if you wish. /// NB Adding one or more errors indicates that the item fails, adding no errors indicates success. /// </param> /// <returns></returns> virtual public void ValidateForAction(OrmAction action, dynamic item, Action <object> reportError) { Validate(item, reportError); }
/// <summary> /// Perform CRUD action for the item(s) in the params list. /// An <see cref="IEnumerable{T}"/> of *modified* items is returned; the modification is to update the primary key to the correct new value for inserted items. /// If the input item does not support field writes/inserts as needed then an <see cref="ExpandoObject"/> corresponding to the updated item is returned instead. /// </summary> /// <param name="action">The ORM action</param> /// <param name="connection">The connection to use</param> /// <param name="items">The item or items</param> /// <param name="cancellationToken">Async <see cref="CancellationToken"/></param> /// <returns>The list of modified items</returns> /// <remarks>Here and in <see cref="UpsertItemPK"/> we always return the modified original object where possible</remarks> internal async Task <Tuple <int, IEnumerable <T> > > ActionOnItemsWithOutputAsync(OrmAction action, DbConnection connection, IEnumerable <object> items, CancellationToken cancellationToken = default) { List <T> modifiedItems = null; if (action == OrmAction.Insert) { modifiedItems = new List <T>(); } int count = 0; int affected = 0; ValidateAction(items, action); foreach (var item in items) { if (Validator.ShouldPerformAction(item, action)) { var result = await ActionOnItemAsync(action, item, connection, cancellationToken).ConfigureAwait(false); affected += result.Item1; if (action == OrmAction.Insert) { var modified = result.Item2 ?? item; if (IsGeneric && !(modified is T)) { // create an item of type T from the modified item (e.g. a name-value dictionary/ExpandoObject) modified = New(modified, false); } modifiedItems.Add((T)modified); } Validator.HasPerformedAction(item, action); } count++; } return(new Tuple <int, IEnumerable <T> >(affected, modifiedItems)); }
/// <summary> /// Perform CRUD action for the item(s) in the params list. /// An <see cref="IEnumerable{T}"/> of *modified* items is returned; the modification is to update the primary key to the correct new value for inserted items. /// If the input item does not support field writes/inserts as needed then an <see cref="ExpandoObject"/> corresponding to the updated item is returned instead. /// </summary> /// <param name="action">The ORM action</param> /// <param name="connection">The connection to use</param> /// <param name="items">The item or items</param> /// <param name="cancellationToken">Async <see cref="CancellationToken"/></param> /// <returns>The list of modified items</returns> /// <remarks>Here and in <see cref="UpsertItemPK"/> we always return the modified original object where possible</remarks> internal async Task <IEnumerable <T> > ActionOnItemsAsync(OrmAction action, DbConnection connection, IEnumerable <object> items, CancellationToken cancellationToken = default) { return((await ActionOnItemsWithOutputAsync(action, connection, items, cancellationToken).ConfigureAwait(false)).Item2); }