/// <summary>
        /// Ensures the automatic history rollback.
        /// The entity goes back to a defined previous state or its last previous state.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="entity">The entity being restaured to its previous state.</param>
        /// <param name="historyNumber">[Optional] The history number from where rollback is done.</param>

        public static void AutoHistoryRollback <T>(this DbContext context, ref T entity, int?historyNumber = null) where T : class
        {
            if (String.IsNullOrEmpty(context.Entry(entity).PrimaryKey()))
            {
                throw new ArgumentNullException(String.Format("The entity {0} doesn't have a valid primary key, so can not be rollbacked", entity.GetType().Name));
            }

            if (context.Entry(entity).State == EntityState.Added)
            {
                throw new Exception(String.Format("The entity {0} is added, it can not be rollbacked", entity.GetType().Name));
            }

            context.Entry(entity).State = EntityState.Detached;
            string      keyString       = context.Entry(entity).PrimaryKey();
            var         historicChanges = context.Set <AutoHistory>().Where(h => h.RowId == keyString);
            AutoHistory history         = null;


            if (historicChanges.Count() == 1)
            {
                return;
            }

            if (historyNumber != null)
            {
                history = historicChanges.Where(h => h.Id == historyNumber).FirstOrDefault();
            }
            else
            {
                history = historicChanges.OrderByDescending(h => h.Id).FirstOrDefault();
            }

            if (history != null)
            {
                try
                {
                    // If this entity type does not exist in its assembly, GetType throws a TypeLoadException.
                    //Type elementType = Type.GetType(history.EntityName, true);
                    JObject jsonObj = JObject.Parse(history.Changed);
                    entity = Newtonsoft.Json.JsonConvert.DeserializeObject <T>(jsonObj.GetValue("before").ToString());
                    context.Remove(history);
                    context.Entry(entity).State = EntityState.Modified;
                }
                catch (Newtonsoft.Json.JsonReaderException ex)
                {
                    Console.WriteLine("{0}: Unable to load json string", ex.Message);
                }
                catch (TypeLoadException e)
                {
                    Console.WriteLine("{0}: Unable to load type", e.GetType().Name);
                }
            }
        }
        private static void WriteHistoryDeletedState(AutoHistory history, EntityEntry entry, IEnumerable <PropertyEntry> properties)
        {
            dynamic json = new System.Dynamic.ExpandoObject();

            foreach (var prop in properties)
            {
                ((IDictionary <String, Object>)json)[prop.Metadata.Name] = prop.OriginalValue;
            }
            history.RowId   = entry.PrimaryKey();
            history.Kind    = EntityState.Deleted;
            history.Changed = JsonSerializer.Serialize(json, AutoHistoryOptions.Instance.JsonSerializerOptions);
        }
        /// <summary>
        /// Ensures the automatic history.
        /// </summary>
        /// <param name="context">The context.</param>
        public static void EnsureAutoHistory(this DbContext context)
        {
            // TODO: only record the changed properties.
            var jsonSetting = new JsonSerializerSettings
            {
                ContractResolver = new EntityContractResolver(context),
            };

            var entries = context.ChangeTracker.Entries().Where(e => e.State != EntityState.Unchanged &&
                                                                e.State != EntityState.Detached &&
                                                                IncludeEntity(e)).ToList();

            if (entries.Count == 0)
            {
                return;
            }
            foreach (var entry in entries)
            {
                var history = new AutoHistory
                {
                    TypeName = entry.Entity.GetType().FullName,
                };

                switch (entry.State)
                {
                case EntityState.Added:
                    // REVIEW: what's the best way to do this?
                    history.SourceId  = "0";
                    history.Kind      = EntityState.Added;
                    history.AfterJson = JsonConvert.SerializeObject(entry.Entity, Formatting.Indented, jsonSetting);
                    break;

                case EntityState.Deleted:
                    history.SourceId   = entry.PrimaryKey();
                    history.Kind       = EntityState.Deleted;
                    history.BeforeJson = JsonConvert.SerializeObject(entry.Entity, Formatting.Indented, jsonSetting);
                    break;

                case EntityState.Modified:
                    history.SourceId   = entry.PrimaryKey();
                    history.Kind       = EntityState.Modified;
                    history.BeforeJson = JsonConvert.SerializeObject(entry.Original(), Formatting.Indented, jsonSetting);
                    history.AfterJson  = JsonConvert.SerializeObject(entry.Entity, Formatting.Indented, jsonSetting);
                    break;

                default:
                    continue;
                }

                context.Add(history);
            }
        }
        private static void WriteHistoryAddedState(AutoHistory history, IEnumerable <PropertyEntry> properties)
        {
            dynamic json = new System.Dynamic.ExpandoObject();

            foreach (var prop in properties)
            {
                if (prop.Metadata.IsKey() || prop.Metadata.IsForeignKey())
                {
                    continue;
                }
                ((IDictionary <String, Object>)json)[prop.Metadata.Name] = prop.CurrentValue;
            }

            // REVIEW: what's the best way to set the RowId?
            history.RowId   = "0";
            history.Kind    = EntityState.Added;
            history.Changed = JsonSerializer.Serialize(json);
        }
        private static void WriteHistoryModifiedState(AutoHistory history, EntityEntry entry, IEnumerable <PropertyEntry> properties)
        {
            dynamic json = new System.Dynamic.ExpandoObject();
            dynamic bef  = new System.Dynamic.ExpandoObject();
            dynamic aft  = new System.Dynamic.ExpandoObject();

            PropertyValues databaseValues = null;

            foreach (var prop in properties)
            {
                if (prop.IsModified)
                {
                    if (prop.OriginalValue != null)
                    {
                        if (!prop.OriginalValue.Equals(prop.CurrentValue))
                        {
                            ((IDictionary <String, Object>)bef)[prop.Metadata.Name] = prop.OriginalValue;
                        }
                        else
                        {
                            databaseValues ??= entry.GetDatabaseValues();
                            var originalValue = databaseValues.GetValue <object>(prop.Metadata.Name);
                            ((IDictionary <String, Object>)bef)[prop.Metadata.Name] = originalValue;
                        }
                    }
                    else
                    {
                        ((IDictionary <String, Object>)bef)[prop.Metadata.Name] = null;
                    }

                    ((IDictionary <String, Object>)aft)[prop.Metadata.Name] = prop.CurrentValue;
                }
            }

            ((IDictionary <String, Object>)json)["before"] = bef;
            ((IDictionary <String, Object>)json)["after"]  = aft;

            history.RowId   = entry.PrimaryKey();
            history.Kind    = EntityState.Modified;
            history.Changed = JsonSerializer.Serialize(json, AutoHistoryOptions.Instance.JsonSerializerOptions);
        }
