Example #1
0
        /// <summary>
        /// Decollates changes to content object which should be redirected to other records used as property sources
        /// </summary>
        /// <param name="path">path of content record</param>
        /// <param name="data">content object</param>
        /// <returns>JObject build from content object</returns>
        protected virtual JObject SetRelated(string path, object data)
        {
            // Establish the records to fetch and fetch them

            Type contentType   = data.GetType();
            var  rpsAttributes = contentType
                                 .GetCustomAttributes(typeof(RedirectPropertySourceAttribute), false)
                                 .Cast <RedirectPropertySourceAttribute>()
                                 .Where(rpsa => !rpsa.ReadOnly)
                                 .ToList();
            List <string> paths = rpsAttributes
                                  .Select(a => PathFunctions.Redirect(path, a.SourceDescriptor))
                                  .Distinct()
                                  .ToList();
            List <PropertyStore> records = Repository.Instance.Get(contentType, paths).ToList();

            // Update the fetched referenced records with updated referenced properties on the content object

            JObject jObjectContent = JObject.FromObject(data);

            List <object>        ids  = new List <object>();
            List <PropertyStore> vals = new List <PropertyStore>();

            if (rpsAttributes.Any())
            {
                foreach (var rpsAttribute in rpsAttributes)
                {
                    string refdPath = PathFunctions.Redirect(path, rpsAttribute.SourceDescriptor);
                    if (refdPath == path) // redirected to itself
                    {
                        continue;
                    }
                    PropertyStore refdRecord = records.FirstOrDefault(ps => (string)ps["Path"] == refdPath);
                    if (refdRecord == null)
                    {
                        refdRecord = GetNewRecord(contentType, refdPath);
                    }
                    foreach (string propertyPath in rpsAttribute.PropertyPaths)
                    {
                        JObject refdObject  = JObject.Parse((string)refdRecord["Content"]);
                        var     toFromPaths = GetPaths(propertyPath);
                        refdObject.CopyPropertyFrom(toFromPaths[1], jObjectContent, toFromPaths[0]);
                        refdRecord["Content"] = refdObject.ToString();
                    }
                    if (!ids.Contains(refdPath))
                    {
                        ids.Add(refdPath);
                        vals.Add(refdRecord);
                    }
                }

                if (vals.Count > 0)
                {
                    Repository.Instance.Set(contentType, vals, ids);
                }
            }

            return(jObjectContent);
        }
Example #2
0
        /// <summary>
        /// Efficiently gets from database and collates all data referenced by property source redirects
        /// </summary>
        /// <param name="primaryPath">path of main item</param>
        /// <param name="primaryRecord">data record of main item, if already available, or else null</param>
        /// <param name="contentType">type of main content object</param>
        /// <returns>content object</returns>
        protected virtual object GetWithRelated(string primaryPath, PropertyStore primaryRecord, Type contentType)
        {
            // Establish the records to fetch and fetch them

            var rpsAttributes = contentType
                                .GetCustomAttributes(typeof(RedirectPropertySourceAttribute), false)
                                .Cast <RedirectPropertySourceAttribute>()
                                .ToList();
            List <string> paths = new List <string>();

            if (primaryRecord == null)
            {
                paths.Add(primaryPath);
            }
            paths.AddRange(rpsAttributes
                           .Select(a => PathFunctions.Redirect(primaryPath, a.SourceDescriptor))
                           .Distinct());
            List <PropertyStore> records = Repository.Instance.Get(contentType, paths).ToList();

            // Update the primary record with redirected properties from referenced records

            if (primaryRecord == null)
            {
                primaryRecord = records.FirstOrDefault(ps => ((string)ps["Path"]) == primaryPath);
            }
            if (primaryRecord == null)
            {
                return(null);
            }
            JObject jContent = null;

            if (rpsAttributes.Any())
            {
                jContent = JObject.Parse((string)primaryRecord["Content"]);
                foreach (var rpsAttribute in rpsAttributes)
                {
                    string refdPath = PathFunctions.Redirect(primaryPath, rpsAttribute.SourceDescriptor);
                    if (refdPath == primaryPath) // redirected to itself
                    {
                        continue;
                    }
                    PropertyStore refdRecord = records.FirstOrDefault(ps => (string)ps["Path"] == refdPath);
                    if (refdRecord != null)
                    {
                        foreach (string propertyPath in rpsAttribute.PropertyPaths)
                        {
                            var     toFromPaths = GetPaths(propertyPath);
                            JObject refdObject  = JObject.Parse((string)refdRecord["Content"]);
                            jContent.CopyPropertyFrom(toFromPaths[0], refdObject, toFromPaths[1]);
                        }
                    }
                }
            }

            bool isBaseContent = typeof(BaseContent).IsAssignableFrom(contentType);

            if (jContent == null)
            {
                return(GetContent(isBaseContent, contentType, primaryRecord));
            }

            object content = jContent.ToObject(contentType);

            //if (isBaseContent)
            //    (content as BaseContent).OriginalRecord = primaryRecord;
            return(content);
        }
