public void CacheCommittedWithTransactionTest() { string applicationName = "CacheWithinTransactionScopeTest"; string cacheKey = "CacheWithinTransactionScopeTest.CacheKey"; ICache cache = SpringContext.Current.GetObject<ICache>(); using (MembershipDataContext ctx = DataContextFactory.Create<MembershipDataContext>()) using (TransactionScope transactionScope = new TransactionScope()) { // create a new application to database Application application = new Application { ApplicationName = applicationName, LoweredApplicationName = applicationName.ToLowerInvariant(), Description = "" }; ctx.Applications.InsertOnSubmit(application); ctx.SubmitChanges(); // save it into cache for 1 hour cache.Add(cacheKey, application, new TimeSpan(1, 0, 0), CachePriorityTypes.High); // the application should exist in both database and cache in the transaction Assert.AreEqual(1, ctx.Applications.Count(app => app.ApplicationName == applicationName), "The application should exist in database."); Assert.IsNotNull(cache.Get(cacheKey), "The application should exist in cache."); // complete the transaction transactionScope.Complete(); } using (MembershipDataContext ctx = DataContextFactory.Create<MembershipDataContext>()) { // the application should exist in both database and cache in the transaction Assert.AreEqual(1, ctx.Applications.Count(app => app.ApplicationName == applicationName), "The application should exist in database."); Assert.IsNotNull(cache.Get(cacheKey), "The application should exist in cache."); // clear testing data. ctx.Applications.Delete(app => app.ApplicationName == applicationName); cache.Remove(cacheKey); } }
/// <summary> /// Save organization business object. /// If organizationObject.Id equals Guid.Empty, it means to save a new organization. /// Otherwise it's updating an existed organization. /// </summary> /// <param name="organizationObject"></param> private void SaveOrganization(OrganizationObject organizationObject) { try { Organization organization = null; using (TransactionScope transactionScope = new TransactionScope()) using (MembershipDataContext ctx = DataContextFactory.Create<MembershipDataContext>()) { using (ValidationScope validationScope = new ValidationScope(true)) { // check duplicate organization name int existedOrganizationCount = (from org in ctx.Organizations where org.OrganizationName == organizationObject.OrganizationName && org.ApplicationId == this.authenticationContext.ApplicationId && org.OrganizationId != organizationObject.OrganizationId select org).Count(); if (existedOrganizationCount > 0) validationScope.Error(Resources.ExistedOrganizationName, organizationObject.OrganizationName); // check duplicate organization code if (!string.IsNullOrEmpty(organizationObject.OrganizationCode)) { existedOrganizationCount = (from org in ctx.Organizations where org.OrganizationCode == organizationObject.OrganizationCode && org.ApplicationId == this.authenticationContext.ApplicationId && org.OrganizationId != organizationObject.OrganizationId select org).Count(); if (existedOrganizationCount > 0) validationScope.Error(Resources.ExistedOrganizationCode, organizationObject.OrganizationCode); } // validate organization type OrganizationTypeObject organizationTypeObject = this.GetOrganizationType(organizationObject.OrganizationTypeId); if (organizationTypeObject == null) validationScope.Error(Resources.InvalidOrganizationTypeID); // validate organization located area if (organizationObject.Hierarchies != null && organizationObject.Hierarchies.Count > 0) { foreach (string hierarchyType in organizationObject.Hierarchies.Keys) { HierarchyDataObject hierarchyDataObject = hierarchyApi.GetHierarchyData(organizationObject.Hierarchies[hierarchyType]); if (hierarchyDataObject == null || !string.Equals(hierarchyDataObject.HierarchyType, hierarchyType, StringComparison.OrdinalIgnoreCase)) validationScope.Error(Resources.InvalidHierarchyAssociatedWithOrganization, hierarchyType, hierarchyDataObject.HierarchyDataId); } } // check circular reference of parent if (organizationObject.ParentOrganizationId.HasValue) { if (organizationObject.OrganizationId == organizationObject.ParentOrganizationId.Value) validationScope.Error(Resources.InvalidParentOrganizationID); if (organizationObject.OrganizationId != organizationObject.ParentOrganizationId.Value) { List<string> existedOrganizationNames = new List<string>(); HashSet<Guid> existedOrganizationIds = new HashSet<Guid> { organizationObject.OrganizationId }; OrganizationObject parentOrganizationObject = this.GetOrganization(organizationObject.ParentOrganizationId.Value); if (parentOrganizationObject == null) validationScope.Error(Resources.InvalidParentOrganizationID); else { this.VerifyStatusAgainstParentOrganization(parentOrganizationObject, organizationObject.Status, organizationObject.OrganizationName, validationScope); while (parentOrganizationObject != null) { existedOrganizationNames.Add(parentOrganizationObject.OrganizationName); if (existedOrganizationIds.Contains(parentOrganizationObject.OrganizationId)) { validationScope.Error(Resources.ParentOrganizationCircularReference, FormatCircularOrganizationNames(existedOrganizationNames)); break; } existedOrganizationIds.Add(parentOrganizationObject.OrganizationId); if (!parentOrganizationObject.ParentOrganizationId.HasValue) break; parentOrganizationObject = this.GetOrganization(parentOrganizationObject.ParentOrganizationId.Value); } } } } if (organizationObject.OrganizationId == Guid.Empty) { organization = ExtensionObjectFactory.Create<Organization>(organizationObject); organization.OrganizationCode = organizationObject.OrganizationCode; organization.CreatedDate = DateTime.UtcNow; organization.CreatedBy = this.authenticationContext.User.UserId; ctx.Organizations.InsertOnSubmit(organization); } else { organization = ctx.Organizations.FirstOrDefault(org => org.OrganizationId == organizationObject.OrganizationId); if (organization == null) validationScope.Error(Resources.InvalidOrganizationID); organization.ExtensionDataTypeId = organizationObject.ExtensionDataTypeId; if (organization.OrganizationCode != organizationObject.OrganizationCode) validationScope.Error(Resources.CodeCannotUpdate); // update status of all children only when updates status from Enabled to Disabled. if (organizationObject.Status == OrganizationStatus.Disabled && organization.Status == OrganizationStatus.Enabled) UpdateSubOrganizationsStatus(ctx, new[] { organization.OrganizationId }, organizationObject.Status); } } organization.ApplicationId = this.authenticationContext.ApplicationId; organization.OrganizationName = organizationObject.OrganizationName; organization.OrganizationTypeId = organizationObject.OrganizationTypeId; organization.ParentOrganizationId = organizationObject.ParentOrganizationId; organization.Status = organizationObject.Status; organization.Description = organizationObject.Description; organization.LastUpdatedDate = DateTime.UtcNow; organization.LastUpdatedBy = this.authenticationContext.User.UserId; // remove original hierarchies and added new ones if (organizationObject.OrganizationId != Guid.Empty && organization.Hierarchies.Count > 0) ctx.OrganizationsInHierarchies.DeleteAllOnSubmit(organization.Hierarchies); if (organizationObject.Hierarchies != null && organizationObject.Hierarchies.Count > 0) { foreach (string hierarchyType in organizationObject.Hierarchies.Keys) { HierarchyDataObject hierarchyDataObject = hierarchyApi.GetHierarchyData(organizationObject.Hierarchies[hierarchyType]); if (hierarchyDataObject != null) { ctx.OrganizationsInHierarchies.InsertOnSubmit(new OrganizationsInHierarchy { ApplicationId = authenticationContext.ApplicationId, HierarchyType = hierarchyType, HierarchyDataId = organizationObject.Hierarchies[hierarchyType], Organization = organization, }); } } } organization.ParseExtensionPropertiesFrom(organizationObject); ctx.SubmitChanges(); transactionScope.Complete(); organizationObject.OrganizationId = organization.OrganizationId; organizationObject.CreatedBy = organization.CreatedBy; organizationObject.CreatedDate = organization.CreatedDate; organizationObject.LastUpdatedBy = organization.LastUpdatedBy; organizationObject.LastUpdatedDate = organization.LastUpdatedDate; } } catch (ValidationException) { throw; } catch (Exception exp) { Logger.Instance(this).Error(exp); throw; } }
/// <summary> /// Save organization business object. /// If organizationObject.Id equals Guid.Empty, it means to save a new organization. /// Otherwise it's updating an existed organization. /// </summary> /// <param name="organizationObject"></param> public void Save(OrganizationObject organizationObject) { Kit.NotNull(organizationObject, "organizationObject"); Kit.NotNull(organizationObject.OrganizationCode, "organizationObject.OrganizationCode"); Kit.NotNull(organizationObject.OrganizationName, "organizationObject.OrganizationName"); if (organizationObject.OrganizationTypeId == Guid.Empty) throw new ArgumentException(Resources.InvalidOrganizationTypeID); if (organizationObject.Status == OrganizationStatus.None) throw new ArgumentException(Resources.OrganizationStatusNotSpecified, "organizationObject.Status"); try { using (TransactionScope transactionScope = new TransactionScope()) { bool isUpdate = organizationObject.OrganizationId != Guid.Empty; this.SaveOrganization(organizationObject); // remove the cache for update if (isUpdate) base.RemoveCache(organizationObject.OrganizationId); transactionScope.Complete(); UpdateTimeZone(organizationObject); } } catch (ValidationException) { throw; } catch (Exception exp) { Logger.Instance(this).Error(exp); throw; } }
/// <summary> /// Set permissions on specified user. /// </summary> /// <param name="userId"></param> /// <param name="permissions"></param> public void SetUserPermissions(Guid userId, IEnumerable<string> permissions) { Kit.NotNull(permissions, "permissions"); try { using (TransactionScope ts = new TransactionScope()) using (MembershipDataContext ctx = DataContextFactory.Create<MembershipDataContext>()) { ctx.Permissions.Delete(p => p.UserId == userId); foreach (string permission in permissions.Distinct()) { ctx.Permissions.InsertOnSubmit(new Permission() { UserId = userId, PermissionKey = permission }); } ctx.SubmitChanges(); base.RemoveCache(userId); ts.Complete(); } } catch (Exception exp) { Logger.Instance(this).Error(exp); throw; } }
/// <summary> /// Update an existed organization type from detail panel. /// The method needs to load an existed entity by specified id and set control values to overwrite its original properties then persist it. /// </summary> /// <param name="entityId"></param> public override void Update(string entityId) { this.ValidateDataInputForm(new Guid(entityId)); using (TransactionScope ts = new TransactionScope()) { UserObject userObject = membershipApi.Get(new Guid(entityId)); userObject.ExtensionDataTypeId = this.ResolveUserExtensionDataTypeId(); if (this.UserExtensionDataForm != null) this.UserExtensionDataForm.SetObjectPropertiesFromControlValues(userObject); this.SetUserPropertiesFromControls(userObject); string password = null; if (this.TextBoxPassword != null) password = this.TextBoxPassword.Text; membershipApi.Save(userObject, password, null); if (this.OrganizationSelector != null) { OrganizationObject organizationObject = organizationApi.GetOrganization(this.OrganizationSelector.SelectedOrganization.OrganizationId); IEnumerable<Guid> roleIds = this.ResolveSelectedRoleCheckBox(organizationObject); roleApi.SetUserToRoles(userObject.UserId, roleIds); } if (this.PermissionTreeView != null) permissionApi.SetUserPermissions(userObject.UserId, @PermissionTreeView.CheckedValues); ts.Complete(); } }
public void RegisterCallbackToTransactionStatusChangedTest() { object locker = new object(); List<int> newTransactionThreadIds = new List<int>(); List<int> completedTransactionThreadIds = new List<int>(); List<int> rollbackTransactionThreadIds = new List<int>(); TransactionScopeContext.AfterNewTransactionStarted += threadId => { newTransactionThreadIds.Remove(threadId); }; TransactionScopeContext.AfterTransactionCommitted += threadId => { completedTransactionThreadIds.Remove(threadId); }; TransactionScopeContext.AfterTransactionRollback += threadId => { rollbackTransactionThreadIds.Remove(threadId); }; WaitCallback waitTransactionCompleteCallback = state => { Monitor.Enter(locker); try { newTransactionThreadIds.Add(Thread.CurrentThread.ManagedThreadId); using (TransactionScope transactionScope = new TransactionScope()) { completedTransactionThreadIds.Add(Thread.CurrentThread.ManagedThreadId); transactionScope.Complete(); } } finally { Monitor.Exit(locker); } }; WaitCallback waitTransactionRollbackCallback = state => { Monitor.Enter(locker); try { newTransactionThreadIds.Add(Thread.CurrentThread.ManagedThreadId); using (TransactionScope transactionScope = new TransactionScope()) { rollbackTransactionThreadIds.Add(Thread.CurrentThread.ManagedThreadId); // don't commit the transaction } } finally { Monitor.Exit(locker); } }; ThreadPool.QueueUserWorkItem(waitTransactionCompleteCallback); ThreadPool.QueueUserWorkItem(waitTransactionRollbackCallback); Thread.Sleep(500); Monitor.Enter(locker); try { Assert.AreEqual(0, newTransactionThreadIds.Count, "The thread Ids should be removed in the registered callback."); Assert.AreEqual(0, completedTransactionThreadIds.Count, "The thread Ids should be removed in the registered callback."); Assert.AreEqual(0, rollbackTransactionThreadIds.Count, "The thread Ids should be removed in the registered callback."); } finally { Monitor.Exit(locker); } }
/// <summary> /// Create a new role from detail panel and return the id. /// The method needs to create a new entity and set control values to its properties then persist it. /// </summary> /// <returns>returns the id of new created role.</returns> public override string Create() { this.ValidateInput(Guid.Empty); RoleObject roleObject = new RoleObject { RoleName = this.TextBoxName.Text, Description = this.TextBoxDescription.Text, Domain = authenticationContext.TempVariables["Domain.Value"] as string, Predefined = false }; using (TransactionScope ts = new TransactionScope()) { roleApi.Save(roleObject); permissionApi.SetRolePermissions(roleObject.RoleId, this.PermissionTreeView.CheckedValues); ts.Complete(); } return roleObject.RoleId.ToString(); }
/// <summary> /// Set the user into the roles. /// </summary> /// <param name="userId"></param> /// <param name="roleIds"></param> protected static void SetUserToRoles(Guid userId, IEnumerable<Guid> roleIds) { using (TransactionScope ts = new TransactionScope()) using (MembershipDataContext ctx = DataContextFactory.Create<MembershipDataContext>()) { ctx.UsersInRoles.Delete(uir => uir.UserId == userId); foreach (Guid roleId in roleIds) ctx.UsersInRoles.InsertOnSubmit(new UsersInRole { UserId = userId, RoleId = roleId }); ctx.SubmitChanges(); ts.Complete(); } }
/// <summary> /// Update an existed organization type from detail panel. /// The method needs to load an existed entity by specified id and set control values to overwrite its original properties then persist it. /// </summary> /// <param name="entityId"></param> public override void Update(string entityId) { Guid roleId = new Guid(entityId); this.ValidateInput(roleId); RoleObject roleObject = new RoleObject { RoleId = roleId }; roleObject.RoleName = this.TextBoxName.Text; roleObject.Description = this.TextBoxDescription.Text; roleObject.Domain = authenticationContext.TempVariables["Domain.Value"] as string; using (TransactionScope ts = new TransactionScope()) { roleApi.Save(roleObject); permissionApi.SetRolePermissions(roleObject.RoleId, this.PermissionTreeView.CheckedValues); ts.Complete(); } }
/// <summary> ///Bind the files with id of the external object. /// </summary> /// <param name="relationshipType"></param> /// <param name="externalObjectId"></param> /// <param name="fileIds"></param> public void Bind(string relationshipType, Guid externalObjectId, IEnumerable<Guid> fileIds) { try { using (TransactionScope ts = new TransactionScope()) using (FileManagementDataContext ctx = DataContextFactory.Create<FileManagementDataContext>()) { List<Guid> associatedFileIds = (from f in ctx.FileBindings where f.ApplicationId == applicationContext.ApplicationId && f.RelationshipType == relationshipType && f.ExtenalObjectId == externalObjectId select f.FileId).Distinct().ToList(); foreach (Guid fileId in fileIds) { if (associatedFileIds.Contains(fileId)) continue; ctx.FileBindings.InsertOnSubmit(new FileBinding { ApplicationId = this.applicationContext.ApplicationId, RelationshipType = relationshipType, ExtenalObjectId = externalObjectId, FileId = fileId }); } ctx.SubmitChanges(); ts.Complete(); } } catch (Exception exp) { Logger.Instance(this).Error(exp); throw; } }
/// <summary> /// Save organization type object. /// </summary> /// <param name="organizationTypeObject"></param> /// <exception cref="ValidationException">etc organization type name does exist.</exception> public void Save(OrganizationTypeObject organizationTypeObject) { Kit.NotNull(organizationTypeObject, "organizationTypeObject"); Kit.NotNull(organizationTypeObject.Name, "organizationTypeObject.Name"); if (!this.platformConfiguration.Domains.Select(d => d.Value).Contains(organizationTypeObject.Domain)) throw new ArgumentException(string.Format(Resources.InvalidOrganizationTypeDomain, organizationTypeObject.Domain), "organizationTypeObject.Domain"); Kit.NotNull(organizationTypeObject, "organizationTypeObject"); Kit.NotNull(organizationTypeObject.Name, "organizationTypeObject.Name"); if (!this.platformConfiguration.Domains.Select(d => d.Value).Contains(organizationTypeObject.Domain)) throw new ArgumentException(string.Format(Resources.InvalidOrganizationTypeDomain, organizationTypeObject.Domain), "organizationTypeObject.Domain"); try { using (TransactionScope ts = new TransactionScope()) using (MembershipDataContext ctx = DataContextFactory.Create<MembershipDataContext>()) { OrganizationType organizationType = null; DeleteStatus originalDeleteStatus = DeleteStatus.Deleted; using (ValidationScope validationScope = new ValidationScope(true)) { if (ctx.OrganizationTypes.Where(x => x.Name == organizationTypeObject.Name && x.ApplicationId == this.authenticationContext.ApplicationId && x.OrganizationTypeId != organizationTypeObject.OrganizationTypeId).Count() > 0) { validationScope.Error(Resources.ExistedOrganizationTypeName, organizationTypeObject.Name); } if (organizationTypeObject.OrganizationTypeId == Guid.Empty) { organizationType = new OrganizationType { ApplicationId = this.authenticationContext.ApplicationId }; ctx.OrganizationTypes.InsertOnSubmit(organizationType); } else { organizationType = ctx.OrganizationTypes.FirstOrDefault(orgType => orgType.OrganizationTypeId == organizationTypeObject.OrganizationTypeId); if (organizationType == null) validationScope.Error(Resources.InvalidOrganizationTypeID); originalDeleteStatus = organizationType.DeleteStatus; } } // set organization type properties. organizationType.Domain = organizationTypeObject.Domain; organizationType.Name = organizationTypeObject.Name; organizationType.Description = organizationTypeObject.Description; organizationType.LastUpdatedDate = DateTime.UtcNow; organizationType.Predefined = organizationTypeObject.Predefined; organizationType.DeleteStatus = organizationTypeObject.DeleteStatus; // if disable an existed organization type if (organizationTypeObject.OrganizationTypeId != Guid.Empty && organizationTypeObject.DeleteStatus == DeleteStatus.Deleted && organizationTypeObject.DeleteStatus != originalDeleteStatus) { // remove the cache copy for disabled organizations. IEnumerable<Guid> disabledOrganizationIds = (from org in ctx.Organizations where org.ApplicationId == this.authenticationContext.ApplicationId && org.OrganizationTypeId == organizationTypeObject.OrganizationTypeId && org.Status != OrganizationStatus.Disabled select org.OrganizationId).ToList(); foreach (Guid disabledOrganizationId in disabledOrganizationIds) base.RemoveCache(disabledOrganizationId); // batch disable the organizations by sql command string command = string.Format(CultureInfo.InvariantCulture, "UPDATE {0} SET Status={1} WHERE ApplicationId='{2}' AND OrganizationTypeId='{3}' AND Status<>{1}", ctx.Mapping.GetTable(typeof(Organization)).TableName, (int)OrganizationStatus.Disabled, this.authenticationContext.ApplicationId, organizationTypeObject.OrganizationTypeId); ctx.ExecuteCommand(command); } ctx.SubmitChanges(); ts.Complete(); organizationTypeObject.OrganizationTypeId = organizationType.OrganizationTypeId; organizationTypeObject.LastUpdatedDate = organizationType.LastUpdatedDate; // remove cache. base.RemoveCache(CacheKey4AllOrganizationTypes); } } catch (ValidationException) { throw; } catch (Exception exp) { Logger.Instance(this).Error(exp); throw; } }
/// <summary> /// Create sequence number on specified type for special object id. /// The method generates the sequence number suppressing transaction scope which means the generated sequence number cannot be rollback. /// </summary> /// <param name="objectId">E.g. we want each company in the system owns standalone sequence number generation. That means company A can have a sequence number 999 as company B has it meanwhile.</param> /// <param name="sequenceNoType">Sequence number type that means a company for above example can has multiple sequence number generation path. E.g. company A has a Order sequence number 999 as it has a Product sequence number 999 meanwhile.</param> /// <returns></returns> public long Create(Guid objectId, string sequenceNoType) { lock (syncObj) { try { using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Suppress)) using (MembershipDataContext ctx = DataContextFactory.Create<MembershipDataContext>()) { SequenceNo sequenceNo = ctx.SequenceNos.Where(s => s.ObjectId.Equals(objectId) && s.Type.Equals(sequenceNoType) && s.ApplicationId == this.authenticationContext.ApplicationId).FirstOrDefault(); if (sequenceNo == null) { sequenceNo = new SequenceNo() { ApplicationId = this.authenticationContext.ApplicationId, ObjectId = objectId, Type = sequenceNoType, Number = 1 }; ctx.SequenceNos.InsertOnSubmit(sequenceNo); } else { sequenceNo.Number++; } ctx.SubmitChanges(); ts.Complete(); return sequenceNo.Number; } } catch (Exception exp) { Logger.Instance(this).Error(exp); throw; } } }
/// <summary> /// Create sequence numbers on specified type for special object id. /// The method generates the sequence number suppressing transaction scope which means the generated sequence number cannot be rollback. /// </summary> /// <param name="objectId">E.g. we want each company in the system owns standalone sequence number generation. That means company A can have a sequence number 999 as company B has it meanwhile.</param> /// <param name="sequenceNoType">Sequence number type that means a company for above example can has multiple sequence number generation path. E.g. company A has a Order sequence number 999 as it has a Product sequence number 999 meanwhile.</param> /// <param name="sequenceNoCount">Indicates how many sequence number is required.</param> /// <returns></returns> public IEnumerable<long> Create(Guid objectId, string sequenceNoType, ushort sequenceNoCount) { if (sequenceNoCount <= 0) throw new ArgumentException("The argument \"sequenceNoCount\" should be more than zero.", "sequenceNoCount"); lock (syncObj) { try { using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Suppress)) using (MembershipDataContext ctx = DataContextFactory.Create<MembershipDataContext>()) { SequenceNo sequenceNo = ctx.SequenceNos.Where(s => s.ObjectId.Equals(objectId) && s.Type.Equals(sequenceNoType) && s.ApplicationId == this.authenticationContext.ApplicationId).FirstOrDefault(); long startSequenceNo; if (sequenceNo == null) { sequenceNo = new SequenceNo() { ApplicationId = this.authenticationContext.ApplicationId, ObjectId = objectId, Type = sequenceNoType, Number = sequenceNoCount }; startSequenceNo = 1; ctx.SequenceNos.InsertOnSubmit(sequenceNo); } else { startSequenceNo = sequenceNo.Number + 1; sequenceNo.Number += sequenceNoCount; } ctx.SubmitChanges(); ts.Complete(); List<long> sequenceNumbers = new List<long>(); for (long i = startSequenceNo; i < startSequenceNo + sequenceNoCount; i++) sequenceNumbers.Add(i); return sequenceNumbers; } } catch (Exception exp) { Logger.Instance(this).Error(exp); throw; } } }