/// <summary> /// Gets accessors for all the objects specified. This method is relatively thread-safe and null-safe. Multiple threads trying to access the same accessors using this method /// are unlikely to make each other deadlock. Deadlock can still occur if one thread forgets to dispose of an accessor, or hogs it to itself. Softlocking can still occur if /// the calls are tailored to do their best to interfere with each other, but deadlocking is impossible when using this method and accessors are disposed of properly /// </summary> /// <param name="objects">The set of objects to generate accessors for</param> /// <returns>An array of accessors. Duplicate and linked accessors can exist if duplicate or linked objects are in the array.</returns> public static async Task <IAccessor[]> GetAccessors(params TSObject[] objects) { if (objects == null) { return(null); } IAccessor[] accessors = new IAccessor[objects.Length]; Dictionary <TSObject, IAccessor> mapping = new Dictionary <TSObject, IAccessor>(); HashSet <IAccessor> owned = new HashSet <IAccessor>(); try { bool restart; do { restart = false; foreach (TSObject obj in objects.Distinct()) // Loop through all objects { if (obj == null) // We don't care if it's null, it can just be null { continue; } if (mapping.ContainsKey(obj)) // We also don't care if we've done it already { continue; } IAccessor a; if (await obj.Sync.Execute(() => { if (owned.Contains(obj.Owner)) { mapping[obj] = new ProxyAccessor(obj, obj.Owner); return(true); } return(false); })) { continue; } a = await obj.CreateAccessor(100).ConfigureAwait(false); // Try to get the accessor, timeout after a 1/10th of a seccond if (a == null) // If we couldn't get an accessor quickly, then release all our accessors, and wait for this one. This is to prevent deadlocks { foreach (IAccessor access in mapping.Values) // Dispose of all our current accessors { access.Dispose(); } mapping.Clear(); // Clear them away, they're not valid anymore owned.Clear(); // Clear away the owned accessors too a = await obj.CreateAccessor().ConfigureAwait(false); // Wait for the accessor mapping[obj] = a; // Map it owned.Add(a); // Add it to the list of owned accessors restart = true; // Flag the program to retry after break; // End current cycle of attempts so we can have a fresh start again } mapping[obj] = a; // We got the accessor, so add it to our map owned.Add(a); // and also add it to the list of owned accessors for linkage detection } }while (restart); for (int i = 0; i < accessors.Length; i++) // For each accessor requested, if its not null, assign the accessor we got to it. { if (objects[i] != null) { accessors[i] = mapping[objects[i]]; } } } catch { foreach (Accessor a in mapping.Values) // Dispose of all the accessors, so we don't leave them in an unusable state { a.Dispose(); } throw; } return(accessors); // Return our array of accessors. If there were duplicate objects, then there will be duplicate accessors, same with null objects and null accessors }
protected void Wire(IOgmConnection connection) { if (connection != null) { using (ManagerAccess.Manager.ScopeOMnG()) { if (IsInverse) { if (Context.TypesManager.IsGraphEntityCollection(DestinationProperty.PropertyType)) { object coll = ObjectExtensions.Configuration.Get(DestinationProperty, connection.Destination); coll.GetType().GetMethod(nameof(ICollection <int> .Add)).Invoke(coll, new[] { connection is OgmConnection ? connection.Destination : connection }); } else { ObjectExtensions.Configuration.Set(DestinationProperty, ProxyAccessor.DynProxyGetTarget(), connection is OgmConnection ? connection.Destination : connection); } if (SourceProperty != null) { if (Context.TypesManager.IsGraphEntityCollection(SourceProperty.PropertyType)) { object coll = ObjectExtensions.Configuration.Get(SourceProperty, connection.Source); if (coll == null) { if (SourceProperty.CanWrite) { coll = Activator.CreateInstance(SourceProperty.PropertyType); ObjectExtensions.Configuration.Set(SourceProperty, connection.Source, coll); } else { throw new InvalidOperationException($"Unable to set property {SourceProperty.ReflectedType.FullName}.{SourceProperty.Name}"); } } coll.GetType().GetMethod(nameof(ICollection <int> .Add)).Invoke(coll, new[] { connection.Destination }); } else { ObjectExtensions.Configuration.Set(SourceProperty, connection.Source, connection.Destination); } } } else { if (Context.TypesManager.IsGraphEntityCollection(SourceProperty.PropertyType)) { object coll = ObjectExtensions.Configuration.Get(SourceProperty, connection.Source); coll.GetType().GetMethod(nameof(ICollection <int> .Add)).Invoke(coll, new[] { connection is OgmConnection ? connection.Destination : connection }); } else { ObjectExtensions.Configuration.Set(SourceProperty, ProxyAccessor.DynProxyGetTarget(), connection is OgmConnection ? connection.Destination : connection); } if (DestinationProperty != null) { if (Context.TypesManager.IsGraphEntityCollection(DestinationProperty.PropertyType)) { object coll = ObjectExtensions.Configuration.Get(DestinationProperty, connection.Destination); if (coll == null) { if (DestinationProperty.CanWrite) { coll = Activator.CreateInstance(DestinationProperty.PropertyType); ObjectExtensions.Configuration.Set(DestinationProperty, connection.Destination, coll); } else { throw new InvalidOperationException($"Unable to set property {DestinationProperty.ReflectedType.FullName}.{DestinationProperty.Name}"); } } coll.GetType().GetMethod(nameof(ICollection <int> .Add)).Invoke(coll, new[] { connection.Source }); } else { ObjectExtensions.Configuration.Set(DestinationProperty, connection.Destination, connection.Source); } } } } } }