예제 #1
0
        internal static void CopyParsePropertyValues(IDictionary <string, object> sourceProps, object source, object target, bool isNew, ServiceScope ss, IDictionary <object, object> visits, bool justTraverse)
        {
            // Recursively parse property values for an object graph. This not only adjusts collection types to be trackable concrete types, but registers child objects into the current service scope.

            _propCache.TryGetValue(target.GetType(), out var dic);

            if (dic == null)
            {
                dic = (from a in target.FastGetAllProperties(true, true) select new { Name = a.name, PropertyType = a.type }).ToDictionary((p) => p.Name, (p) => p.PropertyType);
                _propCache[target.GetType()] = dic;
            }

            var iter = sourceProps == null ?
                       (from t in dic select(t.Key, target.FastGetValue(t.Key), t.Value))
                : (from s in sourceProps from t in dic where s.Key == t.Key select(s.Key, s.Value, t.Value));

            var maxdop = Globals.EnableParallelPropertyParsing && Environment.ProcessorCount > 4 && iter.Count() >= Environment.ProcessorCount ? Environment.ProcessorCount >> 2 : 1;

            Interlocked.Add(ref _copyNesting, maxdop);

            try
            {
                Action <(string PropName, object SourceVal, Type TargPropType)> a = ((string PropName, object SourceVal, Type TargPropType)info) =>
                {
                    object wrapped = null;

                    if (ss != null && IsWrappableListType(info.TargPropType, info.SourceVal))
                    {
                        ICEFList wrappedCol = null;

                        if (ss.Settings.InitializeNullCollections || info.SourceVal != null)
                        {
                            // This by definition represents CHILDREN
                            // Use an observable collection we control - namely EntitySet
                            wrappedCol = CreateWrappingList(ss, info.TargPropType, target, info.PropName);
                            target.FastSetValue(info.PropName, wrappedCol);
                        }
                        else
                        {
                            wrappedCol = info.SourceVal as ICEFList;
                        }

                        // Merge any existing data into the collection - as we do this, recursively construct wrappers!
                        if (info.SourceVal != null && wrappedCol != null)
                        {
                            // Based on the above type checks, we know this thing supports IEnumerable
                            var sValEnum = ((System.Collections.IEnumerable)info.SourceVal).GetEnumerator();

                            while (sValEnum.MoveNext())
                            {
                                if (visits.ContainsKey(sValEnum.Current))
                                {
                                    wrapped = visits[sValEnum.Current] ?? sValEnum.Current;
                                }
                                else
                                {
                                    wrapped = ss.InternalCreateAddBase(sValEnum.Current, isNew, null, null, null, visits, true, true);
                                }

                                wrappedCol.AddWrappedItem(wrapped);
                            }
                        }

                        if (ss.Settings.InitializeNullCollections || info.SourceVal != null)
                        {
                            ((ISupportInitializeNotification)wrappedCol).EndInit();
                        }
                    }
                    else
                    {
                        // If the type is a ref type that we manage, then this property represents a PARENT and we should replace/track it (only if we have a PK for it: without one, can't be tracked)
                        if (ss != null && info.SourceVal != null)
                        {
                            var  svt = info.SourceVal.GetType();
                            bool svtok;

                            if (!_sourceValTypeOk.TryGetValue(svt, out svtok))
                            {
                                svtok = !svt.IsValueType && svt != typeof(string) && KeyService.ResolveKeyDefinitionForType(svt).Any();
                                _sourceValTypeOk[svt] = svtok;
                            }

                            if (svtok)
                            {
                                if (visits.ContainsKey(info.SourceVal))
                                {
                                    wrapped = visits[info.SourceVal] ?? info.SourceVal;
                                }
                                else
                                {
                                    var to = ss.GetTrackedByWrapperOrTarget(info.SourceVal);

                                    if (to == null)
                                    {
                                        wrapped = ss.InternalCreateAddBase(info.SourceVal, isNew, null, null, null, visits, true, true);
                                    }
                                    else
                                    {
                                        wrapped = to.GetWrapperTarget();
                                    }
                                }

                                if (wrapped != null)
                                {
                                    target.FastSetValue(info.PropName, wrapped);
                                }
                            }
                            else
                            {
                                if (!justTraverse)
                                {
                                    target.FastSetValue(info.PropName, info.SourceVal);
                                }
                            }
                        }
                        else
                        {
                            if (!justTraverse)
                            {
                                target.FastSetValue(info.PropName, info.SourceVal);
                            }
                        }
                    }
                };

                int resdop = Interlocked.Read(ref _copyNesting) > 12 ? 1 : maxdop;

                if (resdop == 1)
                {
                    foreach (var info in iter)
                    {
                        a.Invoke(info);
                    }
                }
                else
                {
                    Parallel.ForEach(iter, new ParallelOptions()
                    {
                        MaxDegreeOfParallelism = resdop
                    }, (info) =>
                    {
                        using (CEF.UseServiceScope(ss))
                        {
                            a.Invoke(info);
                        }
                    });
                }
            }
            finally
            {
                Interlocked.Add(ref _copyNesting, -maxdop);
            }
        }