/// <summary> /// Adds neighborhood actions that will announce adding or changing of a specific activity to all followers of the proximity server. /// </summary> /// <param name="ActionType">Type of action on the identity profile.</param> /// <param name="ActivityId">User defined activity ID.</param> /// <param name="OwnerIdentityId">Network identifier of the action owner's identity.</param> /// <param name="AdditionalData">Additional data to store with the action.</param> /// <returns> /// true if at least one new action was added to the database, false otherwise. /// <para> /// This function can throw database exception and the caller is expected to call it within try/catch block. /// </para> /// </returns> /// <remarks>The caller of this function is responsible starting a database transaction with FollowerLock and NeighborhoodActionLock locks.</remarks> public async Task <bool> AddActivityFollowerActionsAsync(NeighborhoodActionType ActionType, uint ActivityId, byte[] OwnerIdentityId, string AdditionalData = null) { log.Trace("(ActionType:{0},ActivityId:{1},OwnerIdentityId:'{2}')", ActionType, ActivityId, OwnerIdentityId.ToHex()); bool res = false; List <Follower> followers = (await unitOfWork.FollowerRepository.GetAsync()).ToList(); if (followers.Count > 0) { // Disable change tracking for faster multiple inserts. unitOfWork.Context.ChangeTracker.AutoDetectChangesEnabled = false; DateTime now = DateTime.UtcNow; foreach (Follower follower in followers) { NeighborhoodAction neighborhoodAction = new NeighborhoodAction() { ServerId = follower.NetworkId, ExecuteAfter = null, TargetActivityId = ActivityId, TargetActivityOwnerId = OwnerIdentityId, Timestamp = now, Type = ActionType, AdditionalData = AdditionalData }; await InsertAsync(neighborhoodAction); res = true; log.Trace("Activity action with activity ID {0}, owner identity ID '{1}' added for follower ID '{2}'.", ActivityId, OwnerIdentityId.ToHex(), follower.NetworkId.ToHex()); } } else { log.Trace("No followers found to propagate identity profile change to."); } log.Trace("(-):{0}", res); return(res); }
/// <summary> /// Updates identity profile and creates neighborhood action to propagate the change unless the client did not want the propagation. /// </summary> /// <param name="IdentityId">Network identifier of the identity to update.</param> /// <param name="SignedProfile">Signed profile information with updated values.</param> /// <param name="ProfileImageChanged">True if profile image is about to change.</param> /// <param name="ThumbnailImageChanged">True if thumbnail image is about to change.</param> /// <param name="NoPropagation">True if the client does not want this change of profile to be propagated to the neighborhood.</param> /// <param name="IdentityNotFound">If the function fails because the identity is not found, this referenced value is set to true.</param> /// <param name="ImagesToDelete">If the function succeeds and the profile images are altered, old image files has to be deleted, in which case their hashes /// are returned in this list, which has to be initialized by the caller.</param> /// <returns>true if the function succeeds, false otherwise.</returns> public async Task <bool> UpdateProfileAndPropagateAsync(byte[] IdentityId, SignedProfileInformation SignedProfile, bool ProfileImageChanged, bool ThumbnailImageChanged, bool NoPropagation, StrongBox <bool> IdentityNotFound, List <byte[]> ImagesToDelete) { log.Trace("()"); bool res = false; bool signalNeighborhoodAction = false; bool success = false; List <byte[]> imagesToDelete = new List <byte[]>(); ProfileInformation profile = SignedProfile.Profile; DatabaseLock[] lockObjects = new DatabaseLock[] { UnitOfWork.HostedIdentityLock, UnitOfWork.FollowerLock, UnitOfWork.NeighborhoodActionLock }; using (IDbContextTransaction transaction = await unitOfWork.BeginTransactionWithLockAsync(lockObjects)) { try { HostedIdentity identity = (await GetAsync(i => (i.IdentityId == IdentityId) && (i.Cancelled == false))).FirstOrDefault(); if (identity != null) { bool isProfileInitialization = !identity.Initialized; identity.Initialized = true; identity.Version = profile.Version.ToByteArray(); identity.Name = profile.Name; GpsLocation location = new GpsLocation(profile.Latitude, profile.Longitude); identity.SetInitialLocation(location); identity.ExtraData = profile.ExtraData; identity.Signature = SignedProfile.Signature.ToByteArray(); if (ProfileImageChanged) { if (identity.ProfileImage != null) { imagesToDelete.Add(identity.ProfileImage); } identity.ProfileImage = profile.ProfileImageHash.Length != 0 ? profile.ProfileImageHash.ToByteArray() : null; } if (ThumbnailImageChanged) { if (identity.ThumbnailImage != null) { imagesToDelete.Add(identity.ThumbnailImage); } identity.ThumbnailImage = profile.ThumbnailImageHash.Length != 0 ? profile.ThumbnailImageHash.ToByteArray() : null; } Update(identity); if (!NoPropagation) { // The profile change has to be propagated to all our followers // we create database actions that will be processed by dedicated thread. NeighborhoodActionType actionType = isProfileInitialization ? NeighborhoodActionType.AddProfile : NeighborhoodActionType.ChangeProfile; string extraInfo = identity.PublicKey.ToHex(); signalNeighborhoodAction = await unitOfWork.NeighborhoodActionRepository.AddIdentityProfileFollowerActionsAsync(actionType, identity.IdentityId, extraInfo); } await unitOfWork.SaveThrowAsync(); transaction.Commit(); success = true; } else { IdentityNotFound.Value = true; } } catch (Exception e) { log.Error("Exception occurred: {0}", e.ToString()); } if (!success) { log.Warn("Rolling back transaction."); unitOfWork.SafeTransactionRollback(transaction); } unitOfWork.ReleaseLock(lockObjects); } if (success) { // Only when the function succeeds the old images can be deleted. ImagesToDelete.AddRange(imagesToDelete); // Send signal to neighborhood action processor to process the new series of actions. if (signalNeighborhoodAction) { Network.NeighborhoodActionProcessor neighborhoodActionProcessor = (Network.NeighborhoodActionProcessor)Base.ComponentDictionary[Network.NeighborhoodActionProcessor.ComponentName]; neighborhoodActionProcessor.Signal(); } res = true; } log.Trace("(-):{0}", res); return(res); }