/// <summary> /// Gets the select and expand dictionary object. /// </summary> /// <param name="type">The type.</param> /// <param name="selectAndExpandList">The select and expand list.</param> /// <param name="rockContext">The rock context.</param> /// <param name="loadAttributesOptions">The load attributes options.</param> /// <returns></returns> private static List <Dictionary <string, object> > GetSelectAndExpandDictionaryObject(Type type, List <object> selectAndExpandList, Rock.Data.RockContext rockContext, LoadAttributesOptions loadAttributesOptions) { // The normal SelectAllAndExpand converts stuff into dictionaries, but some stuff ends up missing // So, this will do all that manually using reflection and our own dictionary of each Entity var valueAsDictionary = new List <Dictionary <string, object> >(); var isPersonModel = type.IsGenericType && type.GenericTypeArguments[0] == typeof(Rock.Model.Person); Dictionary <int, Rock.Model.PersonAlias> personAliasLookup = null; if (isPersonModel) { var personIds = selectAndExpandList.Select(a => (a.GetPropertyValue("Instance") as Rock.Model.Person).Id).ToList(); // NOTE: If this is a really long list of PersonIds (20000+ or so), this might time out or get an error, // so if it is more than 20000 just get *all* the PersonAlias records which would probably be much faster than a giant where clause var personAliasQry = new Rock.Model.PersonAliasService(rockContext).Queryable().AsNoTracking(); if (personIds.Count < 20000) { personAliasLookup = personAliasQry.Where(a => personIds.Contains(a.PersonId) && a.AliasPersonId == a.PersonId).ToDictionary(k => k.PersonId, v => v); } else { personAliasLookup = personAliasQry.Where(a => a.AliasPersonId == a.PersonId).ToDictionary(k => k.PersonId, v => v); } } foreach (var selectExpandItem in selectAndExpandList) { var entityProperty = selectExpandItem.GetType().GetProperty("Instance"); var entity = entityProperty.GetValue(selectExpandItem) as Rock.Data.IEntity; if (entity is Rock.Model.Person) { // if this is a SelectAndExpand of Person, we manually need to load Aliases so that the PrimaryAliasId is populated var person = entity as Rock.Model.Person; if (!person.Aliases.Any()) { var primaryAlias = personAliasLookup.GetValueOrNull(person.Id); if (primaryAlias != null) { person.Aliases.Add(personAliasLookup[person.Id]); } } } Dictionary <string, object> valueDictionary = new Dictionary <string, object>(); // add the "Expanded" stuff first to emulate the default behavior var expandedStuff = selectExpandItem.GetPropertyValue("Container"); while (expandedStuff != null) { var expandedName = expandedStuff.GetPropertyValue("Name") as string; var expandedValue = expandedStuff.GetPropertyValue("Value"); valueDictionary.Add(expandedName, expandedValue); expandedStuff = expandedStuff.GetPropertyValue("Next"); } // add each of the Entity's properties foreach (var entityKeyValue in entity.ToDictionary()) { valueDictionary.Add(entityKeyValue.Key, entityKeyValue.Value); } // if LoadAttributes was specified, add those last if (loadAttributesOptions.LoadAttributesEnabled && (entity is Attribute.IHasAttributes)) { // Add Attributes and AttributeValues valueDictionary.Add("Attributes", (entity as Attribute.IHasAttributes).Attributes); valueDictionary.Add("AttributeValues", (entity as Attribute.IHasAttributes).AttributeValues); } valueAsDictionary.Add(valueDictionary); } return(valueAsDictionary); }
/// <summary> /// Called during serialization to write an object of the specified type to the specified stream. /// </summary> /// <param name="type">The type of the object to write.</param> /// <param name="value">The object to write.</param> /// <param name="writeStream">The stream to write to.</param> /// <param name="effectiveEncoding">The encoding to use when writing.</param> public override void WriteToStream(Type type, object value, System.IO.Stream writeStream, Encoding effectiveEncoding) { IEnumerable <Attribute.IHasAttributes> items = null; // query should be filtered by now, so iterate thru items and load attributes before the response is serialized if (LoadAttributes) { if (value is IEnumerable <Attribute.IHasAttributes> ) { // if the REST call specified that Attributes should be loaded and we are returning a list of IHasAttributes.. items = value as IEnumerable <Attribute.IHasAttributes>; } else if (value is Attribute.IHasAttributes) { // if the REST call specified that Attributes should be loaded and we are returning a single IHasAttributes.. items = new List <Attribute.IHasAttributes>(new Attribute.IHasAttributes[] { value as Attribute.IHasAttributes }); } else if (value is IQueryable) { Type valueType = value.GetType(); if (valueType.IsGenericType && valueType.GenericTypeArguments[0].Name == "SelectAllAndExpand`1") { // 'SelectAndExpand' buries the Entity in a private field called 'Instance', // so use reflection to get that and load the attributes for each var selectExpandQry = value as IQueryable; var itemsList = new List <Attribute.IHasAttributes>(); foreach (var selectExpandItem in selectExpandQry) { var entityProperty = selectExpandItem.GetType().GetProperty("Instance"); var entity = entityProperty.GetValue(selectExpandItem) as Attribute.IHasAttributes; if (entity != null) { itemsList.Add(entity); } entityProperty.SetValue(selectExpandItem, entity); } items = itemsList; } } if (items != null) { var rockContext = new Rock.Data.RockContext(); foreach (var item in items) { Rock.Attribute.Helper.LoadAttributes(item, rockContext); } FilterAttributes(rockContext, items, this.Person); } } // Special Code if an $expand clause is specified and a $select clause is NOT specified // This fixes a couple of issues: // 1) our special loadAttributes stuff didn't work if $expand is specified // 2) Only non-virtual,non-inherited fields were included (for example: Person.PrimaryAliasId, etc, wasn't getting included) if $expand was specified if (value is IQueryable && typeof(IQueryable <Rock.Data.IEntity>).IsAssignableFrom(type)) { Type valueType = value.GetType(); // if this is an OData 'SelectAndExpand', we have convert stuff to Dictionary manually to get the stuff to serialize the way we want if (valueType.IsGenericType && valueType.GenericTypeArguments[0].Name == "SelectAllAndExpand`1") { var rockContext = new Rock.Data.RockContext(); // The normal SelectAllAndExpand converts stuff into dictionaries, but some stuff ends up missing // So, this will do all that manually using reflection and our own dictionary of each Entity var valueAsDictionary = new List <Dictionary <string, object> >(); var selectExpandQry = value as IQueryable; var selectExpandList = (selectExpandQry as IQueryable <object>).ToList(); var isPersonModel = type.IsGenericType && type.GenericTypeArguments[0] == typeof(Rock.Model.Person); Dictionary <int, Rock.Model.PersonAlias> personAliasLookup = null; if (isPersonModel) { var personIds = selectExpandList.Select(a => (a.GetPropertyValue("Instance") as Rock.Model.Person).Id).ToList(); // NOTE: If this is a really long list of PersonIds (20000+ or so), this might time out or get an error, // so if it is more than 20000 just get *all* the personalias records which would probably be much faster than a giant where clause var personAliasQry = new Rock.Model.PersonAliasService(rockContext).Queryable().AsNoTracking(); if (personIds.Count < 20000) { personAliasLookup = personAliasQry.Where(a => personIds.Contains(a.PersonId) && a.AliasPersonId == a.PersonId).ToDictionary(k => k.PersonId, v => v); } else { personAliasLookup = personAliasQry.Where(a => a.AliasPersonId == a.PersonId).ToDictionary(k => k.PersonId, v => v); } } foreach (var selectExpandItem in selectExpandList) { var entityProperty = selectExpandItem.GetType().GetProperty("Instance"); var entity = entityProperty.GetValue(selectExpandItem) as Rock.Data.IEntity; if (entity is Rock.Model.Person) { // if this is a SelectAndExpand of Person, we manually need to load Aliases so that the PrimaryAliasId is populated var person = entity as Rock.Model.Person; if (!person.Aliases.Any()) { var primaryAlias = personAliasLookup.GetValueOrNull(person.Id); if (primaryAlias != null) { person.Aliases.Add(personAliasLookup[person.Id]); } } } Dictionary <string, object> valueDictionary = new Dictionary <string, object>(); // add the "Expanded" stuff first to emulate the default behavior var expandedStuff = selectExpandItem.GetPropertyValue("Container"); while (expandedStuff != null) { var expandedName = expandedStuff.GetPropertyValue("Name") as string; var expandedValue = expandedStuff.GetPropertyValue("Value"); valueDictionary.Add(expandedName, expandedValue); expandedStuff = expandedStuff.GetPropertyValue("Next"); } // add each of the Entity's properties foreach (var entityKeyValue in entity.ToDictionary()) { valueDictionary.Add(entityKeyValue.Key, entityKeyValue.Value); } // if LoadAttributes was specified, add those last if (LoadAttributes && (entity is Attribute.IHasAttributes)) { // Add Attributes and AttributeValues valueDictionary.Add("Attributes", (entity as Attribute.IHasAttributes).Attributes); valueDictionary.Add("AttributeValues", (entity as Attribute.IHasAttributes).AttributeValues); } valueAsDictionary.Add(valueDictionary); } base.WriteToStream(type, valueAsDictionary, writeStream, effectiveEncoding); return; } } base.WriteToStream(type, value, writeStream, effectiveEncoding); }