// determines if a given entity, appearing in a given event definition, should be filtered out, // considering the entities that have already been visited - an entity is filtered out if it // appears in another even definition, which superceedes this event definition. private static bool IsSuperceeded(IEntity entity, EventDefinitionInfos infos, List <Tuple <IEntity, EventDefinitionInfos> > entities) { //var argType = meta.EventArgsType; var argType = infos.EventDefinition.Args.GetType(); // look for other instances of the same entity, coming from an event args that supercedes other event args, // ie is marked with the attribute, and is not this event args (cannot supersede itself) var superceeding = entities .Where(x => x.Item2.SupersedeTypes.Length > 0 && // has the attribute x.Item2.EventDefinition.Args.GetType() != argType && // is not the same Equals(x.Item1, entity)) // same entity .ToArray(); // first time we see this entity = not filtered if (superceeding.Length == 0) { return(false); } // fixme see notes above // delete event args does NOT superceedes 'unpublished' event if (argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(PublishEventArgs <>) && infos.EventDefinition.EventName == "Unpublished") { return(false); } // found occurences, need to determine if this event args is superceded if (argType.IsGenericType) { // generic, must compare type arguments var supercededBy = superceeding.FirstOrDefault(x => x.Item2.SupersedeTypes.Any(y => // superceeding a generic type which has the same generic type definition // fixme no matter the generic type parameters? could be different? y.IsGenericTypeDefinition && y == argType.GetGenericTypeDefinition() // or superceeding a non-generic type which is ... fixme how is this ever possible? argType *is* generic? || y.IsGenericTypeDefinition == false && y == argType)); return(supercededBy != null); } else { // non-generic, can compare types 1:1 var supercededBy = superceeding.FirstOrDefault(x => x.Item2.SupersedeTypes.Any(y => y == argType)); return(supercededBy != null); } }
// this is way too convoluted, the supersede attribute is used only on DeleteEventargs to specify // that it supersedes save, publish, move and copy - BUT - publish event args is also used for // unpublishing and should NOT be superseded - so really it should not be managed at event args // level but at event level // // what we want is: // if an entity is deleted, then all Saved, Moved, Copied, Published events prior to this should // not trigger for the entity - and even though, does it make any sense? making a copy of an entity // should ... trigger? // // not going to refactor it all - we probably want to *always* trigger event but tell people that // due to scopes, they should not expected eg a saved entity to still be around - however, now, // going to write a ugly condition to deal with U4-10764 // iterates over the events (latest first) and filter out any events or entities in event args that are included // in more recent events that Supersede previous ones. For example, If an Entity has been Saved and then Deleted, we don't want // to raise the Saved event (well actually we just don't want to include it in the args for that saved event) internal static IEnumerable <IEventDefinition> FilterSupersededAndUpdateToLatestEntity(IReadOnlyList <IEventDefinition> events) { // keeps the 'latest' entity and associated event data var entities = new List <Tuple <IEntity, EventDefinitionInfos> >(); // collects the event definitions // collects the arguments in result, that require their entities to be updated var result = new List <IEventDefinition>(); var resultArgs = new List <CancellableObjectEventArgs>(); // eagerly fetch superseded arg types for each arg type var argTypeSuperceeding = events.Select(x => x.Args.GetType()) .Distinct() .ToDictionary(x => x, x => x.GetCustomAttributes <SupersedeEventAttribute>(false).Select(y => y.SupersededEventArgsType).ToArray()); // iterate over all events and filter // // process the list in reverse, because events are added in the order they are raised and we want to keep // the latest (most recent) entities and filter out what is not relevant anymore (too old), eg if an entity // is Deleted after being Saved, we want to filter out the Saved event for (var index = events.Count - 1; index >= 0; index--) { var def = events[index]; var infos = new EventDefinitionInfos { EventDefinition = def, SupersedeTypes = argTypeSuperceeding[def.Args.GetType()] }; var args = def.Args as CancellableObjectEventArgs; if (args == null) { // not a cancellable event arg, include event definition in result result.Add(def); } else { // event object can either be a single object or an enumerable of objects // try to get as an enumerable, get null if it's not var eventObjects = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject); if (eventObjects == null) { // single object, cast as an IEntity // if cannot cast, cannot filter, nothing - just include event definition in result var eventEntity = args.EventObject as IEntity; if (eventEntity == null) { result.Add(def); continue; } // look for this entity in superseding event args // found = must be removed (ie not added), else track if (IsSuperceeded(eventEntity, infos, entities) == false) { // track entities.Add(Tuple.Create(eventEntity, infos)); // track result arguments // include event definition in result resultArgs.Add(args); result.Add(def); } } else { // enumerable of objects var toRemove = new List <IEntity>(); foreach (var eventObject in eventObjects) { // extract the event object, cast as an IEntity // if cannot cast, cannot filter, nothing to do - just leave it in the list & continue var eventEntity = eventObject as IEntity; if (eventEntity == null) { continue; } // look for this entity in superseding event args // found = must be removed, else track if (IsSuperceeded(eventEntity, infos, entities)) { toRemove.Add(eventEntity); } else { entities.Add(Tuple.Create(eventEntity, infos)); } } // remove superseded entities foreach (var entity in toRemove) { eventObjects.Remove(entity); } // if there are still entities in the list, keep the event definition if (eventObjects.Count > 0) { if (toRemove.Count > 0) { // re-assign if changed args.EventObject = eventObjects; } // track result arguments // include event definition in result resultArgs.Add(args); result.Add(def); } } } } // go over all args in result, and update them with the latest instanceof each entity UpdateToLatestEntities(entities, resultArgs); // reverse, since we processed the list in reverse result.Reverse(); return(result); }