예제 #1
0
        public async Task Save(IEntity entity, SaveBehaviour behaviour)
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }

            Task save() => new SaveOperation(this, entity, behaviour).Run();

            async Task doSave()
            {
                if (entity.IsNew)
                {
                    await save();
                }
                else
                {
                    using (await GetSyncLock(entity.GetType().FullName + entity.GetId()).Lock()) await save();
                }
            }

            if (ProviderConfig.Configuration.Transaction.EnforceForSave)
            {
                await EnlistOrCreateTransaction(doSave);
            }
            else
            {
                await doSave();
            }
        }
예제 #2
0
파일: Sorter.cs 프로젝트: Geeksltd/Olive
        /// <summary>
        /// Justifies the order of a specified item and its siblings.
        /// The value of the "Order" property in those objects will be 10, 20, 30, ...
        /// </summary>
        public static async Task JustifyOrders(ISortable item, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            using (await AsyncLock.Lock())
            {
                var changed = new List <Entity>();

                var order = 0;

                foreach (var sibling in (await FindSiblings(item)).OrderBy(i => i.Order).Distinct().ToArray())
                {
                    order += INCREMENT;
                    if (sibling.Order == order)
                    {
                        continue;
                    }

                    var clone = sibling.Clone() as Entity;
                    (clone as ISortable).Order = order;
                    //changed.Add(clone);
                    try
                    {
                        await Database.Save(clone, saveBehaviour);
                    }
                    catch
                    {
                    }
                }

                //await Database.Save(changed, saveBehaviour);
            }
        }
예제 #3
0
        public async Task Save(IEntity entity, SaveBehaviour behaviour)
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }

            Func <Task> save = async() => await DoSave(entity, behaviour);

            Func <Task> doSave = async() =>
            {
                if (entity.IsNew)
                {
                    await save();
                }
                else
                {
                    using (await GetSyncLock(entity.GetType().FullName + entity.GetId()).Lock()) await save();
                }
            };

            if (Configuration.Transaction.EnforceForSave)
            {
                await EnlistOrCreateTransaction(doSave);
            }
            else
            {
                await doSave();
            }
        }
예제 #4
0
        /// <summary>
        /// Moves an item up among its siblings. Returns False if the item is already first in the list, otherwise true.
        /// </summary>
        public static async Task <bool> MoveUp(ISortable item, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            using (await AsyncLock.Lock())
            {
                var above = await FindItemAbove(item);

                if (above == null)
                {
                    return(false);
                }

                if (above.Order == item.Order)
                {
                    above.Order--;
                }

                await Swap(item, above, saveBehaviour);

                item = await Database.Reload(item);

                above = await Database.Reload(above);
            }

            await JustifyOrders(item, saveBehaviour);

            return(true);
        }
예제 #5
0
        /// <summary>
        /// Moves an item up among its siblings. Returns False if the item is already first in the list, otherwise true.
        /// </summary>
        public static bool MoveUp(ISortable item, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            lock (SyncLock)
            {
                var above = FindItemAbove(item);

                if (above == null)
                {
                    return(false);
                }

                if (above.Order == item.Order)
                {
                    above.Order--;
                }

                Swap(item, above, saveBehaviour);

                Database.Reload(ref item);
                Database.Reload(ref above);

                JustifyOrders(item, saveBehaviour);

                return(true);
            }
        }
예제 #6
0
        /// <summary>
        /// Inserts or updates an object in the database.
        /// </summary>
        public static void Save(IEntity entity, SaveBehaviour behaviour)
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }

            Action save = () => DoSave(entity, behaviour);

            Action doSave = () =>
            {
                if (entity.IsNew)
                {
                    save();
                }
                else
                {
                    lock (string.Intern(entity.GetType().FullName + entity.GetId())) { save(); }
                }
            };

            if (ENFORCE_SAVE_TRANSACTION)
            {
                EnlistOrCreateTransaction(doSave);
            }
            else
            {
                doSave();
            }
        }
