private bool IsPropertyModified(HookedEntity entry, string propertyName) { var result = false; if (entry.State != EntityState.Detached) { var prop = entry.Entry.Property(propertyName); if (prop != null) { switch (entry.State) { case EntityState.Added: // OriginalValues cannot be used for entities in the Added state. result = prop.CurrentValue != null; break; case EntityState.Deleted: // CurrentValues cannot be used for entities in the Deleted state. result = prop.OriginalValue != null; break; default: result = (prop.CurrentValue != null && !prop.CurrentValue.Equals(prop.OriginalValue)) || (prop.OriginalValue != null && !prop.OriginalValue.Equals(prop.CurrentValue)); break; } } } return(result); }
protected override void OnDeleting(BaseEntity entity, HookedEntity entry) { var type = entry.Entity.GetUnproxiedType(); if (!_candidateTypes.Contains(type)) { return; } if (type == typeof(ProductAttributeOption)) { var pictureId = ((ProductAttributeOption)entry.Entity).PictureId; if (pictureId != 0) { _toDelete.Add(pictureId); } } else if (type == typeof(ProductAttributeOptionsSet)) { var options = _productAttributeService.Value.GetProductAttributeOptionsByOptionsSetId(entity.Id); _toDelete.AddRange(options.Where(x => x.PictureId != 0).Select(x => x.PictureId)); } else if (type == typeof(ProductAttribute)) { var options = _productAttributeService.Value.GetProductAttributeOptionsByAttributeId(entity.Id); _toDelete.AddRange(options.Where(x => x.PictureId != 0).Select(x => x.PictureId)); } else if (type == typeof(ProductVariantAttribute)) { var options = _productAttributeService.Value.GetProductVariantAttributeValues(entity.Id); _toDelete.AddRange(options.Where(x => x.PictureId != 0).Select(x => x.PictureId)); } }
protected override void OnDeleted(ILocalizedEntity entity, HookedEntity entry) { var entityType = entry.Entity.GetUnproxiedType(); var localizedEntities = _localizedEntityService.Value.GetLocalizedProperties(entry.Entity.Id, entityType.Name); _toDelete.AddRange(localizedEntities); }
protected override void OnDeleted(ISlugSupported entity, HookedEntity entry) { var entityType = entry.Entity.GetUnproxiedType(); var records = _urlRecordService.Value.GetUrlRecordsFor(entityType.Name, entry.Entity.Id); _toDelete.AddRange(records); }
protected override void OnDeleted(IStoreMappingSupported entity, HookedEntity entry) { var entityType = entry.Entity.GetUnproxiedType(); var records = _storeMappingService.Value .GetStoreMappingsFor(entityType.Name, entry.Entity.Id) .ToList(); _toDelete.AddRange(records); }
protected override void OnDeleting(BaseEntity entity, HookedEntity entry) { var type = entry.Entity.GetUnproxiedType(); if (!_candidateTypes.Contains(type)) { return; } if (type == typeof(SpecificationAttribute)) { var attribute = (SpecificationAttribute)entry.Entity; var attributeOptions = attribute.SpecificationAttributeOptions.ToList(); if (!attributeOptions.Any()) { attributeOptions = _specificationAttributeService.Value.GetSpecificationAttributeOptionsBySpecificationAttribute(attribute.Id).ToList(); } attributeOptions.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x.Id, "SpecificationAttributeOption"))); } else if (type == typeof(ProductVariantAttribute)) { var attribute = (ProductVariantAttribute)entry.Entity; var attributeOptions = attribute.ProductVariantAttributeValues.ToList(); if (!attributeOptions.Any()) { attributeOptions = _productAttributeService.Value.GetProductVariantAttributeValues(attribute.Id).ToList(); } attributeOptions.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x.Id, "ProductVariantAttributeValue"))); } else if (type == typeof(ProductAttribute)) { var optionIds = ( from a in _productAttributeRepository.Value.TableUntracked from os in a.ProductAttributeOptionsSets from ao in os.ProductAttributeOptions where a.Id == entry.Entity.Id select ao.Id).ToList(); optionIds.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x, "ProductAttributeOption"))); } else if (type == typeof(ProductAttributeOptionsSet)) { var set = (ProductAttributeOptionsSet)entry.Entity; var attributeOptions = set.ProductAttributeOptions.ToList(); if (!attributeOptions.Any()) { attributeOptions = _productAttributeService.Value.GetProductAttributeOptionsByOptionsSetId(set.Id).ToList(); } attributeOptions.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x.Id, "ProductAttributeOption"))); } }
protected override void OnUpdating(ISoftDeletable entity, HookedEntity entry) { var baseEntity = entry.Entity; var prop = entry.Entry.Property("Deleted"); var deletedModified = !prop.CurrentValue.Equals(prop.OriginalValue); if (!deletedModified) { return; } var entityType = baseEntity.GetUnproxiedType(); // mark orphaned ACL records as idle var aclSupported = baseEntity as IAclSupported; if (aclSupported != null && aclSupported.SubjectToAcl) { var shouldSetIdle = entity.Deleted; var rsAclRecord = _ctx.Resolve <IRepository <AclRecord> >(); var aclService = _ctx.Resolve <IAclService>(); var records = aclService.GetAclRecordsFor(entityType.Name, baseEntity.Id); foreach (var record in records) { record.IsIdle = shouldSetIdle; aclService.UpdateAclRecord(record); } } // Delete orphaned inactive UrlRecords. // We keep the active ones on purpose in order to be able to fully restore a soft deletable entity once we implemented the "recycle bin" feature var slugSupported = baseEntity as ISlugSupported; if (slugSupported != null && entity.Deleted) { var rsUrlRecord = _ctx.Resolve <IRepository <UrlRecord> >(); var urlRecordService = _ctx.Resolve <IUrlRecordService>(); var activeRecords = urlRecordService.GetUrlRecordsFor(entityType.Name, baseEntity.Id); using (urlRecordService.BeginScope()) { foreach (var record in activeRecords) { if (!record.IsActive) { urlRecordService.DeleteUrlRecord(record); } } } } }
protected override void OnInserting(IAuditable entity, HookedEntity entry) { var now = DateTime.UtcNow; if (entity.CreatedOnUtc == DateTime.MinValue) { entity.CreatedOnUtc = now; } if (entity.UpdatedOnUtc == DateTime.MinValue) { entity.UpdatedOnUtc = now; } }
public void OnBeforeSave(HookedEntity entry) { var entity = entry.Entity; if (entity is Product && entry.InitialState == EntityState.Modified) { var modProps = _dbContext.GetModifiedProperties(entity); if (modProps.Keys.Any(x => _countAffectingProductProps.Contains(x))) { Invalidate(true); } } }
private void RevertChanges(HookedEntity entry, string errorMessage) { // throw exception in OnBeforeSaveCompleted _errorMessage = errorMessage; // revert changes if (entry.Entry.State == System.Data.Entity.EntityState.Modified) { entry.Entry.State = System.Data.Entity.EntityState.Unchanged; } else if (entry.Entry.State == System.Data.Entity.EntityState.Added) { entry.Entry.State = System.Data.Entity.EntityState.Detached; } }
private bool HasEntityDuplicate <TEntity>( HookedEntity entry, BaseEntity baseEntity, Func <TEntity, string> getName, Expression <Func <TEntity, bool> > getDuplicate) where TEntity : BaseEntity { if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) { var repository = _ctx.Resolve <IRepository <TEntity> >(); var allEntities = repository.TableUntracked as IQueryable <TEntity>; var existingEntity = allEntities.FirstOrDefault(getDuplicate); if (existingEntity != null && existingEntity.Id != baseEntity.Id) { RevertChanges(entry, string.Concat(T("Common.Error.OptionAlreadyExists", getName(existingEntity).NaIfEmpty()), " ", T("Common.Error.ChooseDifferentValue"))); return(true); } } return(false); }
private bool HasAliasDuplicate <T1, T2>(HookedEntity entry, BaseEntity baseEntity) where T1 : BaseEntity where T2 : BaseEntity { if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) { var entity = baseEntity as ISearchAlias; if (entity != null) { entity.Alias = SeoExtensions.GetSeName(entity.Alias); if (entity.Alias.HasValue()) { var entities1 = _ctx.Resolve <IRepository <T1> >().TableUntracked as IQueryable <ISearchAlias>; var entities2 = _ctx.Resolve <IRepository <T2> >().TableUntracked as IQueryable <ISearchAlias>; var duplicate1 = entities1.FirstOrDefault(x => x.Alias == entity.Alias); var duplicate2 = entities2.FirstOrDefault(x => x.Alias == entity.Alias); if (duplicate1 != null || duplicate2 != null) { var type = baseEntity.GetUnproxiedType(); if (duplicate1 != null && duplicate1.Id == entity.Id && type == typeof(T1)) { return(false); } if (duplicate2 != null && duplicate2.Id == entity.Id && type == typeof(T2)) { return(false); } RevertChanges(entry, string.Concat(T("Common.Error.AliasAlreadyExists", entity.Alias), " ", T("Common.Error.ChooseDifferentValue"))); return(true); } } } } return(false); }
private bool HasAliasDuplicate <TEntity>( HookedEntity entry, BaseEntity baseEntity, Func <IQueryable <TEntity>, TEntity, bool> hasDuplicate = null) where TEntity : BaseEntity { if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) { var entity = baseEntity as ISearchAlias; if (entity != null) { entity.Alias = SeoExtensions.GetSeName(entity.Alias); if (entity.Alias.HasValue()) { var repository = _ctx.Resolve <IRepository <TEntity> >(); var allEntities = repository.TableUntracked as IQueryable <ISearchAlias>; //if (allEntities != null && allEntities.Any(x => x.Id != entity.Id && x.Alias == entity.Alias)) if (allEntities != null) { var duplicateExists = hasDuplicate == null ? allEntities.Any(x => x.Id != entity.Id && x.Alias == entity.Alias) : hasDuplicate(repository.TableUntracked, (TEntity)entity); if (duplicateExists) { RevertChanges(entry, string.Concat(T("Common.Error.AliasAlreadyExists", entity.Alias), " ", T("Common.Error.ChooseDifferentValue"))); return(true); } } } } } return(false); }
protected override void OnUpdating(IAuditable entity, HookedEntity entry) { entity.UpdatedOnUtc = DateTime.UtcNow; }
protected override void OnDeleted(ProductVariantAttributeValue entity, HookedEntity entry) { _toDelete.Add(entity); }
private void HookObject(BaseEntity baseEntity, HookedEntity entry) { var type = baseEntity.GetUnproxiedType(); if (!_candidateTypes.Contains(type)) { return; } if (type == typeof(SpecificationAttribute)) { if (HasAliasDuplicate <ProductAttribute, SpecificationAttribute>(entry, baseEntity)) { return; } if (IsPropertyModified(entry, "Alias")) { _catalogSearchQueryAliasMapper.Value.ClearAttributeCache(); } } else if (type == typeof(SpecificationAttributeOption)) { var entity = (SpecificationAttributeOption)baseEntity; if (HasEntityDuplicate <SpecificationAttributeOption>(entry, baseEntity, x => x.Name, x => x.SpecificationAttributeId == entity.SpecificationAttributeId && x.Name == entity.Name)) { return; } if (HasAliasDuplicate <SpecificationAttributeOption>(entry, baseEntity)) { return; } if (IsPropertyModified(entry, "Alias")) { _catalogSearchQueryAliasMapper.Value.ClearAttributeCache(); } } else if (type == typeof(ProductSpecificationAttribute)) { var entity = (ProductSpecificationAttribute)baseEntity; if (HasEntityDuplicate <ProductSpecificationAttribute>(entry, baseEntity, x => x.SpecificationAttributeOption?.Name, x => x.ProductId == entity.ProductId && x.SpecificationAttributeOptionId == entity.SpecificationAttributeOptionId)) { return; } } else if (type == typeof(ProductAttribute)) { if (HasAliasDuplicate <ProductAttribute, SpecificationAttribute>(entry, baseEntity)) { return; } if (IsPropertyModified(entry, "Alias")) { _catalogSearchQueryAliasMapper.Value.ClearVariantCache(); } } else if (type == typeof(ProductAttributeOption)) { var entity = (ProductAttributeOption)baseEntity; if (HasEntityDuplicate <ProductAttributeOption>(entry, baseEntity, x => x.Name, x => x.ProductAttributeOptionsSetId == entity.ProductAttributeOptionsSetId && x.Name == entity.Name)) { return; } // ClearVariantCache() not necessary if (HasAliasDuplicate <ProductAttributeOption>(entry, baseEntity)) { return; } } else if (type == typeof(ProductVariantAttribute)) { var entity = (ProductVariantAttribute)baseEntity; if (HasEntityDuplicate <ProductVariantAttribute>(entry, baseEntity, x => x.ProductAttribute?.Name, x => x.ProductId == entity.ProductId && x.ProductAttributeId == entity.ProductAttributeId)) { return; } } else if (type == typeof(ProductVariantAttributeValue)) { var entity = (ProductVariantAttributeValue)baseEntity; if (HasEntityDuplicate <ProductVariantAttributeValue>(entry, baseEntity, x => x.Name, x => x.ProductVariantAttributeId == entity.ProductVariantAttributeId && x.Name == entity.Name)) { return; } if (HasAliasDuplicate <ProductVariantAttributeValue>(entry, baseEntity, (all, e) => all.Any(x => x.Id != e.Id && x.ProductVariantAttributeId == e.ProductVariantAttributeId && x.Alias == e.Alias))) { return; } if (IsPropertyModified(entry, "Alias")) { _catalogSearchQueryAliasMapper.Value.ClearVariantCache(); } } else if (type == typeof(LocalizedProperty)) { // note: not fired when SpecificationAttribute or SpecificationAttributeOption deleted. // not necessary anyway because cache cleared by above code. var prop = (LocalizedProperty)baseEntity; var keyGroup = prop.LocaleKeyGroup; if (!prop.LocaleKey.IsCaseInsensitiveEqual("Alias")) { return; } // validating ProductVariantAttributeValue goes too far here. if (!keyGroup.IsCaseInsensitiveEqual("SpecificationAttribute") && !keyGroup.IsCaseInsensitiveEqual("SpecificationAttributeOption") && !keyGroup.IsCaseInsensitiveEqual("ProductAttribute") && !keyGroup.IsCaseInsensitiveEqual("ProductAttributeOption")) { return; } // check alias duplicate if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) { prop.LocaleValue = SeoExtensions.GetSeName(prop.LocaleValue); if (prop.LocaleValue.HasValue() && HasAliasDuplicate(prop)) { RevertChanges(entry, string.Concat(T("Common.Error.AliasAlreadyExists", prop.LocaleValue), " ", T("Common.Error.ChooseDifferentValue"))); return; } } if (IsPropertyModified(entry, "LocaleValue")) { if (keyGroup.IsCaseInsensitiveEqual("SpecificationAttribute") || keyGroup.IsCaseInsensitiveEqual("SpecificationAttributeOption")) { _catalogSearchQueryAliasMapper.Value.ClearAttributeCache(); } else if (keyGroup.IsCaseInsensitiveEqual("ProductAttribute") || keyGroup.IsCaseInsensitiveEqual("ProductVariantAttributeValue")) { // not necessary for ProductAttributeOption _catalogSearchQueryAliasMapper.Value.ClearVariantCache(); } } } }
protected override void OnUpdating(BaseEntity entity, HookedEntity entry) { HookObject(entity, entry); }
public void OnAfterSave(HookedEntity entry) { if (_invalidated) { // Don't bother processing. return; } // INFO: Acl & StoreMapping affect element counts var entity = entry.Entity; if (entity is Product) { if (entry.InitialState == EntityState.Added) { Invalidate(true); } } else if (entity is Category) { Invalidate(); } else if (entity is Language || entity is CustomerRole) { InvalidateWhen(entry.InitialState == EntityState.Modified || entry.InitialState == EntityState.Deleted); } else if (entity is Setting) { var name = (entity as Setting).Name.ToLowerInvariant(); InvalidateWhen(name == "catalogsettings.showcategoryproductnumber" || name == "catalogsettings.showcategoryproductnumberincludingsubcategories"); } else if (entity is ProductCategory) { Invalidate(true); } else if (entity is AclRecord) { var acl = entity as AclRecord; if (!acl.IsIdle) { if (acl.EntityName == "Product") { Invalidate(true); } else if (acl.EntityName == "Category") { Invalidate(false); } } } else if (entity is StoreMapping) { var stm = entity as StoreMapping; if (stm.EntityName == "Product") { Invalidate(true); } else if (stm.EntityName == "Category") { Invalidate(false); } } else if (entity is LocalizedProperty) { var lp = entity as LocalizedProperty; var key = lp.LocaleKey; if (lp.LocaleKeyGroup == "Category" && (key == "Name" || key == "FullName" || key == "Description" || key == "BadgeText")) { Invalidate(); } } }