Пример #1
0
        private static void CopyChanges(this ConnectionDetail connectionDetail, Label existingLabel, Label newLabel, List <Guid> deletedIds)
        {
            if (newLabel == null)
            {
                return;
            }

            foreach (var localizedLabel in newLabel.LocalizedLabels)
            {
                var existingLocalizedLabel = existingLabel.LocalizedLabels.SingleOrDefault(ll => ll.MetadataId == localizedLabel.MetadataId);

                if (existingLocalizedLabel == null)
                {
                    existingLabel.LocalizedLabels.Add(localizedLabel);
                }
                else
                {
                    connectionDetail.CopyChanges(existingLocalizedLabel, localizedLabel, deletedIds);
                }
            }

            for (var i = existingLabel.LocalizedLabels.Count - 1; i >= 0; i--)
            {
                if (deletedIds.Contains(existingLabel.LocalizedLabels[i].MetadataId.Value))
                {
                    existingLabel.LocalizedLabels.RemoveAt(i);
                }
            }

            if (newLabel.UserLocalizedLabel != null)
            {
                connectionDetail.CopyChanges(existingLabel.UserLocalizedLabel, newLabel.UserLocalizedLabel, deletedIds);
            }
        }
Пример #2
0
        private static void CopyChanges(this ConnectionDetail connectionDetail, MetadataBase existingItem, MetadataBase newItem, List <Guid> deletedIds)
        {
            foreach (var prop in existingItem.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if (!Attribute.IsDefined(prop, typeof(DataMemberAttribute)))
                {
                    continue;
                }

                var newValue      = prop.GetValue(newItem);
                var existingValue = prop.GetValue(existingItem) as MetadataBase;
                var type          = prop.PropertyType;

                if (type.IsArray)
                {
                    type = type.GetElementType();
                }

                if (!typeof(MetadataBase).IsAssignableFrom(type) && !typeof(Label).IsAssignableFrom(type))
                {
                    if (newItem.HasChanged != false)
                    {
                        prop.SetValue(existingItem, newValue);
                    }
                }
                else if (typeof(Label).IsAssignableFrom(type))
                {
                    if (newItem.HasChanged != false)
                    {
                        connectionDetail.CopyChanges((Label)prop.GetValue(existingItem), (Label)newValue, deletedIds);
                    }
                }
                else if (newValue != null)
                {
                    if (prop.PropertyType.IsArray)
                    {
                        connectionDetail.CopyChanges(existingItem, prop, (MetadataBase[])newValue, deletedIds);
                    }
                    else
                    {
                        if (existingValue.MetadataId == ((MetadataBase)newValue).MetadataId)
                        {
                            connectionDetail.CopyChanges(existingValue, (MetadataBase)newValue, deletedIds);
                        }
                        else
                        {
                            prop.SetValue(existingItem, newValue);
                        }
                    }
                }
                else if (existingValue != null && deletedIds.Contains(existingValue.MetadataId.Value))
                {
                    prop.SetValue(existingItem, null);
                }
            }
        }
Пример #3
0
        private static void CopyChanges(this ConnectionDetail connectionDetail, object source, PropertyInfo sourceProperty, MetadataBase[] newArray, List <Guid> deletedIds)
        {
            var existingArray = (MetadataBase[])sourceProperty.GetValue(source);
            var existingList  = new List <MetadataBase>(existingArray);

            // Add any new items and update any modified ones
            foreach (var newItem in newArray)
            {
                var existingItem = existingList.SingleOrDefault(e => e.MetadataId == newItem.MetadataId);

                if (existingItem == null)
                {
                    existingList.Add(newItem);
                }
                else
                {
                    connectionDetail.CopyChanges(existingItem, newItem, deletedIds);
                }
            }

            // Store the new array
            var updatedArray = Array.CreateInstance(sourceProperty.PropertyType.GetElementType(), existingList.Count);

            for (var i = 0; i < existingList.Count; i++)
            {
                updatedArray.SetValue(existingList[i], i);
            }

            sourceProperty.SetValue(source, updatedArray);

            if (deletedIds.Count > 0)
            {
                connectionDetail.RemoveDeletedItems(source, sourceProperty, deletedIds);
            }
        }