예제 #7
0
        /// <summary>
        /// Moves this item after a specified other item. If null is specified, it will be moved to the beginning of its siblings.
        /// </summary>
        public static async Task MoveAfter(ISortable item, ISortable after, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            var newOrder = (after == null ? 0 : after.Order) + 1;

            item = await Database.Update(item, o => o.Order = newOrder, saveBehaviour);

            await JustifyOrders(item, saveBehaviour);
        }
예제 #8
0
 public SaveOperation(Database db, IEntity entity, SaveBehaviour behaviour)
 {
     Entity        = entity as Entity ?? throw new ArgumentNullException(nameof(entity));
     InTransaction = db.AnyOpenTransaction();
     Mode          = entity.IsNew ? SaveMode.Insert : SaveMode.Update;
     ClonedFrom    = Entity._ClonedFrom;
     Provider      = db.GetProvider(entity);
     Behaviour     = behaviour;
     Db            = db;
 }
예제 #9
0
        /// <summary>
        /// Moves an item up to last among its siblings. Always returns true.
        /// </summary>
        public static bool MoveLast(ISortable item, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            lock (SyncLock)
            {
                var last = FindSiblings(item).Max(o => o.Order);

                Database.Update(item, o => o.Order = last + 1, saveBehaviour);
                JustifyOrders(item, saveBehaviour);
                return(true);
            }
        }
예제 #10
0
        /// <summary>
        /// Moves an item up to last among its siblings. Always returns true.
        /// </summary>
        public static async Task <bool> MoveLast(ISortable item, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            using (await AsyncLock.Lock())
            {
                var last = (await FindSiblings(item)).Max(o => o.Order);

                await Entity.Database.Update(item, o => o.Order = last + 1, saveBehaviour);
                await JustifyOrders(item, saveBehaviour);

                return(true);
            }
        }
예제 #11
0
        /// <summary>
        /// Swaps the order of two specified items.
        /// </summary>
        static void Swap(ISortable one, ISortable two, SaveBehaviour saveBehaviour)
        {
            var somethingAboveAll = FindSiblings(one).Max(i => i.Order) + 20;

            Database.EnlistOrCreateTransaction(() =>
            {
                var order1 = two.Order;
                var order2 = one.Order;

                Database.Update(one, i => i.Order = order1, saveBehaviour);
                Database.Update(two, i => i.Order = order2, saveBehaviour);
            });
        }
예제 #12
0
        /// <summary>
        /// Moves this item before a specified other item. If null is specified, it will be moved to the end of its siblings.
        /// </summary>
        public static async Task MoveBefore(ISortable item, ISortable before, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            var newOrder = (before == null ? int.MaxValue : before.Order) - 1;

            if (newOrder < 0)
            {
                newOrder = 0;
            }

            item = await Database.Update(item, o => o.Order = newOrder, saveBehaviour);

            await JustifyOrders(item, saveBehaviour);
        }
예제 #13
0
        /// <summary>
        /// Swaps the order of two specified items.
        /// </summary>
        static async Task Swap(ISortable one, ISortable two, SaveBehaviour saveBehaviour)
        {
            var somethingAboveAll = (await FindSiblings(one)).Max(i => i.Order) + 20;

            await Database.EnlistOrCreateTransaction(async() =>
            {
                var order1 = two.Order;
                var order2 = one.Order;

                await Database.Update(one, i => i.Order = order1, saveBehaviour);
                await Database.Update(two, i => i.Order = order2, saveBehaviour);
            });
        }