Beispiel #6
0
        internal static AutoHistory AutoHistory(this EntityEntry entry)
        {
            var history = new AutoHistory {
                TableName = entry.Metadata.Relational().TableName,
            };

            // Get the mapped properties for the entity type.
            // (include shadow properties, not include navigations & references)
            var properties = entry.Properties;

            var json = new JObject();

            switch (entry.State)
            {
            case EntityState.Added:
                foreach (var prop in properties)
                {
                    if (prop.Metadata.IsKey() || prop.Metadata.IsForeignKey())
                    {
                        continue;
                    }
                    //json[prop.Metadata.Name] = new JObject(prop.CurrentValue);
                    json[prop.Metadata.Name] = JToken.FromObject(prop.CurrentValue);
                }

                // REVIEW: what's the best way to set the RowId?
                history.RowId   = "0";
                history.Kind    = EntityState.Added;
                history.Changed = json.ToString();
                break;

            case EntityState.Modified:
                var bef = new JObject();
                var aft = new JObject();

                foreach (var prop in properties)
                {
                    if (prop.IsModified)
                    {
                        bef[prop.Metadata.Name] = JToken.FromObject(prop.OriginalValue);
                        aft[prop.Metadata.Name] = JToken.FromObject(prop.CurrentValue);
                    }
                }

                json["Before"] = bef;
                json["After"]  = aft;

                history.RowId   = entry.PrimaryKey();
                history.Kind    = EntityState.Modified;
                history.Changed = json.ToString();
                break;

            case EntityState.Deleted:
                foreach (var prop in properties)
                {
                    json[prop.Metadata.Name] = JToken.FromObject(prop.OriginalValue);
                }
                history.RowId   = entry.PrimaryKey();
                history.Kind    = EntityState.Deleted;
                history.Changed = json.ToString();
                break;

            case EntityState.Detached:
            case EntityState.Unchanged:
            default:
                throw new NotSupportedException("AutoHistory only support Deleted and Modified entity.");
            }

            return(history);
        }