Example #3
0
        /// <summary>
        /// Starting from a list of addresses and optionally (or only) the containers at those addresses, fetch
        /// any containers necessary and any other containers required to supply redirected properties for them,
        /// obtain the contained content items and collate their properties, returning the content items at the
        /// addresses.
        /// </summary>
        /// <typeparam name="T">Type of content items to return</typeparam>
        /// <param name="startContainers">Initial list of containers if they are available</param>
        /// <param name="startAddresses">Initial list of addresses, which may be omitted and derived from containers</param>
        /// <returns>List of content items</returns>
        public IEnumerable <T> Collate <T>(IEnumerable <object> startContainers, IEnumerable <Address> startAddresses) where T : class
        {
            // place to store all the containers we have currently
            Dictionary <VersionedAddress, object> containers;
            ItemVersion containerCommonVersion;

            startAddresses = startAddresses ?? Enumerable.Empty <Address>();

            (containers, containerCommonVersion) = ProcessContainers(startContainers);

            List <Address> fetchAddrs = startAddresses
                                        .Where(sa => !containers.Any(kvp => kvp.Key.Address == sa)).ToList();

            List <IGrouping <Type, Address> > allStartAddressesByType = null;

            if (DoCollation)
            {
                allStartAddressesByType = fetchAddrs.Concat(containers.Keys)
                                          .GroupBy(a => a.Type)
                                          .ToList();

                // Get all addresses for items to collate (startAddresses plus addresses from startContainers)
                foreach (var addrTypeG in allStartAddressesByType)
                {
                    Type contentType   = addrTypeG.Key;
                    var  rpsAttributes = contentType
                                         .GetCustomAttributes(typeof(RedirectPropertySourceAttribute), true)
                                         .Cast <RedirectPropertySourceAttribute>()
                                         .ToList();
                    foreach (Address addr in addrTypeG)
                    {
                        fetchAddrs.AddRange(rpsAttributes
                                            .Select(attr => new Address(attr.ContentType ?? contentType,
                                                                        PathFunctions.Redirect(addr.GetAsContentPath(), attr.SourceDescriptor))));
                    }
                }
                fetchAddrs = fetchAddrs.Distinct().ToList();
            }

            bool pushVersion = (startContainers != null && DoCollation);

            if (pushVersion) // Get containers in any version that might be relevant to a start container
            {
                System.Versions.PushState(VersioningMode.Specific, containerCommonVersion);
            }

            try
            {
                // Get all the containers for collation (if current version is not fully specified, may be multiple per address)
                foreach (var cont in System.Repository.Get(typeof(object), fetchAddrs))
                {
                    var va = new VersionedAddress(System, cont);
                    if (containers.ContainsKey(va))
                    {
                        log.Error("Duplicate versioned address in db: " + va.ToString());
                    }
                    else
                    {
                        containers.Add(new VersionedAddress(new Address(cont), new ItemVersion(System, cont).Canonicalise()), cont);
                    }
                }
            }
            finally
            {
                if (pushVersion)
                {
                    System.Versions.PopState();
                }
            }

            if (!DoCollation)
            {
                // just return the containers we have
                foreach (object cont in containers.Values)
                {
                    yield return(cont as T);
                }
                yield break;
            }

            // Create a lookup by (non-versioned) address of all the containers we have
            var contLookup = containers.ToLookup(kvp => kvp.Key.Address.ToString(), kvp => kvp.Value);

            // We have the data, now collate it into the content from the startContainers
            foreach (var addrTypeG in allStartAddressesByType)
            {
                // Process all the start addresses (including those of the start containers) of a given type

                Type contentType   = addrTypeG.Key;
                var  rpsAttributes = contentType
                                     .GetCustomAttributes(typeof(RedirectPropertySourceAttribute), true)
                                     .Cast <RedirectPropertySourceAttribute>()
                                     .ToList();

                foreach (var addr in addrTypeG.Select(a => new Address(a.Type, a)).Distinct()) // convert a VersionedAddress to an Address if necessary
                {
                    var primaryPath = addr.GetAsContentPath();
                    if (!contLookup.Contains(new Address(addr.Type, addr).ToString()))
                    {
                        continue;
                    }

                    foreach (var cont in contLookup[addr.ToString()])
                    {
                        object primaryContent = cont;

                        if (primaryContent is IContentContainer)
                        {
                            primaryContent = ((IContentContainer)primaryContent).GetContent(System.Extender);
                        }

                        foreach (var rpsAttribute in rpsAttributes)
                        {
                            var refAddress = new VersionedAddress(
                                rpsAttribute.ContentType ?? contentType,
                                PathFunctions.Redirect(primaryPath, rpsAttribute.SourceDescriptor),
                                new ItemVersion(System, cont).Canonicalise()
                                );
                            if (refAddress.Address == addr) // redirected to itself, ignore
                            {
                                continue;
                            }
                            object refItem = containers.ContainsKey(refAddress) ? containers[refAddress] : null;
                            if (refItem is IContentContainer)
                            {
                                refItem = ((IContentContainer)refItem).GetContent(System.Extender);
                            }
                            if (refItem != null)
                            {
                                foreach (string propertyPath in rpsAttribute.PropertyPaths)
                                {
                                    var    toFromPaths = GetPaths(propertyPath);
                                    object val         = ReflectionX.GetPropertyValueByPath(refItem, toFromPaths[1]);
                                    var    piSet       = ReflectionX.GetPropertyByPath(primaryContent.GetType(), toFromPaths[0]);
                                    piSet.SetValue(primaryContent, val);
                                }
                            }
                        }

                        yield return(primaryContent as T);
                    }
                }
            }
        }