예제 #14
0
        /// <summary>
        /// Runs an update command on a given object's clone and persists the updated object in database. It returns the updated instance.
        /// </summary>
        /// <param name="item">The object to be updated in database.</param>
        /// <param name="action">Update action. For example: o=>o.Property = "Value"</param>
        public static T Update <T>(T item, Action <T> action, SaveBehaviour behaviour) where T : IEntity
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            if (item.IsNew)
            {
                throw new ArgumentException("New instances cannot be updated using the Update method.");
            }

            if (!(item is Entity))
            {
                throw new ArgumentException("Database.Update() method accepts a type inheriting from {0}. So {1} is not supported.".FormatWith(typeof(Entity).FullName, typeof(T).FullName));
            }

            if ((item as Entity)._ClonedFrom?.IsStale == true && AnyOpenTransaction())
            {
                // No need for an error. We can just get the fresh version here.
                item = Reload(item);
            }

            if (EntityManager.IsImmutable(item as Entity))
            {
                var clone = (T)((IEntity)item).Clone();

                action(clone);

                Save(clone as Entity, behaviour);

                if (!AnyOpenTransaction())
                {
                    action(item);
                }

                return(clone);
            }
            else
            {
                action(item);
                Save(item, behaviour);

                return(item);
            }
        }
예제 #15
0
        /// <summary>
        /// Runs an update command on a given object's clone and persists the updated object in database. It returns the updated instance.
        /// </summary>
        /// <param name="item">The object to be updated in database.</param>
        /// <param name="action">Update action. For example: o=>o.Property = "Value"</param>
        public async Task <T> Update <T>(T item, Action <T> action, SaveBehaviour behaviour) where T : IEntity
        {
            if (item == null)
            {
                throw new ArgumentNullException(nameof(item));
            }

            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

            if (item.IsNew)
            {
                throw new InvalidOperationException("New instances cannot be updated using the Update method.");
            }

            if (!(item is Entity))
            {
                throw new ArgumentException($"Database.Update() method accepts a type inheriting from {typeof(Entity).FullName}. So {typeof(T).FullName} is not supported.");
            }

            if ((item as Entity)._ClonedFrom?.IsStale == true && AnyOpenTransaction())
            {
                // No need for an error. We can just get the fresh version here.
                item = await Reload(item);
            }

            if (Entity.Services.IsImmutable(item as Entity))
            {
                var clone = (T)((IEntity)item).Clone();

                action(clone);

                await Save(clone as Entity, behaviour);

                if (!AnyOpenTransaction())
                {
                    action(item);
                }

                return(clone);
            }
            else
            {
                action(item);
                await Save(item, behaviour);

                return(item);
            }
        }
예제 #16
0
        /// <summary>
        /// Runs an update command on a list of given objects and persists the updated objects in database.
        /// It returns the updated instances.
        /// </summary>
        /// <param name="items">The objects to be updated in database.</param>
        /// <param name="action">Update action. For example: o=>o.Property = "Value"</param>
        public static List <T> Update <T>(IEnumerable <T> items, Action <T> action, SaveBehaviour behaviour) where T : IEntity
        {
            var result = new List <T>();

            EnlistOrCreateTransaction(() =>
            {
                foreach (var item in items)
                {
                    result.Add(Update(item, action, behaviour));
                }
            });

            return(result);
        }
예제 #17
0
        /// <summary>
        /// Inserts or updates an object in the database.
        /// </summary>
        public static void Save(IEntity entity, SaveBehaviour behaviour)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }

            if (ENFORCE_SAVE_TRANSACTION)
            {
                EnlistOrCreateTransaction(() => DoSave(entity, behaviour));
            }
            else
            {
                DoSave(entity, behaviour);
            }
        }
예제 #18
0
        /// <summary>
        /// Moves an item up to first among its siblings. Returns False if the item is already first in the list, otherwise true.
        /// </summary>
        public static bool MoveFirst(ISortable item, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            lock (SyncLock)
            {
                var first = FindSiblings(item).Min(o => o.Order);

                if (first <= 0)
                {
                    return(false);
                }

                Database.Update(item, o => o.Order = first - 1, saveBehaviour);
                JustifyOrders(item, saveBehaviour);
                return(true);
            }
        }
