private void ReleaseLockRecursively(bool forceRelease) { LoadAllChildren(false); foreach (Entity e in EnumerateAllChildren()) { e.ReleaseLockRecursively(forceRelease); } if (!forceRelease) { CheckConcurrency(); } string sql = "spReleaseEntityLock"; using (SqlCommand cmd = Context.CreateStoredProcedureCommand(sql)) { AppendBasicParameters(cmd); cmd.Parameters.Add("@LockOwner", SqlDbType.UniqueIdentifier).Value = Context.ContextGuid; cmd.Parameters.Add("@ConcurrencyVersion", SqlDbType.Binary, 8).Direction = ParameterDirection.Output; cmd.Parameters.Add("RETVAL", SqlDbType.Int).Direction = ParameterDirection.ReturnValue; cmd.ExecuteNonQuery(); int retval = (int)cmd.Parameters["RETVAL"].Value; this.lockOwner = Guid.Empty; this.concurrencyVersion = BitConverter.ToInt64((byte[])cmd.Parameters["@ConcurrencyVersion"].Value, 0); } Context.LogEvent(new Jhu.Graywulf.Logging.Event("Jhu.Graywulf.Registry.Entity.ReleaseLock", this.guid)); }
/// <summary> /// Moves the entity up among its siblings. /// </summary> /// <remarks> /// Moves the entity up among its siblings by decreasing its <see cref="Number" /> property. /// All siblings are reorganized to keep the values of <b>Numbers</b> contigous. /// </remarks> public void Move(EntityMoveDirection direction) { string sql; if (direction == EntityMoveDirection.Up) { sql = "spMoveUpEntity"; } else { sql = "spMoveDownEntity"; } CheckConcurrency(); using (SqlCommand cmd = Context.CreateStoredProcedureCommand(sql)) { AppendBasicParameters(cmd); cmd.Parameters.Add("RETVAL", SqlDbType.Int).Direction = ParameterDirection.ReturnValue; cmd.ExecuteNonQuery(); var retval = (int)cmd.Parameters["RETVAL"].Value; if (retval >= 0) { this.number = retval; } } Context.LogEvent(new Jhu.Graywulf.Logging.Event("Jhu.Graywulf.Registry.Entity.Move", this.guid)); }
/// <summary> /// Check whether the entity was modified in the database since it was loaded. /// </summary> /// <remarks> /// Refer to the Developer's Guide about the optimistic concurrency model. /// The <see cref="Context" /> property must have a value of a valid object context with open database connection. /// </remarks> /// <exception cref="InvalidConcurrencyVersionException">Thrown when a potential concurrency issue is detected.</exception> /// <exception cref="LockingCollisionException">TODO</exception> protected void CheckConcurrency() { // TODO: implement locking for move operations (don't allow move if any locked) string sql = "spCheckEntityConcurrency"; using (SqlCommand cmd = Context.CreateStoredProcedureCommand(sql)) { AppendBasicParameters(cmd); cmd.Parameters.Add("@LockOwner", SqlDbType.UniqueIdentifier).Value = Context.JobGuid; cmd.Parameters.Add("@ConcurrencyVersion", SqlDbType.Binary, 8).Value = BitConverter.GetBytes(this.concurrencyVersion); cmd.Parameters.Add("RETVAL", SqlDbType.Int).Direction = ParameterDirection.ReturnValue; cmd.ExecuteNonQuery(); int retval = (int)cmd.Parameters["RETVAL"].Value; if (retval < 0) { Jhu.Graywulf.Logging.Event e = new Jhu.Graywulf.Logging.Event("Jhu.Graywulf.Registry.Entity.CheckConcurrency", this.guid);; switch (retval) { case -1: e.Message = LogMessages.InvalidConcurrencyVersion; Context.LogEvent(e); throw new InvalidConcurrencyVersionException(); case -2: e.Message = LogMessages.LockingCollision; Context.LogEvent(e); throw new LockingCollisionException(); } } } }
private void DeleteRecursively(bool forceOverwrite) { LoadAllChildren(true); // Delete should go in reverse order in order to prevent // concurrency version exceptions foreach (Entity e in EnumerateAllChildren().Reverse <Entity>()) { e.DeleteRecursively(forceOverwrite); } if (!forceOverwrite) { CheckConcurrency(); } string sql = "spDeleteEntity"; using (SqlCommand cmd = Context.CreateStoredProcedureCommand(sql)) { AppendBasicParameters(cmd); cmd.Parameters.Add("@ConcurrencyVersion", SqlDbType.Binary, 8).Direction = ParameterDirection.Output; cmd.ExecuteNonQuery(); this.concurrencyVersion = BitConverter.ToInt64((byte[])cmd.Parameters["@ConcurrencyVersion"].Value, 0); this.deleted = true; this.dateDeleted = DateTime.Now; this.userGuidDeleted = Context.UserGuid; } Context.LogEvent(new Jhu.Graywulf.Logging.Event("Jhu.Graywulf.Registry.Entity.Delete", this.guid)); }
/// <summary> /// Authenticate user /// </summary> /// <param name="userName"></param> /// <param name="password"></param> private User LoginUserInternal(Entity parent, string nameOrEmail, string password) { var user = new User(Context); // Load user from the database string sql = "spLoginUser"; using (var cmd = Context.CreateStoredProcedureCommand(sql)) { cmd.Parameters.Add("@ParentGuid", SqlDbType.UniqueIdentifier).Value = parent.Guid; cmd.Parameters.Add("@NameOrEmail", SqlDbType.NVarChar, 50).Value = nameOrEmail; using (var dr = cmd.ExecuteReader()) { if (!dr.Read()) { throw new EntityNotFoundException(ExceptionMessages.LoginFailed); } user.LoadFromDataReader(dr); } } // Compute password hash bool hashok = true; byte[] hash = User.ComputePasswordHash(password); // Compare the hash with the one in the database if (hash.Length != user.PasswordHash.Length) { hashok = false; } else { for (int i = 0; i < hash.Length; i++) { if (hash[i] != user.PasswordHash[i]) { hashok = false; break; } } } if (!hashok) { throw new SecurityException(ExceptionMessages.LoginFailed); } // Update context Context.UserGuid = user.Guid; Context.UserName = user.Name; Context.LogEvent(new Jhu.Graywulf.Logging.Event("Jhu.Graywulf.Registry.UserFactory.LoginUser", user.Guid)); return(user); }
/// <summary> /// Modifies the already existing entity in the database. /// </summary> /// <remarks> /// The <see cref="Context" /> property must have a value of a valid object context with open database connection. /// </remarks> /// <param name="forceOverwrite">Determines if concurrency issues are ignored.</param> /// <exception cref="InvalidConcurrencyVersionException"> /// Thrown when someone else modified the entity since it was loaded from the database. /// </exception> protected virtual void Modify(bool forceOverwrite) { if (!forceOverwrite) { CheckConcurrency(); } // --- Modify record in the Entities table --- this.dateModified = DateTime.Now; string sql = "spModifyEntity"; using (SqlCommand cmd = Context.CreateStoredProcedureCommand(sql)) { AppendBasicParameters(cmd); cmd.Parameters.Add("@ConcurrencyVersion", SqlDbType.Binary, 8).Direction = ParameterDirection.Output; AppendEntityCreateModifyParameters(cmd); cmd.ExecuteNonQuery(); this.concurrencyVersion = BitConverter.ToInt64((byte[])cmd.Parameters["@ConcurrencyVersion"].Value, 0); } // --- Modify record in the type specific table if (DBHelpers[this.GetType()].HasColumns) { using (var cmd = DBHelpers[this.GetType()].CreateUpdateCommand(this)) { AppendBasicParameters(cmd); cmd.ExecuteNonQuery(); } } Context.LogEvent(new Jhu.Graywulf.Logging.Event("Jhu.Graywulf.Registry.Entity.Modify", this.guid)); }
/// <summary> /// Saves the newly created entity to the database. /// </summary> /// <remarks> /// This function is called by the <see cref="Save()"/> function of the <see cref="Entity" /> class /// through the <see cref="Create"/> function in the derived classes. /// The <see cref="Context" /> property must have a value of a valid object context with open database connection. /// </remarks> protected virtual void Create() { // Generate a new Guid for the entity guid = Guid.NewGuid(); // --- Create record in the Entities table --- string sql = "spCreateEntity"; using (var cmd = Context.CreateStoredProcedureCommand(sql)) { AppendBasicParameters(cmd); cmd.Parameters.Add("@ConcurrencyVersion", SqlDbType.Binary, 8).Direction = ParameterDirection.Output; cmd.Parameters.Add("@ParentGuid", SqlDbType.UniqueIdentifier).Value = parentReference.Guid; cmd.Parameters.Add("@EntityType", SqlDbType.Int).Value = (int)EntityType; // always use property cmd.Parameters.Add("@Number", SqlDbType.Int).Direction = ParameterDirection.Output; AppendEntityCreateModifyParameters(cmd); cmd.ExecuteNonQuery(); // Read return values this.concurrencyVersion = BitConverter.ToInt64((byte[])cmd.Parameters["@ConcurrencyVersion"].Value, 0); this.number = (int)cmd.Parameters["@Number"].Value; } using (var cmd = DBHelpers[this.GetType()].CreateInsertCommand(this)) { AppendBasicParameters(cmd); cmd.ExecuteNonQuery(); } isExisting = true; Context.LogEvent(new Jhu.Graywulf.Logging.Event("Jhu.Graywulf.Registry.Entity.Create", this.guid)); }