Beispiel #1
0
        /// <summary>
        /// Map domain instance
        /// </summary>
        public object MapDomainInstance(Type tDomain, Type tModel, object domainInstance, bool useCache = true, HashSet <Guid> keyStack = null)
        {
            ClassMap classMap = this.m_mapFile.GetModelClassMap(tModel, tDomain);

            if (domainInstance == null)
            {
                return(null);
            }
            else
            {
                var cType = tModel;
                while (cType != null && classMap == null || !tDomain.IsAssignableFrom(Type.GetType(classMap.DomainClass)))
                {
                    cType = cType.BaseType;
                    if (cType != null)
                    {
                        classMap = this.m_mapFile.GetModelClassMap(cType);
                    }
                } // work up the tree
            }

            // Now the property maps
            object retVal = Activator.CreateInstance(tModel);

            // Key?
            if (classMap == null)
            {
                return(retVal);
            }

            // Cache lookup
            var idEnt  = retVal as IIdentifiedEntity;
            var vidEnt = retVal as IVersionedEntity;

            PropertyMap iKeyMap = null;

            if (idEnt != null)
            {
                classMap.TryGetModelProperty("Key", out iKeyMap);
            }
            if (iKeyMap != null)
            {
                object keyValue = tDomain.GetRuntimeProperty(iKeyMap.DomainName).GetValue(domainInstance);
                while (iKeyMap.Via != null)
                {
                    keyValue = keyValue.GetType().GetRuntimeProperty(iKeyMap.Via.DomainName).GetValue(keyValue);
                    iKeyMap  = iKeyMap.Via;
                }
                if (keyValue is byte[])
                {
                    keyValue = new Guid(keyValue as byte[]);
                }

                // Set key vaue
                idEnt.Key = (Guid)keyValue;

                var cache = FireMappingToModel(this, (Guid)keyValue, retVal as IdentifiedData);
                if (cache != null && useCache)
                {
                    return(cache);
                }
            }

            // Classifier value
            String classifierValue   = null;
            String classPropertyName = String.Empty;

            if (!m_domainClassPropertyName.TryGetValue(tModel, out classPropertyName))
            {
                classPropertyName = tModel.GetCustomAttribute <ClassifierAttribute>()?.ClassifierProperty;
                lock (m_domainClassPropertyName)
                    if (!m_domainClassPropertyName.ContainsKey(tModel))
                    {
                        m_domainClassPropertyName.Add(tModel, classPropertyName);
                    }
            }

            if (classPropertyName != null)
            {
                // Key value
                classPropertyName = tModel.GetRuntimeProperty(classPropertyName)?.GetCustomAttribute <SerializationReferenceAttribute>()?.RedirectProperty ?? classPropertyName;

                if (classMap.TryGetModelProperty(classPropertyName ?? "____XXX", out iKeyMap))
                {
                    object keyValue = tDomain.GetRuntimeProperty(iKeyMap.DomainName).GetValue(domainInstance);
                    while (iKeyMap.Via != null)
                    {
                        keyValue = keyValue.GetType().GetRuntimeProperty(iKeyMap.Via.DomainName).GetValue(keyValue);
                        iKeyMap  = iKeyMap.Via;
                    }
                    classifierValue = keyValue?.ToString();
                }
            }

            // Are we currently processing this?
            if (idEnt != null)
            {
                if (keyStack == null)
                {
                    keyStack = new HashSet <Guid>();
                }
                if (idEnt.Key.HasValue)
                {
                    if (keyStack.Contains(idEnt.Key.Value))
                    {
                        return(null);
                    }
                    else
                    {
                        keyStack.Add(idEnt.Key.Value);
                    }
                }
            }

            // Properties
            // Properties
            PropertyInfo[] properties = null;
            Dictionary <String, PropertyInfo[]> propertyClassMap = null;

            if (!s_modelPropertyCache.TryGetValue(tModel, out propertyClassMap))
            {
                lock (s_modelPropertyCache)
                {
                    if (!s_modelPropertyCache.TryGetValue(tModel, out propertyClassMap))
                    {
                        propertyClassMap = new Dictionary <string, PropertyInfo[]>();
                    }

                    if (!s_modelPropertyCache.ContainsKey(tModel))
                    {
                        s_modelPropertyCache.Add(tModel, propertyClassMap);
                    }
                }
            }
            if (!propertyClassMap.TryGetValue(classifierValue ?? String.Empty, out properties))
            {
                lock (s_modelPropertyCache)
                {
                    properties = tModel.GetRuntimeProperties().Where(m => m != null &&
                                                                     m.GetCustomAttribute <DataIgnoreAttribute>() == null &&
                                                                     (primitives.Contains(m.PropertyType) || m.PropertyType.IsEnum ||
                                                                      m.GetCustomAttributes <AutoLoadAttribute>().Any(o => o.ClassCode == classifierValue || o.ClassCode == null)) &&
                                                                     m.CanWrite).ToArray();
                    if (!propertyClassMap.ContainsKey(classifierValue ?? String.Empty))
                    {
                        propertyClassMap.Add(classifierValue ?? String.Empty, properties);
                    }
                }
            }


            // Iterate the properties and map
            foreach (var modelPropertyInfo in properties)
            {
                // Map property
                PropertyMap propMap = null;
                classMap.TryGetModelProperty(modelPropertyInfo.Name, out propMap);

                if (propMap?.DontLoad == true)
                {
                    continue;
                }
                var propInfo = tDomain.GetRuntimeProperty(propMap?.DomainName ?? modelPropertyInfo.Name);
                if (propInfo == null)
                {
#if VERBOSE_DEBUG
                    Debug.WriteLine("Unmapped property ({0}[{1}]).{2}", typeof(TDomain).Name, idEnt.Key, modelPropertyInfo.Name);
#endif
                    continue;
                }

                var originalValue = propInfo.GetValue(domainInstance);
#if VERBOSE_DEBUG
                Debug.WriteLine("Value property ({0}[{1}]).{2} = {3}", typeof(TDomain).Name, idEnt.Key, modelPropertyInfo.Name, originalValue);
#endif

                // Property info
                try
                {
                    if (originalValue == null)
                    {
                        continue;
                    }
                }
                catch (Exception e) // HACK: For some reason, some LINQ providers will return NULL on EntityReferences with no value
                {
                    Debug.WriteLine(e.ToString());
                }

                // Traversal stuff
                PropertyInfo modelProperty  = propMap == null ? modelPropertyInfo : tModel.GetRuntimeProperty(propMap.ModelName);;
                object       sourceObject   = domainInstance;
                PropertyInfo sourceProperty = propInfo;

                // Go through the via elements in the object map. This code traces a path
                // through the domain class instantiating any necessary associative entity
                // Example when a model entity is really two or three tables in the DB..
                // This piece of code does whatever is necessary to traverse the data model,
                // kinda reminds me of a song:
                // 🎶 Ah for just one time, I would take the northwest passage
                // To find the hand of Franklin reaching for the Beaufort Sea.
                // Tracing one warm line, through a land so wide and savage
                // And make a northwest passage to the sea. 🎶
                if (propMap != null)
                {
                    var via = propMap.Via;
                    List <PropertyMap> viaWalk = new List <PropertyMap>();
                    while (via?.DontLoad == false)
                    {
                        viaWalk.Add(via);
                        via = via.Via;
                    }

                    sourceProperty = propInfo;
                    foreach (var p in viaWalk.Select(o => o))
                    {
                        if (!(sourceObject is IList))
                        {
                            sourceObject = sourceProperty.GetValue(sourceObject);
                        }
                        sourceProperty = this.ExtractDomainType(sourceProperty.PropertyType).GetRuntimeProperty(p.DomainName);
                    }
                }

                // validate property type
                if (propMap?.DontLoad == true)
                {
                    continue;
                }

#if VERBOSE_DEBUG
                Debug.WriteLine("Mapping property ({0}[{1}]).{2} = {3}", typeof(TDomain).Name, idEnt.Key, modelPropertyInfo.Name, originalValue);
#endif
                // Set value
                object pValue = null;

                //DebugWriteLine("Unmapped property ({0}).{1}", typeof(TDomain).Name, propInfo.Name);
                if (sourceProperty.PropertyType == typeof(byte[]) && modelProperty.PropertyType.StripNullable() == typeof(Guid)) // Guid to BA
                {
                    modelProperty.SetValue(retVal, new Guid((byte[])sourceProperty.GetValue(sourceObject)));
                }
                else if (modelProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                {
                    modelProperty.SetValue(retVal, sourceProperty.GetValue(sourceObject));
                }
                else if (sourceProperty.PropertyType == typeof(String) && modelProperty.PropertyType == typeof(Type))
                {
                    modelProperty.SetValue(retVal, Type.GetType(sourceProperty.GetValue(sourceObject) as String));
                }
                else if (MapUtil.TryConvert(originalValue, modelProperty.PropertyType, out pValue))
                {
                    modelProperty.SetValue(retVal, pValue);
                }
                // Handles when a map is a list for example doing a VIA over a version relationship
                else if (originalValue is IList)
                {
                    var modelInstance = Activator.CreateInstance(modelProperty.PropertyType) as IList;
                    modelProperty.SetValue(retVal, modelInstance);
                    var instanceMapFunction = typeof(ModelMapper).GetGenericMethod("MapDomainInstance", new Type[] { sourceProperty.PropertyType.GenericTypeArguments[0], modelProperty.PropertyType.GenericTypeArguments[0] },
                                                                                   new Type[] { sourceProperty.PropertyType.GenericTypeArguments[0], typeof(bool), typeof(HashSet <Guid>) });
                    foreach (var itm in originalValue as IList)
                    {
                        // Traverse?
                        var instance = itm;
                        var via      = propMap?.Via;
                        while (via != null)
                        {
                            instance = instance?.GetType().GetRuntimeProperty(via.DomainName)?.GetValue(instance);
                            if (instance is IList)
                            {
                                var        parm          = Expression.Parameter(instance.GetType());
                                Expression aggregateExpr = parm;
                                if (!String.IsNullOrEmpty(via.OrderBy))
                                {
                                    aggregateExpr = parm.Sort(via.OrderBy, via.SortOrder);
                                }
                                aggregateExpr = aggregateExpr.Aggregate(via.Aggregate);

                                // Get the generic method for LIST to be widdled down
                                instance = Expression.Lambda(aggregateExpr, parm).Compile().DynamicInvoke(instance);
                            }
                            via = via.Via;
                        }
                        modelInstance.Add(instanceMapFunction.Invoke(this, new object[] { instance, useCache, keyStack }));
                    }
                }
                // Flat map list 1..1
                else if (typeof(IList).IsAssignableFrom(modelProperty.PropertyType) &&
                         typeof(IList).IsAssignableFrom(sourceProperty.PropertyType))
                {
                    var modelInstance = Activator.CreateInstance(modelProperty.PropertyType) as IList;
                    modelProperty.SetValue(retVal, modelInstance);
                    var instanceMapFunction = typeof(ModelMapper).GetGenericMethod("MapDomainInstance", new Type[] { sourceProperty.PropertyType.GenericTypeArguments[0], modelProperty.PropertyType.GenericTypeArguments[0] },
                                                                                   new Type[] { sourceProperty.PropertyType.GenericTypeArguments[0], typeof(bool), typeof(HashSet <Guid>) });
                    var listValue = sourceProperty.GetValue(sourceObject);

                    // Is this list a versioned association??
                    if (tDomain.GetRuntimeProperty("VersionSequenceId") != null &&
                        sourceProperty.PropertyType.GenericTypeArguments[0].GetRuntimeProperty("EffectiveVersionSequenceId") != null) // Filter!!! Yay!
                    {
                        var        parm          = Expression.Parameter(listValue.GetType());
                        Expression aggregateExpr = null;
                        aggregateExpr = parm.IsActive(domainInstance);
                        listValue     = Expression.Lambda(aggregateExpr, parm).Compile().DynamicInvoke(listValue);
                    }

                    foreach (var itm in listValue as IEnumerable)
                    {
                        modelInstance.Add(instanceMapFunction.Invoke(this, new object[] { itm, useCache, keyStack }));
                    }
                }
                else if (m_mapFile.GetModelClassMap(modelProperty.PropertyType) != null)
                {
                    // TODO: Clean this up
                    var instance = originalValue; //sourceProperty.GetValue(sourceObject);

                    var via = propMap?.Via;
                    while (via != null)
                    {
                        instance = instance?.GetType().GetRuntimeProperty(via.DomainName)?.GetValue(instance);
                        if (instance is IList)
                        {
                            var        parm          = Expression.Parameter(instance.GetType());
                            Expression aggregateExpr = parm;
                            if (!String.IsNullOrEmpty(via.OrderBy))
                            {
                                aggregateExpr = parm.Sort(via.OrderBy, via.SortOrder);
                            }
                            aggregateExpr = aggregateExpr.Aggregate(via.Aggregate);

                            // Get the generic method for LIST to be widdled down
                            instance = Expression.Lambda(aggregateExpr, parm).Compile().DynamicInvoke(instance);
                        }
                        via = via.Via;
                    }
                    if (instance != null)
                    {
                        var instanceMapFunction = typeof(ModelMapper).GetGenericMethod("MapDomainInstance", new Type[] { instance?.GetType(), modelProperty.PropertyType },
                                                                                       new Type[] { instance?.GetType(), typeof(bool), typeof(HashSet <Guid>) });
                        modelProperty.SetValue(retVal, instanceMapFunction.Invoke(this, new object[] { instance, useCache, keyStack }));
                    }
                }
            }

#if VERBOSE_DEBUG
            Debug.WriteLine("Leaving: {0}>{1}", typeof(TDomain).FullName, typeof(TModel).FullName);
#endif
            if (idEnt != null && useCache)
            {
                keyStack.Remove(idEnt.Key.Value);
                FireMappedToModel(this, vidEnt?.VersionKey ?? idEnt?.Key ?? Guid.Empty, retVal as IdentifiedData);
            }
            // (retVal as IdentifiedData).SetDelayLoad(true);

            return(retVal);
        }
Beispiel #2
0
        /// <summary>
        /// Map model instance
        /// </summary>
        public TDomain MapModelInstance <TModel, TDomain>(TModel modelInstance) where TDomain : new()
        {
            ClassMap classMap = this.m_mapFile.GetModelClassMap(typeof(TModel), typeof(TDomain));

            if (classMap == null)
            {
                classMap = this.m_mapFile.GetModelClassMap(typeof(TModel));
            }

            if (classMap == null || modelInstance == null)
            {
                return(default(TDomain));
            }

            // Now the property maps
            TDomain retVal = new TDomain();

            // Properties
            PropertyInfo[] properties = null;
            Dictionary <String, PropertyInfo[]> propertyClassMap = null;

            if (!s_modelPropertyCache.TryGetValue(typeof(TModel), out propertyClassMap))
            {
                lock (s_modelPropertyCache)
                {
                    if (!s_modelPropertyCache.TryGetValue(typeof(TModel), out propertyClassMap))
                    {
                        propertyClassMap = new Dictionary <string, PropertyInfo[]>();
                    }
                    if (!s_modelPropertyCache.ContainsKey(typeof(TModel)))
                    {
                        s_modelPropertyCache.Add(typeof(TModel), propertyClassMap);
                    }
                }
            }

            if (!propertyClassMap.TryGetValue(String.Empty, out properties))
            {
                lock (s_modelPropertyCache)
                {
                    properties = typeof(TModel).GetRuntimeProperties().Where(m => m != null &&
                                                                             m.GetCustomAttribute <DataIgnoreAttribute>() == null &&
                                                                             (primitives.Contains(m.PropertyType) || m.PropertyType.IsEnum) &&
                                                                             m.CanWrite).ToArray();
                    if (!propertyClassMap.ContainsKey(String.Empty))
                    {
                        propertyClassMap.Add(String.Empty, properties);
                    }
                }
            }

            // Iterate through properties
            foreach (var propInfo in properties)
            {
                var propValue = propInfo.GetValue(modelInstance);
                // Property info
                if (propValue == null)
                {
                    continue;
                }

                if (!propInfo.PropertyType.IsPrimitive && propInfo.PropertyType != typeof(Guid) &&
                    (!propInfo.PropertyType.IsGenericType || propInfo.PropertyType.GetGenericTypeDefinition() != typeof(Nullable <>)) &&
                    propInfo.PropertyType != typeof(String) &&
                    propInfo.PropertyType != typeof(DateTime) &&
                    propInfo.PropertyType != typeof(DateTimeOffset) &&
                    propInfo.PropertyType != typeof(Type) &&
                    propInfo.PropertyType != typeof(Decimal) &&
                    propInfo.PropertyType != typeof(byte[]) &&
                    !propInfo.PropertyType.IsEnum)
                {
                    continue;
                }

                // Map property
                PropertyMap propMap = null;
                classMap.TryGetModelProperty(propInfo.Name, out propMap);
                PropertyInfo domainProperty = null;
                Object       targetObject   = retVal;

                // Set
                if (propMap == null)
                {
                    domainProperty = typeof(TDomain).GetRuntimeProperty(propInfo.Name);
                }
                else
                {
                    domainProperty = typeof(TDomain).GetRuntimeProperty(propMap.DomainName);
                }

                object domainValue = null;
                // Set value
                if (domainProperty == null || !domainProperty.CanWrite)
                {
                    continue;
                }
                //Debug.WriteLine ("Unmapped property ({0}).{1}", typeof(TModel).Name, propInfo.Name);
                else if (domainProperty.PropertyType == typeof(byte[]) && propInfo.PropertyType.StripNullable() == typeof(Guid))
                {
                    domainProperty.SetValue(targetObject, ((Guid)propValue).ToByteArray());
                }
                else if (
                    (domainProperty.PropertyType == typeof(DateTime) || domainProperty.PropertyType == typeof(DateTime?)) &&
                    (propInfo.PropertyType == typeof(DateTimeOffset) || propInfo.PropertyType == typeof(DateTimeOffset?)))
                {
                    domainProperty.SetValue(targetObject, ((DateTimeOffset)propValue).DateTime);
                }
                else if (domainProperty.PropertyType.IsAssignableFrom(propInfo.PropertyType))
                {
                    domainProperty.SetValue(targetObject, propValue);
                }
                else if (propInfo.PropertyType == typeof(Type) && domainProperty.PropertyType == typeof(String))
                {
                    domainProperty.SetValue(targetObject, (propValue as Type).AssemblyQualifiedName);
                }
                else if (MapUtil.TryConvert(propValue, domainProperty.PropertyType, out domainValue))
                {
                    domainProperty.SetValue(targetObject, domainValue);
                }
            }

            return(retVal);
        }