예제 #19
0
        /// <summary>
        /// Moves an item up to first among its siblings. Returns False if the item is already first in the list, otherwise true.
        /// </summary>
        public static async Task <bool> MoveFirst(ISortable item, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            using (await AsyncLock.Lock())
            {
                var first = (await FindSiblings(item)).Min(o => o.Order);

                if (first <= 0)
                {
                    return(false);
                }

                await Entity.Database.Update(item, o => o.Order = first - 1, saveBehaviour);
                await JustifyOrders(item, saveBehaviour);

                return(true);
            }
        }
        /// <summary>
        /// Saves the specified records in the data repository.
        /// The operation will run in a Transaction.
        /// </summary>
        public static IEnumerable <T> Save <T>(IEnumerable <T> records, SaveBehaviour behaviour) where T : IEntity
        {
            if (records == null)
            {
                throw new ArgumentNullException("records");
            }

            if (records.None())
            {
                return(records);
            }

            EnlistOrCreateTransaction(() =>
            {
                foreach (var record in records)
                {
                    Save(record as Entity, behaviour);
                }
            });

            return(records);
        }
예제 #21
0
        /// <summary>
        /// Saves the specified records in the data repository.
        /// The operation will run in a Transaction.
        /// </summary>
        public async Task <IEnumerable <T> > Save <T>(IEnumerable <T> records, SaveBehaviour behaviour) where T : IEntity
        {
            if (records == null)
            {
                throw new ArgumentNullException(nameof(records));
            }

            if (records.None())
            {
                return(records);
            }

            await EnlistOrCreateTransaction(async() =>
            {
                foreach (var record in records)
                {
                    await Save(record as Entity, behaviour);
                }
            });

            return(records);
        }
예제 #22
0
        /// <summary>
        /// Moves an item down among its siblings. Returns False if the item is already last in the list, otherwise true.
        /// </summary>
        public static bool MoveDown(ISortable item, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            lock (SyncLock)
            {
                var below = FindItemBelow(item);

                if (below == null)
                {
                    return(false);
                }

                if (below.Order == item.Order)
                {
                    item.Order++;
                }

                Swap(item, below, saveBehaviour);

                JustifyOrders(item, saveBehaviour);

                return(true);
            }
        }
예제 #23
0
        /// <summary>
        /// Moves an item down among its siblings. Returns False if the item is already last in the list, otherwise true.
        /// </summary>
        public static async Task <bool> MoveDown(ISortable item, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            using (await AsyncLock.Lock())
            {
                var below = await FindItemBelow(item);

                if (below == null)
                {
                    return(false);
                }

                if (below.Order == item.Order)
                {
                    item.Order++;
                }

                await Swap(item, below, saveBehaviour);
            }

            await JustifyOrders(item, saveBehaviour);

            return(true);
        }
예제 #24
0
        /// <summary>
        /// Justifies the order of a specified item and its siblings.
        /// The value of the "Order" property in those objects will be 10, 20, 30, ...
        /// </summary>
        public static void JustifyOrders(ISortable item, SaveBehaviour saveBehaviour = SaveBehaviour.Default)
        {
            lock (SyncLock)
            {
                var changed = new List <Entity>();

                var order = 0;

                foreach (var sibling in FindSiblings(item).OrderBy(i => i.Order).ToArray())
                {
                    order += INCREMENT;
                    if (sibling.Order == order)
                    {
                        continue;
                    }

                    var clone = sibling.Clone() as Entity;
                    (clone as ISortable).Order = order;
                    changed.Add(clone);
                }

                Database.Save(changed, saveBehaviour);
            }
        }
