123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- using System.Diagnostics.CodeAnalysis;
- using System.Reflection;
- using InABox.Core;
- using InABox.Database;
- namespace PRS.Shared;
- public class Update_7_06 : DatabaseUpdateScript
- {
- public override VersionNumber Version => new (7,06);
-
- public override bool Update()
- {
- var deletions = DbFactory.NewProvider(Logger.Main).Query<Deletion>(
- new Filter<Deletion>(x => x.Data).IsEqualTo(""));
- Logger.Send(LogType.Information, "", "Updating Deletions");
- foreach (var deletion in deletions.ToObjects<Deletion>())
- {
- Purge(deletion);
- }
- Logger.Send(LogType.Information, "", "Finished updating Deletions");
- return true;
- }
-
- private static Dictionary<Type, List<Tuple<Type, string>>> _cascades = new();
- private static Dictionary<Type, List<Tuple<Type, List<string>>>> _setNulls = new();
- private static void LoadDeletions(Type type)
- {
- if (_cascades.ContainsKey(type)) return;
- // Get the EntityLink that is associated with this class
- var linkclass = CoreUtils.TypeList(
- new[] { type.Assembly },
- x => typeof(IEntityLink).GetTypeInfo().IsAssignableFrom(x) && x.GetInheritedGenericTypeArguments().FirstOrDefault() == type
- ).FirstOrDefault();
- // if The entitylink does not exist, we don't need to do anything
- if (linkclass == null)
- return;
- var cascades = new List<Tuple<Type, string>>();
- var setNulls = new List<Tuple<Type, List<string>>>();
- var childtypes = DbFactory.ProviderFactory.Types.Where(x => x.IsSubclassOf(typeof(Entity)) && x.GetCustomAttribute<AutoEntity>() == null);
- foreach (var childtype in childtypes)
- {
- // Get all registered types for this entitylink
- var fields = new List<string>();
- var bDelete = false;
- // Find any IEntityLink<> properties that refer back to this class
- var childprops = CoreUtils.PropertyList(childtype, x => x.PropertyType == linkclass);
- foreach (var childprop in childprops)
- {
- var fieldname = string.Format("{0}.ID", childprop.Name);
- var attr = childprop.GetCustomAttributes(typeof(EntityRelationshipAttribute), true).FirstOrDefault();
- if (attr != null && ((EntityRelationshipAttribute)attr).Action.Equals(DeleteAction.Cascade))
- {
- cascades.Add(new(childtype, fieldname));
- bDelete = true;
- break;
- }
- fields.Add(fieldname);
- }
- if (!bDelete && fields.Any())
- {
- setNulls.Add(new(childtype, fields));
- }
- }
- _cascades[type] = cascades;
- _setNulls[type] = setNulls;
- }
- private static bool GetCascades(Type type, [NotNullWhen(true)] out List<Tuple<Type, string>>? cascades)
- {
- LoadDeletions(type);
- return _cascades.TryGetValue(type, out cascades);
- }
- private static bool GetSetNulls(Type type, [NotNullWhen(true)] out List<Tuple<Type, List<string>>>? setNulls)
- {
- LoadDeletions(type);
- return _setNulls.TryGetValue(type, out setNulls);
- }
- private static MethodInfo _deleteEntitiesMethod = typeof(Update_7_06).GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
- .Single(x => x.Name == nameof(DeleteEntity) && x.IsGenericMethod);
- private static void DeleteEntity<T>(Deletion deletion, Guid parentID, string parentField, DeletionData deletionData) where T : Entity, new()
- {
- var columns = DeletionData.DeletionColumns<T>();
- var delEntities = DbFactory.NewProvider(Logger.Main).QueryDeleted(deletion, new Filter<T>(parentField).IsEqualTo(parentID), columns);
- var nDelntities = DbFactory.NewProvider(Logger.Main).Query(new Filter<T>(parentField).IsEqualTo(parentID), columns);
- foreach (var row in delEntities.Rows.Concat(nDelntities.Rows))
- {
- deletionData.DeleteEntity<T>(row);
- CascadeDelete(typeof(T), deletion, row.Get<T, Guid>(x => x.ID), deletionData);
- }
- }
- private static void DeleteEntity(Type T, Deletion deletion, Guid parentID, string parentField, DeletionData deletionData)
- {
- _deleteEntitiesMethod.MakeGenericMethod(T).Invoke(null, new object?[] { deletion, parentID, parentField, deletionData });
- }
- private static MethodInfo _setNullEntityMethod = typeof(Update_7_06).GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
- .Single(x => x.Name == nameof(SetNullEntity) && x.IsGenericMethod);
- private static void SetNullEntity<T>(List<string> properties, Guid parentID, DeletionData deletionData) where T : Entity, new()
- {
- foreach (var property in properties)
- {
- var entities = DbFactory.NewProvider(Logger.Main).Query(new Filter<T>(property).IsEqualTo(parentID), Columns.None<T>().Add(x => x.ID));
- foreach (var row in entities.Rows)
- {
- deletionData.SetNullEntity<T>(row.Get<T, Guid>(x => x.ID), property, parentID);
- }
- }
- }
- private static void SetNullEntity(Type T, List<string> properties, Guid parentID, DeletionData deletionData)
- {
- _setNullEntityMethod.MakeGenericMethod(T).Invoke(null, new object?[] { properties, parentID, deletionData });
- }
- private static void CascadeDelete(Type type, Deletion deletion, Guid parentID, DeletionData deletionData)
- {
- if (GetCascades(type, out var cascades))
- {
- foreach (var cascade in cascades)
- {
- DeleteEntity(cascade.Item1, deletion, parentID, cascade.Item2, deletionData);
- }
- }
- if (GetSetNulls(type, out var setNulls))
- {
- foreach (var setNull in setNulls)
- {
- SetNullEntity(setNull.Item1, setNull.Item2, parentID, deletionData);
- }
- }
- }
- // Referenced via reflection.
- private static void PurgeEntityType<T>(Deletion deletion) where T : Entity, new()
- {
- var entities = DbFactory.NewProvider(Logger.Main).QueryDeleted(deletion, null, DeletionData.DeletionColumns<T>()).ToObjects<T>().ToList();
- var deletionData = new DeletionData();
- foreach (var entity in entities)
- {
- deletionData.DeleteEntity(entity);
- CascadeDelete(typeof(T), deletion, entity.ID, deletionData);
- }
- if (deletionData.Cascades.Count > 0 || deletionData.SetNulls.Count > 0)
- {
- var tableName = typeof(T).Name;
- var newDeletion = new Deletion()
- {
- DeletionDate = DateTime.Now,
- HeadTable = tableName,
- Description = $"Deleted {entities.Count} entries",
- DeletedBy = deletion.DeletedBy,
- Data = Serialization.Serialize(deletionData)
- };
- DbFactory.NewProvider(Logger.Main).Save(newDeletion);
- }
- DbFactory.NewProvider(Logger.Main).Purge(entities);
- }
- public static void Purge(Deletion deletion)
- {
- if (deletion.ID == Guid.Empty)
- {
- Logger.Send(LogType.Error, "", "Empty Deletion ID");
- return;
- }
- var entityType = CoreUtils.Entities.FirstOrDefault(x => x.Name == deletion.HeadTable);
- if (entityType is null)
- {
- Logger.Send(LogType.Error, "", $"Entity {deletion.HeadTable} does not exist");
- return;
- }
- var purgeMethod = typeof(Update_7_06).GetMethod(nameof(PurgeEntityType), BindingFlags.NonPublic | BindingFlags.Static)!;
- purgeMethod.MakeGenericMethod(entityType).Invoke(null, new object[] { deletion });
- DbFactory.NewProvider(Logger.Main).Purge<Deletion>(deletion);
- }
- }
|