Пример #4
0
        /// <summary>
        /// Updates the <see cref="MetadataCache"/>
        /// </summary>
        /// <param name="flush">Indicates if the existing cache should be flushed and a full new copy of the metadata should be retrieved</param>
        public static Task UpdateMetadataCache(this ConnectionDetail connectionDetail, bool flush)
        {
            if (connectionDetail.OrganizationMajorVersion < 8)
            {
                throw new NotSupportedException("Metadata cache is only supported on Dynamics CRM 2016 or later");
            }

            // If there's already an update in progress, don't start a new one
            if (!MetadataCacheLoader.IsCompleted)
            {
                return(MetadataCacheLoader);
            }

            // Load the metadata in a background task
            var task = new Task <MetadataCache>(() =>
            {
                // Load & update metadata cache
                var metadataCachePath = Path.Combine(Path.GetDirectoryName(ConnectionsList.ConnectionsListFilePath), "..", "Metadata", connectionDetail.ConnectionId + ".xml.gz");
                metadataCachePath     = Path.IsPathRooted(metadataCachePath) ? metadataCachePath : Path.Combine(new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName, metadataCachePath);

                var metadataSerializer = new DataContractSerializer(typeof(MetadataCache));
                var metadataCache      = _metadataCache;

                if (metadataCache == null && File.Exists(metadataCachePath) && !flush)
                {
                    try
                    {
                        using (var stream = File.OpenRead(metadataCachePath))
                            using (var gz = new GZipStream(stream, CompressionMode.Decompress))
                            {
                                metadataCache = (MetadataCache)metadataSerializer.ReadObject(gz);
                            }
                    }
                    catch
                    {
                        // If the cache file isn't readable for any reason, throw it away and download a new copy
                    }
                }

                // Get all the metadata that's changed since the last connection
                // If this query changes, increment the version number to ensure any previously cached versions are flushed
                const int queryVersion = 2;

                var metadataQuery = new RetrieveMetadataChangesRequest
                {
                    ClientVersionStamp = !flush && metadataCache?.MetadataQueryVersion == queryVersion ? metadataCache?.ClientVersionStamp : null,
                    Query = new EntityQueryExpression
                    {
                        Properties = new MetadataPropertiesExpression {
                            AllProperties = true
                        },
                        AttributeQuery = new AttributeQueryExpression
                        {
                            Properties = new MetadataPropertiesExpression {
                                AllProperties = true
                            }
                        },
                        RelationshipQuery = new RelationshipQueryExpression
                        {
                            Properties = new MetadataPropertiesExpression {
                                AllProperties = true
                            }
                        }
                    },
                    DeletedMetadataFilters = DeletedMetadataFilters.All
                };

                RetrieveMetadataChangesResponse metadataUpdate;

                try
                {
                    metadataUpdate = (RetrieveMetadataChangesResponse)connectionDetail.GetServiceClient().Execute(metadataQuery);
                }
                catch (FaultException <Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
                {
                    // If the last connection was too long ago, we need to request all the metadata, not just the changes
                    if (ex.Detail.ErrorCode == unchecked ((int)0x80044352))
                    {
                        _metadataCache = null;
                        metadataQuery.ClientVersionStamp = null;
                        metadataUpdate = (RetrieveMetadataChangesResponse)connectionDetail.GetServiceClient().Execute(metadataQuery);
                    }
                    else
                    {
                        throw;
                    }
                }

                if (metadataCache == null || flush)
                {
                    // If we didn't have a previous cache, just start a fresh one
                    metadataCache = new MetadataCache();
                    metadataCache.EntityMetadata       = metadataUpdate.EntityMetadata.ToArray();
                    metadataCache.MetadataQueryVersion = queryVersion;
                }
                else
                {
                    // We had a cached version of the metadata before, so now we need to merge in the changes
                    var deletedIds = metadataUpdate.DeletedMetadata == null ? new List <Guid>() : metadataUpdate.DeletedMetadata.SelectMany(kvp => kvp.Value).Distinct().ToList();

                    connectionDetail.CopyChanges(metadataCache, typeof(MetadataCache).GetProperty(nameof(Utils.MetadataCache.EntityMetadata)), metadataUpdate.EntityMetadata.ToArray(), deletedIds);
                }

                _metadataCache = metadataCache;

                // Save the latest metadata cache
                if (metadataCache.ClientVersionStamp != metadataUpdate.ServerVersionStamp ||
                    metadataCache.MetadataQueryVersion != queryVersion)
                {
                    metadataCache.ClientVersionStamp   = metadataUpdate.ServerVersionStamp;
                    metadataCache.MetadataQueryVersion = queryVersion;

                    Directory.CreateDirectory(Path.GetDirectoryName(metadataCachePath));

                    using (var stream = File.Create(metadataCachePath))
                        using (var gz = new GZipStream(stream, CompressionLevel.Optimal))
                        {
                            metadataSerializer.WriteObject(gz, metadataCache);
                        }
                }

                return(metadataCache);
            });

            task.ConfigureAwait(false);

            // Store the current metadata loading task and run it
            MetadataCacheLoader = task;
            task.Start();
            return(task);
        }