예제 #25
0
        static void DoSave(IEntity entity, SaveBehaviour behaviour)
        {
            var mode = entity.IsNew ? SaveMode.Insert : SaveMode.Update;

            var asEntity = entity as Entity;

            if (mode == SaveMode.Update && (asEntity._ClonedFrom?.IsStale == true) && AnyOpenTransaction())
            {
                throw new InvalidOperationException("This " + entity.GetType().Name + " instance in memory is out-of-date. " +
                                                    "A clone of it is already updated in the transaction. It is not allowed to update the same instance multiple times in a transaction, because then the earlier updates would be overwriten by the older state of the instance in memory. \r\n\r\n" +
                                                    @"BAD: 
Database.Update(myObject, x=> x.P1 = ...); // Note: this could also be nested inside another method that's called here instead.
Database.Update(myObject, x=> x.P2 = ...);

GOOD: 
Database.Update(myObject, x=> x.P1 = ...);
myObject = Database.Reload(myObject);
Database.Update(myObject, x=> x.P2 = ...);");
            }

            if (EntityManager.IsImmutable(entity))
            {
                throw new ArgumentException("An immutable record must be cloned before any modifications can be applied on it. Type=" +
                                            entity.GetType().FullName + ", Id=" + entity.GetId() + ".");
            }

            var dataProvider = GetProvider(entity);

            if (!IsSet(behaviour, SaveBehaviour.BypassValidation))
            {
                EntityManager.RaiseOnValidating(entity as Entity, EventArgs.Empty);
                entity.Validate();
            }
            else if (!dataProvider.SupportValidationBypassing())
            {
                throw new ArgumentException(dataProvider.GetType().Name + " does not support bypassing validation.");
            }

            #region Raise saving event

            if (!IsSet(behaviour, SaveBehaviour.BypassSaving))
            {
                var savingArgs = new System.ComponentModel.CancelEventArgs();
                EntityManager.RaiseOnSaving(entity, savingArgs);

                if (savingArgs.Cancel)
                {
                    Cache.Current.Remove(entity);
                    return;
                }
            }
            #endregion

            if (!IsSet(behaviour, SaveBehaviour.BypassLogging) && !(entity is IApplicationEvent) &&
                Config.Get("Log.Record.Application.Events", defaultValue: true))
            {
                ApplicationEventManager.RecordSave(entity, mode);
            }

            dataProvider.Save(entity);
            Cache.Current.UpdateRowVersion(entity);

            if (mode == SaveMode.Update && asEntity?._ClonedFrom != null && AnyOpenTransaction())
            {
                asEntity._ClonedFrom.IsStale = true;
                asEntity.IsStale             = false;
            }

            if (mode == SaveMode.Insert)
            {
                EntityManager.SetSaved(entity);
            }

            Cache.Current.Remove(entity);

            if (Transaction.Current != null)
            {
                Transaction.Current.TransactionCompleted += (s, e) => { Cache.Current.Remove(entity); }
            }
            ;

            if (DbTransactionScope.Root != null)
            {
                DbTransactionScope.Root.OnTransactionCompleted(() => Cache.Current.Remove(entity));
            }

            if (!(entity is IApplicationEvent))
            {
                OnUpdated(new EventArgs <IEntity>(entity));
            }

            if (!IsSet(behaviour, SaveBehaviour.BypassSaved))
            {
                EntityManager.RaiseOnSaved(entity, new SaveEventArgs(mode));
            }

            // OnSaved event handler might have read the object again and put it in the cache, which would
            // create invalid CachedReference objects.
            Cache.Current.Remove(entity);
        }
예제 #26
0
 void Start()
 {
     _saveBehaviour = GameObject.FindObjectOfType <SaveBehaviour>();
     _switch_scene  = GameObject.FindObjectOfType <Switch_Scene>();
     _setHighscore  = GameObject.FindObjectOfType <SetHighscore>();
 }
예제 #27
0
 bool Not(SaveBehaviour behaviour) => !IsSet(Behaviour, behaviour);
예제 #28
0
 public Task <T> Update <T>(T item, Action <T> action, SaveBehaviour behaviour) where T : IEntity =>
 Update(item, action, null, behaviour);
예제 #29
0
 /// <summary>
 /// Initialize instance of Database by injecting ICache dependency
 /// </summary>
 ///
 internal static bool IsSet(SaveBehaviour setting, SaveBehaviour behaviour) => (setting & behaviour) == behaviour;
예제 #30
0
 public Task <T> Update <T>(T item, Func <T, Task> action, SaveBehaviour behaviour) where T : IEntity =>
 Update(item, null, action, behaviour);