/// <summary> /// Initializes a new instance. /// </summary> internal ChangeEntry(ChangeType changeType, IUberMap map, object entity) { UberMap = map; ChangeType = changeType; Entity = entity; MetaEntity = MetaEntity.Locate(entity); }
/// <summary> /// Returns whether this child entity requires a save operation. /// </summary> internal static bool DoesMetaNeedSave(this MetaEntity child, bool cascade, object origin = null) { if (child.Map == null) { return(true); } if (child.HasRecordChanges()) { return(true); } var list = child.GetRemovedChilds(forgetRemoved: false); var need = list.Count != 0; list.Clear(); list = null; if (need) { return(true); } if (cascade) { list = child.GetDependencies(child.UberMap, MemberDependencyMode.Child); foreach (var obj in list) { if (obj == null) { continue; } var type = obj.GetType(); if (!type.IsClass && !type.IsInterface) { continue; } if (object.ReferenceEquals(origin, obj)) { continue; } var meta = MetaEntity.Locate(obj); var temp = meta.UberMap ?? child.UberMap.Repository.LocateUberMap(type); if (temp == null) { throw new NotFoundException("Cannot find map for type '{0}'.".FormatWith(type.EasyName())); } if (meta.DoesMetaNeedSave(cascade: false)) { need = true; break; } } list.Clear(); list = null; if (need) { return(true); } } return(false); }
/// <summary> /// Initializes a new instance. /// </summary> /// <param name="map">The map this command will be associated with.</param> /// <param name="entity">The entity affected by this operation.</param> protected MetaOperation(DataMap <T> map, T entity) : base(map) { if (entity == null) { throw new ArgumentNullException("entity", "Entity cannot be null."); } _Entity = entity; _MetaEntity = MetaEntity.Locate(entity); }
/// <summary> /// Generates a delete core command for the given entity, or returns null if such /// command cannot be generated for whatever reasons. /// </summary> internal static IDeleteCommand GenerateDeleteCommand(this IUberMap map, object entity) { if (entity == null) { return(null); } if (map == null || map.IsDisposed || !map.IsValidated) { return(null); } IDeleteCommand cmd = null; MetaEntity meta = MetaEntity.Locate(entity, create: true); if (meta.Record == null) { var record = new Core.Concrete.Record(map.Schema); map.WriteRecord(entity, record); meta.Record = record; } var id = map.ExtractId(meta.Record); if (id != null) { cmd = map.Link.Engine.CreateDeleteCommand(map.Link, x => map.Table); if (map.Discriminator != null) { cmd.Where(map.Discriminator); } var tag = new DynamicNode.Argument("x"); for (int i = 0; i < id.Count; i++) { var left = new DynamicNode.GetMember(tag, id.Schema[i].ColumnName); var bin = new DynamicNode.Binary(left, ExpressionType.Equal, id[i]); cmd.Where(x => bin); left.Dispose(); bin.Dispose(); } tag.Dispose(); id.Dispose(); } return(cmd); }
/// <summary> /// Invoked to execute this operation. /// </summary> internal void OnExecute(object origin = null) { lock (Repository.MasterLock) { List <object> list = null; ChangeEntry change = null; IDeleteCommand cmd = null; DebugEx.IndentWriteLine("\n- Preparing 'Delete({0})'...", MetaEntity); try { list = MetaEntity.GetRemovedChilds(forgetRemoved: true); foreach (var obj in list) { if (obj == null) { continue; } if (object.ReferenceEquals(obj, origin)) { continue; } var type = obj.GetType(); if (!type.IsClass && !type.IsInterface) { continue; } var meta = MetaEntity.Locate(obj); var map = meta.UberMap ?? Repository.LocateUberMap(type); if (map == null) { throw new NotFoundException("Cannot find map for type '{0}'.".FormatWith(type.EasyName())); } var op = map.Delete(obj); try { ((IUberOperation)op).OnExecute(origin: Entity); } finally { op.Dispose(); } } list.Clear(); list = null; list = MetaEntity.GetDependencies(Map, MemberDependencyMode.Child); foreach (var obj in list) { if (obj == null) { continue; } if (object.ReferenceEquals(obj, origin)) { continue; } var type = obj.GetType(); if (!type.IsClass && !type.IsInterface) { continue; } var meta = MetaEntity.Locate(obj); var map = meta.UberMap ?? Repository.LocateUberMap(type); if (map == null) { throw new NotFoundException("Cannot find map for type '{0}'.".FormatWith(type.EasyName())); } var op = map.Delete(obj); try { ((IUberOperation)op).OnExecute(origin: Entity); } finally { op.Dispose(); } } list.Clear(); list = null; cmd = Map.GenerateDeleteCommand(Entity); if (cmd != null) { DebugEx.IndentWriteLine("\n- Executing '{0}'...", cmd); try { MetaEntity.ValidateRowVersion(); int n = cmd.Execute(); } finally { DebugEx.Unindent(); } } Map.Detach(Entity); change = new ChangeEntry(ChangeType.Delete, Map, Entity); Repository.ChangeEntries.Add(change); list = MetaEntity.GetDependencies(Map, MemberDependencyMode.Parent); foreach (var obj in list) { if (obj == null) { continue; } if (object.ReferenceEquals(obj, origin)) { continue; } var type = obj.GetType(); if (!type.IsClass && !type.IsInterface) { continue; } var meta = MetaEntity.Locate(obj); var map = meta.UberMap ?? Repository.LocateUberMap(type); if (map == null) { throw new NotFoundException("Cannot find map for type '{0}'.".FormatWith(type.EasyName())); } change = new ChangeEntry(ChangeType.Refresh, map, obj); Repository.ChangeEntries.Add(change); } } finally { if (cmd != null) { cmd.Dispose(); } cmd = null; if (list != null) { list.Clear(); } list = null; DebugEx.Unindent(); } } }
/// <summary> /// Invoked to execute this operation. /// </summary> internal void OnExecute(object origin = null) { lock (Repository.MasterLock) { List <object> list = null; ChangeEntry change = null; ChangeType changeType = MetaEntity.UberMap == null ? ChangeType.Insert : ChangeType.Update; IEnumerableCommand cmd = null; IRecord rec = null; DebugEx.IndentWriteLine("\n- Preparing '{1}({0})'...", MetaEntity, changeType); try { if (changeType == ChangeType.Update) { list = MetaEntity.GetRemovedChilds(forgetRemoved: true); foreach (var obj in list) { if (obj == null) { continue; } if (object.ReferenceEquals(obj, origin)) { continue; } var type = obj.GetType(); if (!type.IsClass && !type.IsInterface) { continue; } var meta = MetaEntity.Locate(obj); var map = meta.UberMap ?? Repository.LocateUberMap(type); if (map == null) { throw new NotFoundException("Cannot find map for type '{0}'.".FormatWith(type.EasyName())); } var op = map.Delete(obj); try { ((IUberOperation)op).OnExecute(origin: Entity); } finally { op.Dispose(); } } list.Clear(); list = null; } list = MetaEntity.GetDependencies(Map, MemberDependencyMode.Parent); foreach (var obj in list) { if (obj == null) { continue; } if (object.ReferenceEquals(obj, origin)) { continue; } var type = obj.GetType(); if (!type.IsClass && !type.IsInterface) { continue; } var meta = MetaEntity.Locate(obj); var map = meta.UberMap ?? Repository.LocateUberMap(type); if (map == null) { throw new NotFoundException("Cannot find map for type '{0}'.".FormatWith(type.EasyName())); } if (meta.DoesMetaNeedSave(true, origin: Entity)) { var op = map.Save(obj); try { ((IUberOperation)op).OnExecute(origin: Entity); } finally { op.Dispose(); } } else { change = new ChangeEntry(ChangeType.Refresh, map, obj); Repository.ChangeEntries.Add(change); } } if (changeType == ChangeType.Insert) { cmd = Map.GenerateInsertCommand(Entity); if (cmd == null) { throw new CannotCreateException("Cannot create an insert command for '{0}'.".FormatWith(MetaEntity)); } DebugEx.IndentWriteLine("\n- Executing '{0}'...", cmd); try { rec = (IRecord)cmd.First(); if (rec == null) { throw new CannotExecuteException("Cannot execute '{0}'.".FormatWith(cmd)); } } finally { DebugEx.Unindent(); } MetaEntity.Record = rec; Map.LoadEntity(rec, Entity); MetaEntity.UberMap = Map; if (Map.TrackEntities) { Map.MetaEntities.Add(MetaEntity); } MetaEntity.Completed = false; change = new ChangeEntry(ChangeType.Insert, Map, Entity); Repository.ChangeEntries.Add(change); } else if (changeType == ChangeType.Update) { cmd = Map.GenerateUpdateCommand(Entity); if (cmd != null) { DebugEx.IndentWriteLine("\n- Executing '{0}'...", cmd); try { MetaEntity.ValidateRowVersion(); rec = (IRecord)cmd.First(); if (rec == null) { throw new CannotExecuteException("Cannot execute '{0}'.".FormatWith(cmd)); } } finally { DebugEx.Unindent(); } MetaEntity.Record = rec; Map.LoadEntity(rec, Entity); MetaEntity.Completed = false; change = new ChangeEntry(ChangeType.Update, Map, Entity); Repository.ChangeEntries.Add(change); } else { change = new ChangeEntry(ChangeType.Refresh, Map, Entity); Repository.ChangeEntries.Add(change); } } list = MetaEntity.GetDependencies(Map, MemberDependencyMode.Child); foreach (var obj in list) { if (obj == null) { continue; } if (object.ReferenceEquals(obj, origin)) { continue; } var type = obj.GetType(); if (!type.IsClass && !type.IsInterface) { continue; } var meta = MetaEntity.Locate(obj); var map = meta.UberMap ?? Repository.LocateUberMap(type); if (map == null) { throw new NotFoundException("Cannot find map for type '{0}'.".FormatWith(type.EasyName())); } if (meta.DoesMetaNeedSave(cascade: true)) { var op = map.Save(obj); try { ((IUberOperation)op).OnExecute(origin: Entity); } finally { op.Dispose(); } } else { change = new ChangeEntry(ChangeType.Refresh, map, obj); Repository.ChangeEntries.Add(change); } } list.Clear(); list = null; } finally { if (cmd != null) { cmd.Dispose(); } cmd = null; if (list != null) { list.Clear(); } list = null; DebugEx.Unindent(); } } }
/// <summary> /// Generates an update core command for the given entity, or returns null if such /// command cannot be generated for whatever reasons. /// </summary> internal static IUpdateCommand GenerateUpdateCommand(this IUberMap map, object entity) { if (entity == null) { return(null); } if (map == null || map.IsDisposed || !map.IsValidated) { return(null); } IUpdateCommand cmd = null; MetaEntity meta = MetaEntity.Locate(entity, create: true); if (meta.Record == null) { var record = new Core.Concrete.Record(map.Schema); map.WriteRecord(entity, record); meta.Record = record; } var changes = meta.GetRecordChanges(); if (changes == null) { return(null); } var num = changes.Schema.Count(x => !x.IsReadOnlyColumn); if (num != 0) { var id = map.ExtractId(meta.Record); if (id != null) { cmd = map.Link.Engine.CreateUpdateCommand(map.Link, x => map.Table); if (map.Discriminator != null) { cmd.Where(map.Discriminator); } var tag = new DynamicNode.Argument("x"); for (int i = 0; i < id.Count; i++) { var left = new DynamicNode.GetMember(tag, id.Schema[i].ColumnName); var bin = new DynamicNode.Binary(left, ExpressionType.Equal, id[i]); cmd.Where(x => bin); left.Dispose(); bin.Dispose(); } for (int i = 0; i < changes.Count; i++) { if (changes.Schema[i].IsReadOnlyColumn) { continue; } var node = new DynamicNode.SetMember(tag, changes.Schema[i].ColumnName, changes[i]); cmd.Columns(x => node); node.Dispose(); } tag.Dispose(); id.Dispose(); } } changes.Dispose(disposeSchema: true); return(cmd); }
/// <summary> /// Executes the command if it has not been executed yet in this instance. Returns true /// if a new record is available, or false otherwise. /// </summary> public bool MoveNext() { if (IsDisposed) { throw new ObjectDisposedException(this.ToString()); } try { if (_CoreCommand == null) // Preparing iterations... { _CoreCommand = _Command.GenerateCoreCommand(); if (_CoreCommand == null) { throw new CannotCreateException("Cannot create a core query command for this '{0}'.".FormatWith(this)); } _Enumerator = _CoreCommand.GetEnumerator(); if (_Enumerator == null) { throw new CannotCreateException("Cannot create a core query enumerator for this '{0}'.".FormatWith(this)); } DebugEx.IndentWriteLine("\n- Query: entering '{0}'...".FormatWith(_Command.TraceString())); _Indented = true; } _Current = null; bool r = _Enumerator.MoveNext(); if (r) // Current iteration... { var record = _Enumerator.CurrentRecord; var disposable = true; var map = _Command.Map; lock (map.Repository.MasterLock) { if (map.TrackEntities) { var node = map.MetaEntities.FindNode(record); if (node != null) { foreach (var meta in node) { T obj = (T)meta.Entity; if (obj == null) { DebugEx.IndentWriteLine("\n- Query: meta invalid '{0}'.".FormatWith(meta)); DebugEx.Unindent(); continue; } if (_Current == null) { _Current = obj; } else if (map.ProxyType != null && map.ProxyType != _Current.GetType() && map.ProxyType == obj.GetType()) { _Current = obj; } if (meta.Completed) { DebugEx.IndentWriteLine("\n- Query: meta completed '{0}'.".FormatWith(meta)); DebugEx.Unindent(); continue; } DebugEx.IndentWriteLine("\n- Query: hydrating meta '{0}'.".FormatWith(meta)); meta.Record = record.Clone(); map.LoadEntity(meta.Record, obj); map.CompleteMembers(meta); DebugEx.Unindent(); } } } if (_Current == null) { DebugEx.IndentWriteLine("\n- Query: new meta '{0}({1})'.".FormatWith(map.EntityType.EasyName(), record)); _Current = map.NewEntity(); var meta = MetaEntity.Locate(_Current); meta.Record = record; disposable = false; meta.UberMap = map; if (map.TrackEntities) { map.MetaEntities.Add(meta); } map.LoadEntity(meta.Record, _Current); map.CompleteMembers(meta); DebugEx.Unindent(); } } if (disposable) { record.Dispose(); } } else { Reset(); } return(r); } catch { try { Dispose(); } catch { } throw; } }