/// <summary> /// Extended constructor when also storing the persistance ID-Info /// </summary> public AttributeDefinition(int appId, string name, string type, bool isTitle, int attributeId, int sortOrder, IDeferredEntitiesList metaSource = null) : base(name, type) { AppId = appId; IsTitle = isTitle; AttributeId = attributeId; SortOrder = sortOrder; metadataSource = metaSource; }
/// <summary>Get Data to populate ICache</summary> /// <param name="entityIds">null or a List of EntitiIds</param> /// <param name="appId">AppId (can be different than the appId on current context (e.g. if something is needed from the default appId, like MetaData)</param> /// <param name="source">DataSource to get child entities</param> /// <param name="entitiesOnly">If only the CachItem.Entities is needed, this can be set to true to imporove performance</param> /// <returns>Item1: EntityModels, Item2: all ContentTypes, Item3: Assignment Object Types</returns> internal AppDataPackage GetAppDataPackage(int[] entityIds, int appId, IDeferredEntitiesList source, bool entitiesOnly = false) { var contentTypes = GetEavContentTypes(appId); var metadataForGuid = new Dictionary<int, Dictionary<Guid, IEnumerable<IEntity>>>(); var metadataForNumber = new Dictionary<int, Dictionary<int, IEnumerable<IEntity>>>(); var metadataForString = new Dictionary<int, Dictionary<string, IEnumerable<IEntity>>>(); var relationships = new List<EntityRelationshipItem>(); #region Prepare & Extend EntityIds if (entityIds == null) entityIds = new int[0]; var filterByEntityIds = entityIds.Any(); // Ensure published Versions of Drafts are also loaded (if filtered by EntityId, otherwise all Entities from the app are loaded anyway) if (filterByEntityIds) entityIds = entityIds.Union(from e in Context.SqlDb.Entities where e.PublishedEntityId.HasValue && !e.IsPublished && entityIds.Contains(e.EntityID) && !entityIds.Contains(e.PublishedEntityId.Value) && e.ChangeLogDeleted == null select e.PublishedEntityId.Value).ToArray(); #endregion #region Get Entities with Attribute-Values from Database var entitiesWithAandVfromDb = from e in Context.SqlDb.Entities where !e.ChangeLogIDDeleted.HasValue && e.Set.AppID == appId && e.Set.ChangeLogIDDeleted == null && ( // filter by EntityIds (if set) !filterByEntityIds || entityIds.Contains(e.EntityID) || (e.PublishedEntityId.HasValue && entityIds.Contains(e.PublishedEntityId.Value)) // also load Drafts ) orderby e.EntityID // guarantees Published appear before draft select new { e.EntityID, e.EntityGUID, e.AttributeSetID, Metadata = new Metadata { TargetType = e.AssignmentObjectTypeID, KeyGuid = e.KeyGuid, KeyNumber = e.KeyNumber, KeyString = e.KeyString }, e.IsPublished, e.PublishedEntityId, e.Owner, // new 2016-03-01 Modified = e.ChangeLogModified.Timestamp, RelatedEntities = from r in e.EntityParentRelationships group r by r.AttributeID into rg select new { AttributeID = rg.Key, Childs = rg.OrderBy(c => c.SortOrder).Select(c => c.ChildEntityID) }, Attributes = from v in e.Values where !v.ChangeLogIDDeleted.HasValue group v by v.AttributeID into vg select new { AttributeID = vg.Key, Values = from v2 in vg orderby v2.ChangeLogIDCreated select new { v2.ValueID, v2.Value, Languages = from l in v2.ValuesDimensions select new Data.Dimension { DimensionId = l.DimensionID, ReadOnly = l.ReadOnly, Key = l.Dimension.ExternalKey.ToLower() }, v2.ChangeLogIDCreated } } }; #endregion #region Build EntityModels var entities = new Dictionary<int, IEntity>(); var entList = new List<IEntity>(); foreach (var e in entitiesWithAandVfromDb) { var contentType = (ContentType)contentTypes[e.AttributeSetID]; var newEntity = new Data.Entity(e.EntityGUID, e.EntityID, e.EntityID, e.Metadata /* e.AssignmentObjectTypeID */, contentType, e.IsPublished, relationships, e.Modified, e.Owner); var allAttribsOfThisType = new Dictionary<int, IAttributeManagement>(); // temporary Dictionary to set values later more performant by Dictionary-Key (AttributeId) IAttributeManagement titleAttrib = null; // Add all Attributes from that Content-Type foreach (var definition in contentType.AttributeDefinitions.Values) { var newAttribute = AttributeHelperTools.GetAttributeManagementModel(definition); newEntity.Attributes.Add(((IAttributeBase)newAttribute).Name, newAttribute); allAttribsOfThisType.Add(definition.AttributeId, newAttribute); if (newAttribute.IsTitle) titleAttrib = newAttribute; } // If entity is a draft, add references to Published Entity if (!e.IsPublished && e.PublishedEntityId.HasValue) { // Published Entity is already in the Entities-List as EntityIds is validated/extended before and Draft-EntityID is always higher as Published EntityId newEntity.PublishedEntity = entities[e.PublishedEntityId.Value]; ((Data.Entity)newEntity.PublishedEntity).DraftEntity = newEntity; newEntity.EntityId = e.PublishedEntityId.Value; } #region Add metadata-lists based on AssignmentObjectTypes // unclear why #1 is handled in a special way - why should this not be cached? I believe 1 means no specific assignment if (e.Metadata.HasMetadata && !entitiesOnly) { // Try guid first. Note that an item can be assigned to both a guid, string and an int if necessary, though not commonly used if (e.Metadata.KeyGuid.HasValue) { // Ensure that this assignment-Type (like 4 = entity-assignment) already has a dictionary for storage if (!metadataForGuid.ContainsKey(e.Metadata.TargetType)) // ensure AssignmentObjectTypeID metadataForGuid.Add(e.Metadata.TargetType, new Dictionary<Guid, IEnumerable<IEntity>>()); // Ensure that the assignment type (like 4) the target guid (like a350320-3502-afg0-...) has an empty list of items if (!metadataForGuid[e.Metadata.TargetType].ContainsKey(e.Metadata.KeyGuid.Value)) // ensure Guid metadataForGuid[e.Metadata.TargetType][e.Metadata.KeyGuid.Value] = new List<IEntity>(); // Now all containers must exist, add this item ((List<IEntity>)metadataForGuid[e.Metadata.TargetType][e.Metadata.KeyGuid.Value]).Add(newEntity); } if (e.Metadata.KeyNumber.HasValue) { if (!metadataForNumber.ContainsKey(e.Metadata.TargetType)) // ensure AssignmentObjectTypeID metadataForNumber.Add(e.Metadata.TargetType, new Dictionary<int, IEnumerable<IEntity>>()); if (!metadataForNumber[e.Metadata.TargetType].ContainsKey(e.Metadata.KeyNumber.Value)) // ensure Guid metadataForNumber[e.Metadata.TargetType][e.Metadata.KeyNumber.Value] = new List<IEntity>(); ((List<IEntity>)metadataForNumber[e.Metadata.TargetType][e.Metadata.KeyNumber.Value]).Add(newEntity); } if (!string.IsNullOrEmpty(e.Metadata.KeyString)) { if (!metadataForString.ContainsKey(e.Metadata.TargetType)) // ensure AssignmentObjectTypeID metadataForString.Add(e.Metadata.TargetType, new Dictionary<string, IEnumerable<IEntity>>()); if (!metadataForString[e.Metadata.TargetType].ContainsKey(e.Metadata.KeyString)) // ensure Guid metadataForString[e.Metadata.TargetType][e.Metadata.KeyString] = new List<IEntity>(); ((List<IEntity>)metadataForString[e.Metadata.TargetType][e.Metadata.KeyString]).Add(newEntity); } } #endregion #region add Related-Entities Attributes foreach (var r in e.RelatedEntities) { var attributeModel = allAttribsOfThisType[r.AttributeID]; var valueModel = Value.GetValueModel(((IAttributeBase)attributeModel).Type, r.Childs, source); var valuesModelList = new List<IValue> { valueModel }; attributeModel.Values = valuesModelList; attributeModel.DefaultValue = (IValueManagement)valuesModelList.FirstOrDefault(); } #endregion #region Add "normal" Attributes (that are not Entity-Relations) foreach (var a in e.Attributes) { IAttributeManagement attributeModel; try { attributeModel = allAttribsOfThisType[a.AttributeID]; } catch (KeyNotFoundException) { continue; } if (attributeModel.IsTitle) newEntity.Title = attributeModel; var valuesModelList = new List<IValue>(); #region Add all Values foreach (var v in a.Values) { var valueModel = Value.GetValueModel(((IAttributeBase)attributeModel).Type, v.Value, v.Languages, v.ValueID, v.ChangeLogIDCreated); valuesModelList.Add(valueModel); } #endregion attributeModel.Values = valuesModelList; attributeModel.DefaultValue = (IValueManagement)valuesModelList.FirstOrDefault(); } // Special treatment in case there is no title - sometimes happens if the title-field is re-defined and ol data might no have this //if(newEntity.Title == null) // newEntity.Title = titleAttrib; #endregion entities.Add(e.EntityID, newEntity); entList.Add(newEntity); } #endregion #region Populate Entity-Relationships (after all EntityModels are created) var relationshipsRaw = from r in Context.SqlDb.EntityRelationships where r.Attribute.AttributesInSets.Any(s => s.Set.AppID == appId && (!filterByEntityIds || (!r.ChildEntityID.HasValue || entityIds.Contains(r.ChildEntityID.Value)) || entityIds.Contains(r.ParentEntityID))) orderby r.ParentEntityID, r.AttributeID, r.ChildEntityID select new { r.ParentEntityID, r.Attribute.StaticName, r.ChildEntityID }; foreach (var relationship in relationshipsRaw) { try { if(entities.ContainsKey(relationship.ParentEntityID) && (!relationship.ChildEntityID.HasValue || entities.ContainsKey(relationship.ChildEntityID.Value))) relationships.Add(new EntityRelationshipItem(entities[relationship.ParentEntityID], relationship.ChildEntityID.HasValue ? entities[relationship.ChildEntityID.Value] : null)); } catch (KeyNotFoundException) { } // may occour if not all entities are loaded - edited 2rm 2015-09-29: Should not occur anymore } #endregion return new AppDataPackage(entities, entList, contentTypes, metadataForGuid, metadataForNumber, metadataForString, relationships); }
/// <summary> /// Initializes a new instance of the EntityRelationship class. /// </summary> /// <param name="fullEntitiesListForLookup">DataSource to retrieve child entities</param> /// <param name="entityIds">List of IDs to initialize with</param> public EntityRelationship(IDeferredEntitiesList fullEntitiesListForLookup, IEnumerable <int?> entityIds = null) { EntityIds = entityIds ?? EntityIdsEmpty; _fullEntityList = fullEntitiesListForLookup; }
public AppDataPackage GetDataForCache(IDeferredEntitiesList targetCacheForDeferredLookups) { return new DbLoadIntoEavDataStructure(Context).GetAppDataPackage(null, AppId, targetCacheForDeferredLookups); }
/// <summary> /// Creates a Typed Value Model /// </summary> public static IValue Build(string attributeType, object value, List <ILanguage> languages, IDeferredEntitiesList fullEntityListForLookup = null) { if (languages == null) { languages = new List <ILanguage>(); } Value typedModel; var stringValue = value as string; try { var type = (AttributeTypeEnum)Enum.Parse(typeof(AttributeTypeEnum), attributeType); switch (type) { case AttributeTypeEnum.Boolean: bool typedBoolean; typedModel = new Value <bool?>(value as bool? ?? (bool.TryParse(stringValue, out typedBoolean) ? typedBoolean : new bool?())); break; case AttributeTypeEnum.DateTime: DateTime typedDateTime; typedModel = new Value <DateTime?>(value as DateTime? ?? (DateTime.TryParse(stringValue, CultureInfo.InvariantCulture, DateTimeStyles.None, out typedDateTime) ? typedDateTime : new DateTime?())); break; case AttributeTypeEnum.Number: decimal?newDec = null; if (!(value is string && string.IsNullOrEmpty(value as string))) // only try converting if it's not an empty string { try { newDec = Convert.ToDecimal(value, CultureInfo.InvariantCulture); } catch { // ignored } // if it's still blank, try another method // note: 2dm added this 2017-08-29 after a few bugs, but it may not be necessary any more, as the bug was something else if (newDec == null) { try { if (decimal.TryParse(stringValue, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal typedDecimal)) { newDec = typedDecimal; } } catch { // ignored } } } typedModel = new Value <decimal?>(newDec); break; case AttributeTypeEnum.Entity: var entityIds = value as IEnumerable <int?> ?? (value as IEnumerable <int>)?.Select(x => (int?)x).ToList(); if (entityIds != null) { typedModel = new Value <EntityRelationship>(new EntityRelationship(fullEntityListForLookup, entityIds)); } else if (value is EntityRelationship) { typedModel = new Value <EntityRelationship>(new EntityRelationship(fullEntityListForLookup, ((EntityRelationship)value).EntityIds)); } else { var entityIdEnum = value as IEnumerable; // note: strings are also enum! if (value is string && !String.IsNullOrEmpty(stringValue)) { entityIdEnum = stringValue.Split(',').ToList(); } // this is the case when we get a CSV-string with GUIDs var entityGuids = entityIdEnum?.Cast <object>().Select(x => { var v = x?.ToString().Trim(); // this is the case when an export contains a list with nulls as a special code if (v == null || v == Constants.EmptyRelationship) { return(new Guid?()); } var guid = Guid.Parse(v); return(guid == Guid.Empty ? new Guid?() : guid); }).ToList() ?? new List <Guid?>(0); typedModel = new Value <List <Guid?> >(entityGuids); } break; // ReSharper disable RedundantCaseLabel case AttributeTypeEnum.String: // most common case case AttributeTypeEnum.Empty: // empty - should actually not contain anything! case AttributeTypeEnum.Custom: // custom value, currently just parsed as string for manual processing as needed case AttributeTypeEnum.Hyperlink: // special case, handled as string case AttributeTypeEnum.Undefined: // backup case, where it's not known... // ReSharper restore RedundantCaseLabel default: typedModel = new Value <string>(stringValue); break; } } catch { typedModel = new Value <string>(stringValue); } typedModel.Languages = languages; return((IValue)typedModel); }