Example #4
0
        /// <summary>
        /// Decollates changes to content object which should be redirected to other records used as property sources
        /// </summary>
        /// <param name="path">path of content record</param>
        /// <param name="data">content object</param>
        /// <returns>JObject build from content object</returns>
        protected virtual object SetRelated(string path, object data, bool bypassChecks)
        {
            if (!DoCollation)
            {
                return(null);
            }

            System.Versions.PushState(VersioningMode.Specific, new ItemVersion(System, data));

            try
            {
                JObject jObjectContent = JObject.FromObject(data);

                // Establish the records to fetch and fetch them

                Type contentType   = data.GetType().UnextendedType();
                var  rpsAttributes = contentType
                                     .GetCustomAttributes(typeof(RedirectPropertySourceAttribute), true)
                                     .Cast <RedirectPropertySourceAttribute>()
                                     .Where(rpsa => !rpsa.ReadOnly)
                                     .ToList();

                List <Address> addresses = rpsAttributes
                                           .Select(a => new Address(a.ContentType ?? contentType, PathFunctions.Redirect(path, a.SourceDescriptor)))
                                           .Distinct()
                                           .ToList();
                if (addresses == null || addresses.Count == 0)
                {
                    return(data);
                }
                List <object> records = System.Repository.Get(typeof(object), addresses).ToList();

                // Update the fetched referenced records with updated referenced properties on the content object

                List <Address> doneAddrs  = new List <Address>();
                List <object>  vals       = new List <object>();
                var            writebacks = new Dictionary <string[], object>();

                foreach (var rpsAttribute in rpsAttributes)
                {
                    Address address = new Address(
                        rpsAttribute.ContentType ?? contentType,
                        PathFunctions.Redirect(path, rpsAttribute.SourceDescriptor));

                    string refdPath = address.GetAsContentPath();
                    Type   refdType = address.Type;

                    if (refdPath == path && refdType == contentType) // redirected to itself, ignore
                    {
                        continue;
                    }
                    object refdRecord  = records.FirstOrDefault(r => new Address(r) == address);
                    object refdContent = refdRecord;
                    if (refdRecord is IContentContainer)
                    {
                        refdContent = ((IContentContainer)refdRecord).GetContent(System.Extender);
                    }
                    if (refdRecord == null) // adding a new record
                    {
                        refdContent = System.Collator.GetNew(address);
                        refdRecord  = System.Collator.GetContainer(address, refdContent);
                    }

                    JObject         refdObject     = JObject.FromObject(refdContent);
                    List <string[]> writebackPaths = new List <string[]>();
                    foreach (string propertyPath in rpsAttribute.PropertyPaths)
                    {
                        var toFromPaths = GetPaths(propertyPath);
                        if (toFromPaths[0].EndsWith("<"))
                        {
                            toFromPaths[0] = toFromPaths[0].UpToLast("<");
                            toFromPaths[1] = toFromPaths[1].UpToLast("<");
                            writebackPaths.Add(toFromPaths);
                        }
                        refdObject.CopyPropertyFrom(toFromPaths[1], jObjectContent, toFromPaths[0]);
                    }

                    if (refdRecord is IContentContainer)
                    {
                        Type valType = ((IContentContainer)refdRecord).ContentType;
                        valType = System.Extender[valType] ?? valType;
                        ((IContentContainer)refdRecord).SetContent(System, refdObject.ToObject(valType));
                    }
                    else
                    {
                        refdRecord = refdObject.ToObject(refdRecord.GetType());
                    }

                    if (!doneAddrs.Contains(address))
                    {
                        doneAddrs.Add(address);
                        vals.Add(refdRecord);
                    }

                    writebackPaths.Do(wp => writebacks.Add(wp, refdRecord));
                }

                // Create or update referred-to records
                if (vals.Count > 0)
                {
                    Repository.Set(vals, new Dictionary <string, object>
                    {
                        { "bypassChecks", bypassChecks },
                        { "viaCollation", true }
                    });

                    // write back any values configured by attributes (e.g. database index updates)
                    foreach (var kvp in writebacks)
                    {
                        JObject refdObject = JObject.FromObject(kvp.Value);
                        jObjectContent.CopyPropertyFrom(kvp.Key[0], refdObject, kvp.Key[1]);
                    }
                    data = jObjectContent.ToObject(data.GetType());
                }

                return(writebacks.Count > 0 ? data : null);
            }
            finally
            {
                System.Versions.PopState();
            }
        }
Example #5
0
        /// <summary>
        /// Starting from a list of addresses and optionally (or only) the containers at those addresses, fetch
        /// any containers necessary and any other containers required to supply redirected properties for them,
        /// obtain the contained content items and collate their properties, returning the content items at the
        /// addresses.
        /// </summary>
        /// <typeparam name="T">Type of content items to return</typeparam>
        /// <param name="startContainers">Initial list of containers if they are available</param>
        /// <param name="startAddresses">Initial list of addresses, which may be omitted and derived from containers</param>
        /// <returns>List of content items</returns>
        public IEnumerable <T> Collate <T>(IEnumerable <object> startContainers, IEnumerable <Address> startAddresses) where T : class
        {
            // place to store all the containers we have currently
            var containers = new Dictionary <VersionedAddress, object>();

            ItemVersion containerCommonVersion = null;

            // Ensure we have the start addresses
            if (startContainers != null)
            {
                containers             = startContainers.ToDictionary(sc => new VersionedAddress(sc), sc => sc);
                startAddresses         = containers.Keys.Select(va => va.Address).Distinct().ToList();
                containerCommonVersion = ItemVersion.LeastAbstractCommonVersion(containers.Keys.Select(va => va.Version));
            }

            var fetchAddrs = startAddresses
                             .GroupBy(a => a.Type.GetCustomAttributes <RedirectPropertySourceAttribute>())
                             .SelectMany(ag =>
                                         ag.SelectMany(a => ag.Key
                                                       .Select(attr => attr.Redirect(a))
                                                       .Concat(a)))
                             .Distinct()
                             .Except(containers.Keys.Select(va => va.Address))
                             .ToList();

            bool pushVersion = (startContainers != null);

            if (pushVersion) // Get containers in any version that might be relevant to a start container
            {
                VersionManager.Instance.PushState(VersioningMode.Specific, containerCommonVersion);
            }

            try
            {
                // Get all the containers for collation (if current version is not fully specified, may be multiple per address)
                foreach (var cont in Repository.Instance.Get(typeof(object), fetchAddrs))
                {
                    var va = new VersionedAddress(cont);
                    if (containers.ContainsKey(va))
                    {
                        log.Error("Duplicate versioned address in db: " + va.ToString());
                    }
                    else
                    {
                        containers.Add(new VersionedAddress(cont), cont);
                    }
                }
            }
            finally
            {
                if (pushVersion)
                {
                    VersionManager.Instance.PopState();
                }
            }

            var contLookup = containers.ToLookup(kvp => kvp.Key.Address.ToString(), kvp => kvp.Value);

            if (startContainers == null)
            {
                startContainers = startAddresses.SelectMany(a => contLookup[a.ToString()]);
            }

            // We have the data, now collate it into the content from the startContainers
            foreach (var addrTypeG in startAddresses.GroupBy(a => a.Type))
            {
                // Process all the start addresses of a given type

                Type contentType   = addrTypeG.Key;
                var  rpsAttributes = contentType
                                     .GetCustomAttributes(typeof(RedirectPropertySourceAttribute), false)
                                     .Cast <RedirectPropertySourceAttribute>()
                                     .ToList();

                foreach (var addr in addrTypeG)
                {
                    var primaryPath = addr.GetAsContentPath();
                    if (!contLookup.Contains(addr.ToString()))
                    {
                        continue;
                    }

                    foreach (var cont in contLookup[addr.ToString()])
                    {
                        object primaryContent = cont;

                        if (primaryContent is IContentContainer)
                        {
                            primaryContent = ((IContentContainer)primaryContent).GetContent();
                        }

                        foreach (var rpsAttribute in rpsAttributes)
                        {
                            var refAddress = new VersionedAddress(
                                rpsAttribute.ContentType ?? contentType,
                                PathFunctions.Redirect(primaryPath, rpsAttribute.SourceDescriptor),
                                new ItemVersion(cont)
                                );
                            if (refAddress.Address == addr) // redirected to itself, ignore
                            {
                                continue;
                            }
                            object refItem = containers.ContainsKey(refAddress) ? containers[refAddress] : null;
                            if (refItem is IContentContainer)
                            {
                                refItem = ((IContentContainer)refItem).GetContent();
                            }
                            if (refItem != null)
                            {
                                foreach (string propertyPath in rpsAttribute.PropertyPaths)
                                {
                                    var    toFromPaths = GetPaths(propertyPath);
                                    object val         = ReflectionX.GetPropertyValueByPath(refItem, toFromPaths[1]);
                                    var    piSet       = ReflectionX.GetPropertyByPath(primaryContent.GetType(), toFromPaths[0]);
                                    piSet.SetValue(primaryContent, val);
                                }
                            }
                        }

                        yield return(primaryContent as T);
                    }
                }
            }
        }