ClientType.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataWeb / Client / System / Data / Services / Client / ClientType.cs / 1 / ClientType.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
// type resolver
//  
//--------------------------------------------------------------------- 

namespace System.Data.Services.Client 
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics; 
    using System.Reflection;
    using System.Data.Services.Common; 
    using System.Linq; 

    ///  
    /// wrapper around a clr type to
    ///     get/set properties
    ///     add items to collections
    ///     support open types 
    /// 
    [DebuggerDisplay("{ElementTypeName}")] 
    internal sealed class ClientType 
    {
        /// what is the clr full name using ToString for generic name expansion 
        internal readonly string ElementTypeName;

        /// what clr type does this represent
        internal readonly Type ElementType; 

        /// if HasKeys then EntityType else if !KnownType then ComplexType else PrimitiveType 
        internal readonly bool HasKeys; 

        /// count of keys on entity type 
        internal readonly int KeyCount;

        #region static fields
        /// appdomain cache discovered types with properties that we materialize 
        private static readonly Dictionary types = new Dictionary();
 
        /// cache <T> and wireName to mapped type 
        private static readonly Dictionary namedTypes = new Dictionary(new TypeNameEqualityComparer());
        #endregion 

#if ASTORIA_OPEN_OBJECT
        /// IDictionary<string,object> OpenProperites { get; }
        private readonly ClientProperty openProperties; 
#endif
 
        /// properties 
        private ArraySet properties;
 
        /// Property that holds data for ATOM-style media link entries
        private ClientProperty mediaDataMember;

        ///  
        /// discover and prepare properties for usage
        ///  
        /// type being processed 
        /// parameter name
        /// Whether the skip the check for settable properties. 
        private ClientType(Type type, string typeName, bool skipSettableCheck)
        {
            Debug.Assert(null != type, "null type");
            Debug.Assert(!String.IsNullOrEmpty(typeName), "empty typeName"); 

            this.ElementTypeName = typeName; 
            this.ElementType = Nullable.GetUnderlyingType(type) ?? type; 
#if ASTORIA_OPEN_OBJECT
            string openObjectPropertyName = null; 
#endif
            if (!ClientConvert.IsKnownType(this.ElementType))
            {
#if ASTORIA_OPEN_OBJECT 
                #region OpenObject determined by walking type hierarchy and looking for [OpenObjectAttribute("PropertyName")]
                Type openObjectDeclared = this.ElementType; 
                for (Type tmp = openObjectDeclared; (null != tmp) && (typeof(object) != tmp); tmp = tmp.BaseType) 
                {
                    object[] attributes = openObjectDeclared.GetCustomAttributes(typeof(OpenObjectAttribute), false); 
                    if (1 == attributes.Length)
                    {
                        if (null != openObjectPropertyName)
                        { 
                            throw Error.InvalidOperation(Strings.Clienttype_MultipleOpenProperty(this.ElementTypeName));
                        } 
 
                        openObjectPropertyName = ((OpenObjectAttribute)attributes[0]).OpenObjectPropertyName;
                        openObjectDeclared = tmp; 
                    }
                }
                #endregion
#endif 

                Type keyPropertyDeclaredType = null; 
                DataServiceKeyAttribute dska = type.GetCustomAttributes(true).OfType().FirstOrDefault(); 
                foreach (PropertyInfo pinfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
                { 
                    //// examples where class

                    //// the normal examples
                    //// PropertyType Property { get; set } 
                    //// Nullable Property { get; set; }
 
                    //// if 'Property: struct' then we would be unable set the property during construction (and have them stick) 
                    //// but when its a class, we can navigate if non-null and set the nested properties
                    //// PropertyType Property { get; } where PropertyType: class 

                    //// we do support adding elements to collections
                    //// ICollection { get; /*ignored set;*/ }
 
                    //// indexed properties are not suported because
                    //// we don't have anything to use as the index 
                    //// PropertyType Property[object x] { /*ignored get;*/ /*ignored set;*/ } 

                    //// also ignored 
                    //// if PropertyType.IsPointer (like byte*)
                    //// if PropertyType.IsArray except for byte[] and char[]
                    //// if PropertyType == IntPtr or UIntPtr
 
                    Type ptype = pinfo.PropertyType; // class / interface / value
                    ptype = Nullable.GetUnderlyingType(ptype) ?? ptype; 
 
                    if (ptype.IsPointer ||
                        (ptype.IsArray && (typeof(byte[]) != ptype) && typeof(char[]) != ptype) || 
                        (typeof(IntPtr) == ptype) ||
                        (typeof(UIntPtr) == ptype))
                    {
                        continue; 
                    }
 
                    Debug.Assert(!ptype.ContainsGenericParameters, "remove when test case is found that encounters this"); 

                    if (pinfo.CanRead && 
                        (!ptype.IsValueType || pinfo.CanWrite) &&
                        !ptype.ContainsGenericParameters &&
                        (0 == pinfo.GetIndexParameters().Length))
                    { 
                        #region IsKey?
                        bool keyProperty = dska != null ? dska.KeyNames.Contains(pinfo.Name) : false; 
 
                        if (keyProperty)
                        { 
                            if (null == keyPropertyDeclaredType)
                            {
                                keyPropertyDeclaredType = pinfo.DeclaringType;
                            } 
                            else if (keyPropertyDeclaredType != pinfo.DeclaringType)
                            { 
                                throw Error.InvalidOperation(Strings.ClientType_KeysOnDifferentDeclaredType(this.ElementTypeName)); 
                            }
 
                            if (!ClientConvert.IsKnownType(ptype))
                            {
                                throw Error.InvalidOperation(Strings.ClientType_KeysMustBeSimpleTypes(this.ElementTypeName));
                            } 

                            this.KeyCount++; 
                        } 
                        #endregion
 
#if ASTORIA_OPEN_OBJECT
                        #region IsOpenObjectProperty?
                        bool openProperty = (openObjectPropertyName == pinfo.Name) &&
                                              typeof(IDictionary).IsAssignableFrom(ptype); 
                        Debug.Assert(keyProperty != openProperty || (!keyProperty && !openProperty), "key can't be open type");
                        #endregion 
 
                        ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty, openProperty);
 
                        if (!property.OpenObjectProperty)
#else
                        ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty);
#endif 
                        {
                            if (!this.properties.Add(property, ClientProperty.NameEquality)) 
                            { 
                                // 2nd property with same name shadows another property
                                int shadow = this.IndexOfProperty(property.PropertyName); 
                                if (!property.DeclaringType.IsAssignableFrom(this.properties[shadow].DeclaringType))
                                {   // the new property is on the most derived class
                                    this.properties.RemoveAt(shadow);
                                    this.properties.Add(property, null); 
                                }
                            } 
                        } 
#if ASTORIA_OPEN_OBJECT
                        else 
                        {
                            if (pinfo.DeclaringType == openObjectDeclared)
                            {
                                this.openProperties = property; 
                            }
                        } 
#endif 
                    }
                } 

                #region No KeyAttribute, discover key by name pattern { DeclaringType.Name+ID, ID }
                if (null == keyPropertyDeclaredType)
                { 
                    ClientProperty key = null;
                    for (int i = this.properties.Count - 1; 0 <= i; --i) 
                    { 
                        string propertyName = this.properties[i].PropertyName;
                        if (propertyName.EndsWith("ID", StringComparison.Ordinal)) 
                        {
                            string declaringTypeName = this.properties[i].DeclaringType.Name;
                            if ((propertyName.Length == (declaringTypeName.Length + 2)) &&
                                propertyName.StartsWith(declaringTypeName, StringComparison.Ordinal)) 
                            {
                                // matched "DeclaringType.Name+ID" pattern 
                                if ((null == keyPropertyDeclaredType) || 
                                    this.properties[i].DeclaringType.IsAssignableFrom(keyPropertyDeclaredType))
                                { 
                                    keyPropertyDeclaredType = this.properties[i].DeclaringType;
                                    key = this.properties[i];
                                }
                            } 
                            else if ((null == keyPropertyDeclaredType) && (2 == propertyName.Length))
                            { 
                                // matched "ID" pattern 
                                keyPropertyDeclaredType = this.properties[i].DeclaringType;
                                key = this.properties[i]; 
                            }
                        }
                    }
 
                    if (null != key)
                    { 
                        Debug.Assert(0 == this.KeyCount, "shouldn't have a key yet"); 
                        key.KeyProperty = true;
                        this.KeyCount++; 
                    }
                }
                else if (this.KeyCount != dska.KeyNames.Count)
                { 
                    var m = (from string a in dska.KeyNames
                             where null == (from b in this.properties 
                                            where b.PropertyName == a 
                                            select b).FirstOrDefault()
                             select a).First(); 
                    throw Error.InvalidOperation(Strings.ClientType_MissingProperty(this.ElementTypeName, m));
                }
                #endregion
 
                this.HasKeys = (null != keyPropertyDeclaredType);
                Debug.Assert(this.KeyCount == this.Properties.Where(k => k.KeyProperty).Count(), "KeyCount mismatch"); 
 
                this.WireUpMimeTypeProperties();
                this.CheckMediaLinkEntry(); 

                if (!skipSettableCheck)
                {
#if ASTORIA_OPEN_OBJECT 
                    if ((0 == this.properties.Count) && (null == this.openProperties))
#else 
                    if (0 == this.properties.Count) 
#endif
                    {   // implicit construction? 
                        throw Error.InvalidOperation(Strings.ClientType_NoSettableFields(this.ElementTypeName));
                    }
                }
            } 

            this.properties.Sort(ClientProperty.GetPropertyName, String.CompareOrdinal); 
 
#if ASTORIA_OPEN_OBJECT
            #region Validate OpenObjectAttribute was used 
            if ((null != openObjectPropertyName) && (null == this.openProperties))
            {
                throw Error.InvalidOperation(Strings.ClientType_MissingOpenProperty(this.ElementTypeName, openObjectPropertyName));
            } 

            Debug.Assert((null != openObjectPropertyName) == (null != this.openProperties), "OpenProperties mismatch"); 
            #endregion 
#endif
        } 

        /// Properties sorted by name
        internal ArraySet Properties
        { 
            get { return this.properties; }
        } 
 
        /// Property that holds data for ATOM-style media link entries
        internal ClientProperty MediaDataMember 
        {
            get { return this.mediaDataMember; }
        }
 
        /// 
        /// get a client type resolver 
        ///  
        /// type to wrap
        /// client type 
        internal static ClientType Create(Type type)
        {
            return Create(type, true /* expectModelType */);
        } 

        ///  
        /// get a client type resolver 
        /// 
        /// type to wrap 
        /// Whether the type is expected to be a model type.
        /// client type
        internal static ClientType Create(Type type, bool expectModelType)
        { 
            ClientType clientType;
            lock (ClientType.types) 
            { 
                ClientType.types.TryGetValue(type, out clientType);
            } 

            if (null == clientType)
            {
                bool skipSettableCheck = !expectModelType; 
                clientType = new ClientType(type, type.ToString(), skipSettableCheck); // ToString expands generic type name where as FullName does not
                if (expectModelType) 
                { 
                    lock (ClientType.types)
                    { 
                        ClientType existing;
                        if (ClientType.types.TryGetValue(type, out existing))
                        {
                            clientType = existing; 
                        }
                        else 
                        { 
                            ClientType.types.Add(type, clientType);
                        } 
                    }
                }
            }
 
            return clientType;
        } 
 
#if !ASTORIA_LIGHT
        ///  
        /// resolve the wireName/userType pair to a CLR type
        /// 
        /// type name sent by server
        /// type passed by user or on propertyType from a class 
        /// mapped clr type
        internal static Type ResolveFromName(string wireName, Type userType) 
#else 
        /// 
        /// resolve the wireName/userType pair to a CLR type 
        /// 
        /// type name sent by server
        /// type passed by user or on propertyType from a class
        /// typeof context for strongly typed assembly 
        /// mapped clr type
        internal static Type ResolveFromName(string wireName, Type userType, Type contextType) 
#endif 
        {
            Type foundType; 

            TypeName typename;
            typename.Type = userType;
            typename.Name = wireName; 

            lock (ClientType.namedTypes) 
            { 
                ClientType.namedTypes.TryGetValue(typename, out foundType);
            } 

            if (null == foundType)
            {
                string name = wireName; 
                int index = wireName.LastIndexOf('.');
                if ((0 <= index) && (index < wireName.Length - 1)) 
                { 
                    name = wireName.Substring(index + 1);
                } 

                if (userType.Name == name)
                {
                    foundType = userType; 
                }
                else 
                { 
#if !ASTORIA_LIGHT
                    // searching only loaded assemblies, not referenced assemblies 
                    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
#else
                    foreach (Assembly assembly in new Assembly[] { userType.Assembly, contextType.Assembly }.Distinct())
#endif 
                    {
                        Type found = assembly.GetType(wireName, false); 
                        ResolveSubclass(name, userType, found, ref foundType); 

                        if (null == found) 
                        {
                            Type[] types = null;
                            try
                            { 
                                types = assembly.GetTypes();
                            } 
                            catch (ReflectionTypeLoadException) 
                            {
                            } 

                            if (null != types)
                            {
                                foreach (Type t in types) 
                                {
                                    ResolveSubclass(name, userType, t, ref foundType); 
                                } 
                            }
                        } 
                    }
                }

                if (null != foundType) 
                {
                    lock (ClientType.namedTypes) 
                    { 
                        ClientType.namedTypes[typename] = foundType;
                    } 
                }
            }

            return foundType; 
        }
 
        ///  
        /// get concrete type that implements the genericTypeDefinitation
        ///  
        /// starting type
        /// the generic type definition to find
        /// concrete type that implementats the generic type
        internal static Type GetImplementationType(Type propertyType, Type genericTypeDefinition) 
        {
            if (IsConstructedGeneric(propertyType, genericTypeDefinition)) 
            {   // propertyType is ICollection 
                return propertyType;
            } 
            else
            {
                Type implementationType = null;
                foreach (Type interfaceType in propertyType.GetInterfaces()) 
                {
                    if (IsConstructedGeneric(interfaceType, genericTypeDefinition)) 
                    { 
                        if (null == implementationType)
                        {   // found implmentation of ICollection 
                            implementationType = interfaceType;
                        }
                        else
                        {   // ICollection and ICollection 
                            throw Error.NotSupported(Strings.ClientType_MultipleImplementationNotSupported);
                        } 
                    } 
                }
 
                return implementationType;
            }
        }
 
        /// 
        /// get element type, resolves ICollection and Nullable 
        ///  
        /// starting type
        /// the generic type definition to find 
        /// the method to search for
        /// the collection type if found
        /// element types
        internal static MethodInfo GetCollectionMethod(Type propertyType, Type genericTypeDefinition, string methodName, out Type type) 
        {
            Debug.Assert(null != propertyType, "null propertyType"); 
            Debug.Assert(null != genericTypeDefinition, "null genericTypeDefinition"); 
            Debug.Assert(genericTypeDefinition.IsGenericTypeDefinition, "!IsGenericTypeDefinition");
 
            type = null;

            Type implementationType = GetImplementationType(propertyType, genericTypeDefinition);
            if (null != implementationType) 
            {
                Type[] genericArguments = implementationType.GetGenericArguments(); 
                MethodInfo methodInfo = implementationType.GetMethod(methodName); 
                Debug.Assert(null != methodInfo, "should have found the method");
 
#if DEBUG
                Debug.Assert(null != genericArguments, "null genericArguments");
                ParameterInfo[] parameters = methodInfo.GetParameters();
                if (0 < parameters.Length) 
                {
                    // following assert was disabled for Contains which returns bool 
                    // Debug.Assert(typeof(void) == methodInfo.ReturnParameter.ParameterType, "method doesn't return void"); 

                    Debug.Assert(genericArguments.Length == parameters.Length, "genericArguments don't match parameters"); 
                    for (int i = 0; i < genericArguments.Length; ++i)
                    {
                        Debug.Assert(genericArguments[i] == parameters[i].ParameterType, "parameter doesn't match generic argument");
                    } 
                }
#endif 
                type = genericArguments[genericArguments.Length - 1]; 
                return methodInfo;
            } 

            return null;
        }
 
        /// 
        /// create object using default constructor 
        ///  
        /// instance of propertyType
        internal object CreateInstance() 
        {
            return Activator.CreateInstance(this.ElementType);
        }
 
        /// 
        /// get property wrapper for a property name, might be method around open types for otherwise unknown properties 
        ///  
        /// property name
        /// are missing properties ignored 
        /// property wrapper
        /// for unknown properties on closed types
        internal ClientProperty GetProperty(string propertyName, bool ignoreMissingProperties)
        { 
            int index = this.IndexOfProperty(propertyName);
            if (0 <= index) 
            { 
                return this.properties[index];
            } 
#if ASTORIA_OPEN_OBJECT
            else if (null != this.openProperties)
            {
                return this.openProperties; 
            }
#endif 
            else if (!ignoreMissingProperties) 
            {
                throw Error.InvalidOperation(Strings.ClientType_MissingProperty(this.ElementTypeName, propertyName)); 
            }

            return null;
        } 

        ///  
        /// Checks whether the specified  is a 
        /// closed constructed type of the generic type.
        ///  
        /// Type to check.
        /// Generic type for checkin.
        /// true if  is a constructed type of .
        /// The check is an immediate check; no inheritance rules are applied. 
        private static bool IsConstructedGeneric(Type type, Type genericTypeDefinition)
        { 
            Debug.Assert(type != null, "type != null"); 
            Debug.Assert(!type.ContainsGenericParameters, "remove when test case is found that encounters this");
            Debug.Assert(genericTypeDefinition != null, "genericTypeDefinition != null"); 

            return type.IsGenericType && (type.GetGenericTypeDefinition() == genericTypeDefinition) && !type.ContainsGenericParameters;
        }
 
        /// 
        /// is the type a visible subclass with correct name 
        ///  
        /// type name from server
        /// the type from user for materialization or property type 
        /// type being tested
        /// the previously discovered matching type
        /// if the mapping is ambiguous
        private static void ResolveSubclass(string wireClassName, Type userType, Type type, ref Type existing) 
        {
            if ((null != type) && type.IsVisible && (wireClassName == type.Name) && userType.IsAssignableFrom(type)) 
            { 
                if (null != existing)
                { 
                    throw Error.InvalidOperation(Strings.Deserialize_Current(existing, type));
                }

                existing = type; 
            }
        } 
 
        /// get the index of a property
        /// propertyName 
        /// index else -1
        private int IndexOfProperty(string propertyName)
        {
            return this.properties.IndexOf(propertyName, ClientProperty.GetPropertyName, String.Equals); 
        }
 
        ///  
        /// Find properties with dynamic MIME type related properties and
        /// set the references from each ClientProperty to its related MIME type property 
        /// 
        private void WireUpMimeTypeProperties()
        {
            MimeTypePropertyAttribute attribute = (MimeTypePropertyAttribute)this.ElementType.GetCustomAttributes(typeof(MimeTypePropertyAttribute), true).SingleOrDefault(); 
            if (null != attribute)
            { 
                int dataIndex, mimeTypeIndex; 
                if ((0 > (dataIndex = this.IndexOfProperty(attribute.DataPropertyName))) ||
                    (0 > (mimeTypeIndex = this.IndexOfProperty(attribute.MimeTypePropertyName)))) 
                {
                    throw Error.InvalidOperation(Strings.ClientType_MissingMimeTypeProperty(attribute.DataPropertyName, attribute.MimeTypePropertyName));
                }
 
                Debug.Assert(0 <= dataIndex, "missing data property");
                Debug.Assert(0 <= mimeTypeIndex, "missing mime type property"); 
                this.Properties[dataIndex].MimeTypeProperty = this.Properties[mimeTypeIndex]; 
            }
        } 

        /// 
        /// Check if this type represents an ATOM-style media link entry and
        /// if so mark the ClientType as such 
        /// 
        private void CheckMediaLinkEntry() 
        { 
            object[] attributes = this.ElementType.GetCustomAttributes(typeof(MediaEntryAttribute), true);
            if (attributes != null && attributes.Length > 0) 
            {
                Debug.Assert(attributes.Length == 1, "The AttributeUsage in the attribute definition should be presenting more than 1 per property");

                int index = this.IndexOfProperty(((MediaEntryAttribute)attributes[0]).MediaMemberName); 
                if (index < 0)
                { 
                    throw Error.InvalidOperation(Strings.ClientType_MissingMediaEntryProperty( 
                        ((MediaEntryAttribute)attributes[0]).MediaMemberName));
                } 

                this.mediaDataMember = this.properties[index];
            }
        } 

        /// type + wireName combination 
        private struct TypeName 
        {
            /// type 
            internal Type Type;

            /// type name from server
            internal string Name; 
        }
 
        ///  
        /// wrapper around property methods
        ///  
        [DebuggerDisplay("{PropertyName}")]
        internal sealed class ClientProperty
        {
            /// property name for debugging 
            internal readonly string PropertyName;
 
            /// type of the property 
            internal readonly Type NullablePropertyType;
 
            /// type of the property
            internal readonly Type PropertyType;

            /// what is the nested collection element 
            internal readonly Type CollectionType;
 
            ///  
            /// Is this a known primitive/reference type or an entity/complex/collection type?
            ///  
            internal readonly bool IsKnownType;

#if ASTORIA_OPEN_OBJECT
            /// IDictionary<string,object> OpenProperites { get; } 
            internal readonly bool OpenObjectProperty;
#endif 
 
            /// property getter
            private readonly MethodInfo propertyGetter; 

            /// property setter
            private readonly MethodInfo propertySetter;
 
            /// "set_Item" method supporting IDictionary properties
            private readonly MethodInfo setMethod; 
 
            /// "Add" method supporting ICollection<> properties
            private readonly MethodInfo addMethod; 

            /// "Remove" method supporting ICollection<> properties
            private readonly MethodInfo removeMethod;
 
            /// "Contains" method support ICollection<> properties
            private readonly MethodInfo containsMethod; 
 
            /// IsKeyProperty?
            private bool keyProperty; 

            /// The other property in this type that holds the MIME type for this one
            private ClientProperty mimeTypeProperty;
 
            /// 
            /// constructor 
            ///  
            /// property
            /// propertyType 
            /// keyProperty
#if ASTORIA_OPEN_OBJECT
            /// openObjectProperty
            internal ClientProperty(PropertyInfo property, Type propertyType, bool keyProperty, bool openObjectProperty) 
#else
            internal ClientProperty(PropertyInfo property, Type propertyType, bool keyProperty) 
#endif 
            {
                Debug.Assert(null != property, "null property"); 
                Debug.Assert(null != propertyType, "null propertyType");
                Debug.Assert(null == Nullable.GetUnderlyingType(propertyType), "should already have been denullified");

                this.PropertyName = property.Name; 
                this.NullablePropertyType = property.PropertyType;
                this.PropertyType = propertyType; 
                this.propertyGetter = property.GetGetMethod(); 
                this.propertySetter = property.GetSetMethod();
                this.keyProperty = keyProperty; 
#if ASTORIA_OPEN_OBJECT
                this.OpenObjectProperty = openObjectProperty;
#endif
 
                this.IsKnownType = ClientConvert.IsKnownType(propertyType);
                if (!this.IsKnownType) 
                { 
                    this.setMethod = GetCollectionMethod(this.PropertyType, typeof(IDictionary<,>), "set_Item", out this.CollectionType);
                    if (null == this.setMethod) 
                    {
                        this.containsMethod = GetCollectionMethod(this.PropertyType, typeof(ICollection<>), "Contains", out this.CollectionType);
                        this.addMethod = GetCollectionMethod(this.PropertyType, typeof(ICollection<>), "Add", out this.CollectionType);
                        this.removeMethod = GetCollectionMethod(this.PropertyType, typeof(ICollection<>), "Remove", out this.CollectionType); 
                    }
                } 
 
                Debug.Assert(!this.keyProperty || this.IsKnownType, "can't have an random type as key");
            } 

            /// what type was this property declared on?
            internal Type DeclaringType
            { 
                get { return this.propertyGetter.DeclaringType; }
            } 
 
            /// Does this property particpate in the primary key?
            internal bool KeyProperty 
            {
                get { return this.keyProperty; }
                set { this.keyProperty = value; }
            } 

            /// The other property in this type that holds the MIME type for this one 
            internal ClientProperty MimeTypeProperty 
            {
                get { return this.mimeTypeProperty; } 
                set { this.mimeTypeProperty = value; }
            }

            /// get KeyProperty 
            /// x
            /// KeyProperty 
            internal static bool GetKeyProperty(ClientProperty x) 
            {
                return x.KeyProperty; 
            }

            /// get property name
            /// x 
            /// PropertyName
            internal static string GetPropertyName(ClientProperty x) 
            { 
                return x.PropertyName;
            } 

            /// compare name equality
            /// x
            /// y 
            /// true if the property names are equal; false otherwise.
            internal static bool NameEquality(ClientProperty x, ClientProperty y) 
            { 
                return String.Equals(x.PropertyName, y.PropertyName);
            } 

            /// 
            /// get property value from an object
            ///  
            /// object to get the property value from
            /// property value 
            internal object GetValue(object instance) 
            {
                Debug.Assert(null != instance, "null instance"); 
                Debug.Assert(null != this.propertyGetter, "null propertyGetter");
                return this.propertyGetter.Invoke(instance, null);
            }
 
            /// 
            /// remove a item from collection 
            ///  
            /// collection
            /// item to remove 
            internal void RemoveValue(object instance, object value)
            {
                Debug.Assert(null != instance, "null instance");
                Debug.Assert(null != this.removeMethod, "missing removeMethod"); 

                Debug.Assert(this.PropertyType.IsAssignableFrom(instance.GetType()), "unexpected collection instance"); 
                Debug.Assert((null == value) || this.CollectionType.IsAssignableFrom(value.GetType()), "unexpected collection value to add"); 
                this.removeMethod.Invoke(instance, new object[] { value });
            } 

            /// 
            /// set property value on an object
            ///  
            /// object to set the property value on
            /// property value 
            /// used for open type 
            /// allow add to a collection if available, else allow setting collection property
#if ASTORIA_OPEN_OBJECT 
            /// cached OpenProperties dictionary
            internal void SetValue(object instance, object value, string propertyName, ref object openProperties, bool allowAdd)
#else
            internal void SetValue(object instance, object value, string propertyName, bool allowAdd) 
#endif
            { 
                Debug.Assert(null != instance, "null instance"); 
                if (null != this.setMethod)
                { 
#if ASTORIA_OPEN_OBJECT
                    if (this.OpenObjectProperty)
                    {
                        if (null == openProperties) 
                        {
                            if (null == (openProperties = this.propertyGetter.Invoke(instance, null))) 
                            { 
                                throw Error.NotSupported(Strings.ClientType_NullOpenProperties(this.PropertyName));
                            } 
                        }

                        ((IDictionary)openProperties)[propertyName] = value;
                    } 
                    else
#endif 
                    { 
                        Debug.Assert(this.PropertyType.IsAssignableFrom(instance.GetType()), "unexpected dictionary instance");
                        Debug.Assert((null == value) || this.CollectionType.IsAssignableFrom(value.GetType()), "unexpected dictionary value to set"); 

                        // ((IDictionary)instance)[propertyName] = (CollectionType)value;
                        this.setMethod.Invoke(instance, new object[] { propertyName, value });
                    } 
                }
                else if (allowAdd && (null != this.addMethod)) 
                { 
                    Debug.Assert(this.PropertyType.IsAssignableFrom(instance.GetType()), "unexpected collection instance");
                    Debug.Assert((null == value) || this.CollectionType.IsAssignableFrom(value.GetType()), "unexpected collection value to add"); 

                    // ((ICollection)instance).Add((CollectionType)value);
                    if (!(bool)this.containsMethod.Invoke(instance, new object[] { value }))
                    { 
                        this.addMethod.Invoke(instance, new object[] { value });
                    } 
                } 
                else if (null != this.propertySetter)
                { 
                    Debug.Assert((null == value) || this.PropertyType.IsAssignableFrom(value.GetType()), "unexpected property value to set");

                    // ((ElementType)instance).PropertyName = (PropertyType)value;
                    this.propertySetter.Invoke(instance, new object[] { value }); 
                }
                else 
                { 
                    throw Error.InvalidOperation(Strings.ClientType_MissingProperty(value.GetType().ToString(), propertyName));
                } 
            }
        }

        /// equality comparer for TypeName 
        private sealed class TypeNameEqualityComparer : IEqualityComparer
        { 
            /// equality comparer for TypeName 
            /// left type
            /// right type 
            /// true if x and y are equal
            public bool Equals(TypeName x, TypeName y)
            {
                return (x.Type == y.Type && x.Name == y.Name); 
            }
 
            /// compute hashcode for TypeName 
            /// object to compute hashcode for
            /// computed hashcode 
            public int GetHashCode(TypeName obj)
            {
                return obj.Type.GetHashCode() ^ obj.Name.GetHashCode();
            } 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
// type resolver
//  
//--------------------------------------------------------------------- 

namespace System.Data.Services.Client 
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics; 
    using System.Reflection;
    using System.Data.Services.Common; 
    using System.Linq; 

    ///  
    /// wrapper around a clr type to
    ///     get/set properties
    ///     add items to collections
    ///     support open types 
    /// 
    [DebuggerDisplay("{ElementTypeName}")] 
    internal sealed class ClientType 
    {
        /// what is the clr full name using ToString for generic name expansion 
        internal readonly string ElementTypeName;

        /// what clr type does this represent
        internal readonly Type ElementType; 

        /// if HasKeys then EntityType else if !KnownType then ComplexType else PrimitiveType 
        internal readonly bool HasKeys; 

        /// count of keys on entity type 
        internal readonly int KeyCount;

        #region static fields
        /// appdomain cache discovered types with properties that we materialize 
        private static readonly Dictionary types = new Dictionary();
 
        /// cache <T> and wireName to mapped type 
        private static readonly Dictionary namedTypes = new Dictionary(new TypeNameEqualityComparer());
        #endregion 

#if ASTORIA_OPEN_OBJECT
        /// IDictionary<string,object> OpenProperites { get; }
        private readonly ClientProperty openProperties; 
#endif
 
        /// properties 
        private ArraySet properties;
 
        /// Property that holds data for ATOM-style media link entries
        private ClientProperty mediaDataMember;

        ///  
        /// discover and prepare properties for usage
        ///  
        /// type being processed 
        /// parameter name
        /// Whether the skip the check for settable properties. 
        private ClientType(Type type, string typeName, bool skipSettableCheck)
        {
            Debug.Assert(null != type, "null type");
            Debug.Assert(!String.IsNullOrEmpty(typeName), "empty typeName"); 

            this.ElementTypeName = typeName; 
            this.ElementType = Nullable.GetUnderlyingType(type) ?? type; 
#if ASTORIA_OPEN_OBJECT
            string openObjectPropertyName = null; 
#endif
            if (!ClientConvert.IsKnownType(this.ElementType))
            {
#if ASTORIA_OPEN_OBJECT 
                #region OpenObject determined by walking type hierarchy and looking for [OpenObjectAttribute("PropertyName")]
                Type openObjectDeclared = this.ElementType; 
                for (Type tmp = openObjectDeclared; (null != tmp) && (typeof(object) != tmp); tmp = tmp.BaseType) 
                {
                    object[] attributes = openObjectDeclared.GetCustomAttributes(typeof(OpenObjectAttribute), false); 
                    if (1 == attributes.Length)
                    {
                        if (null != openObjectPropertyName)
                        { 
                            throw Error.InvalidOperation(Strings.Clienttype_MultipleOpenProperty(this.ElementTypeName));
                        } 
 
                        openObjectPropertyName = ((OpenObjectAttribute)attributes[0]).OpenObjectPropertyName;
                        openObjectDeclared = tmp; 
                    }
                }
                #endregion
#endif 

                Type keyPropertyDeclaredType = null; 
                DataServiceKeyAttribute dska = type.GetCustomAttributes(true).OfType().FirstOrDefault(); 
                foreach (PropertyInfo pinfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
                { 
                    //// examples where class

                    //// the normal examples
                    //// PropertyType Property { get; set } 
                    //// Nullable Property { get; set; }
 
                    //// if 'Property: struct' then we would be unable set the property during construction (and have them stick) 
                    //// but when its a class, we can navigate if non-null and set the nested properties
                    //// PropertyType Property { get; } where PropertyType: class 

                    //// we do support adding elements to collections
                    //// ICollection { get; /*ignored set;*/ }
 
                    //// indexed properties are not suported because
                    //// we don't have anything to use as the index 
                    //// PropertyType Property[object x] { /*ignored get;*/ /*ignored set;*/ } 

                    //// also ignored 
                    //// if PropertyType.IsPointer (like byte*)
                    //// if PropertyType.IsArray except for byte[] and char[]
                    //// if PropertyType == IntPtr or UIntPtr
 
                    Type ptype = pinfo.PropertyType; // class / interface / value
                    ptype = Nullable.GetUnderlyingType(ptype) ?? ptype; 
 
                    if (ptype.IsPointer ||
                        (ptype.IsArray && (typeof(byte[]) != ptype) && typeof(char[]) != ptype) || 
                        (typeof(IntPtr) == ptype) ||
                        (typeof(UIntPtr) == ptype))
                    {
                        continue; 
                    }
 
                    Debug.Assert(!ptype.ContainsGenericParameters, "remove when test case is found that encounters this"); 

                    if (pinfo.CanRead && 
                        (!ptype.IsValueType || pinfo.CanWrite) &&
                        !ptype.ContainsGenericParameters &&
                        (0 == pinfo.GetIndexParameters().Length))
                    { 
                        #region IsKey?
                        bool keyProperty = dska != null ? dska.KeyNames.Contains(pinfo.Name) : false; 
 
                        if (keyProperty)
                        { 
                            if (null == keyPropertyDeclaredType)
                            {
                                keyPropertyDeclaredType = pinfo.DeclaringType;
                            } 
                            else if (keyPropertyDeclaredType != pinfo.DeclaringType)
                            { 
                                throw Error.InvalidOperation(Strings.ClientType_KeysOnDifferentDeclaredType(this.ElementTypeName)); 
                            }
 
                            if (!ClientConvert.IsKnownType(ptype))
                            {
                                throw Error.InvalidOperation(Strings.ClientType_KeysMustBeSimpleTypes(this.ElementTypeName));
                            } 

                            this.KeyCount++; 
                        } 
                        #endregion
 
#if ASTORIA_OPEN_OBJECT
                        #region IsOpenObjectProperty?
                        bool openProperty = (openObjectPropertyName == pinfo.Name) &&
                                              typeof(IDictionary).IsAssignableFrom(ptype); 
                        Debug.Assert(keyProperty != openProperty || (!keyProperty && !openProperty), "key can't be open type");
                        #endregion 
 
                        ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty, openProperty);
 
                        if (!property.OpenObjectProperty)
#else
                        ClientProperty property = new ClientProperty(pinfo, ptype, keyProperty);
#endif 
                        {
                            if (!this.properties.Add(property, ClientProperty.NameEquality)) 
                            { 
                                // 2nd property with same name shadows another property
                                int shadow = this.IndexOfProperty(property.PropertyName); 
                                if (!property.DeclaringType.IsAssignableFrom(this.properties[shadow].DeclaringType))
                                {   // the new property is on the most derived class
                                    this.properties.RemoveAt(shadow);
                                    this.properties.Add(property, null); 
                                }
                            } 
                        } 
#if ASTORIA_OPEN_OBJECT
                        else 
                        {
                            if (pinfo.DeclaringType == openObjectDeclared)
                            {
                                this.openProperties = property; 
                            }
                        } 
#endif 
                    }
                } 

                #region No KeyAttribute, discover key by name pattern { DeclaringType.Name+ID, ID }
                if (null == keyPropertyDeclaredType)
                { 
                    ClientProperty key = null;
                    for (int i = this.properties.Count - 1; 0 <= i; --i) 
                    { 
                        string propertyName = this.properties[i].PropertyName;
                        if (propertyName.EndsWith("ID", StringComparison.Ordinal)) 
                        {
                            string declaringTypeName = this.properties[i].DeclaringType.Name;
                            if ((propertyName.Length == (declaringTypeName.Length + 2)) &&
                                propertyName.StartsWith(declaringTypeName, StringComparison.Ordinal)) 
                            {
                                // matched "DeclaringType.Name+ID" pattern 
                                if ((null == keyPropertyDeclaredType) || 
                                    this.properties[i].DeclaringType.IsAssignableFrom(keyPropertyDeclaredType))
                                { 
                                    keyPropertyDeclaredType = this.properties[i].DeclaringType;
                                    key = this.properties[i];
                                }
                            } 
                            else if ((null == keyPropertyDeclaredType) && (2 == propertyName.Length))
                            { 
                                // matched "ID" pattern 
                                keyPropertyDeclaredType = this.properties[i].DeclaringType;
                                key = this.properties[i]; 
                            }
                        }
                    }
 
                    if (null != key)
                    { 
                        Debug.Assert(0 == this.KeyCount, "shouldn't have a key yet"); 
                        key.KeyProperty = true;
                        this.KeyCount++; 
                    }
                }
                else if (this.KeyCount != dska.KeyNames.Count)
                { 
                    var m = (from string a in dska.KeyNames
                             where null == (from b in this.properties 
                                            where b.PropertyName == a 
                                            select b).FirstOrDefault()
                             select a).First(); 
                    throw Error.InvalidOperation(Strings.ClientType_MissingProperty(this.ElementTypeName, m));
                }
                #endregion
 
                this.HasKeys = (null != keyPropertyDeclaredType);
                Debug.Assert(this.KeyCount == this.Properties.Where(k => k.KeyProperty).Count(), "KeyCount mismatch"); 
 
                this.WireUpMimeTypeProperties();
                this.CheckMediaLinkEntry(); 

                if (!skipSettableCheck)
                {
#if ASTORIA_OPEN_OBJECT 
                    if ((0 == this.properties.Count) && (null == this.openProperties))
#else 
                    if (0 == this.properties.Count) 
#endif
                    {   // implicit construction? 
                        throw Error.InvalidOperation(Strings.ClientType_NoSettableFields(this.ElementTypeName));
                    }
                }
            } 

            this.properties.Sort(ClientProperty.GetPropertyName, String.CompareOrdinal); 
 
#if ASTORIA_OPEN_OBJECT
            #region Validate OpenObjectAttribute was used 
            if ((null != openObjectPropertyName) && (null == this.openProperties))
            {
                throw Error.InvalidOperation(Strings.ClientType_MissingOpenProperty(this.ElementTypeName, openObjectPropertyName));
            } 

            Debug.Assert((null != openObjectPropertyName) == (null != this.openProperties), "OpenProperties mismatch"); 
            #endregion 
#endif
        } 

        /// Properties sorted by name
        internal ArraySet Properties
        { 
            get { return this.properties; }
        } 
 
        /// Property that holds data for ATOM-style media link entries
        internal ClientProperty MediaDataMember 
        {
            get { return this.mediaDataMember; }
        }
 
        /// 
        /// get a client type resolver 
        ///  
        /// type to wrap
        /// client type 
        internal static ClientType Create(Type type)
        {
            return Create(type, true /* expectModelType */);
        } 

        ///  
        /// get a client type resolver 
        /// 
        /// type to wrap 
        /// Whether the type is expected to be a model type.
        /// client type
        internal static ClientType Create(Type type, bool expectModelType)
        { 
            ClientType clientType;
            lock (ClientType.types) 
            { 
                ClientType.types.TryGetValue(type, out clientType);
            } 

            if (null == clientType)
            {
                bool skipSettableCheck = !expectModelType; 
                clientType = new ClientType(type, type.ToString(), skipSettableCheck); // ToString expands generic type name where as FullName does not
                if (expectModelType) 
                { 
                    lock (ClientType.types)
                    { 
                        ClientType existing;
                        if (ClientType.types.TryGetValue(type, out existing))
                        {
                            clientType = existing; 
                        }
                        else 
                        { 
                            ClientType.types.Add(type, clientType);
                        } 
                    }
                }
            }
 
            return clientType;
        } 
 
#if !ASTORIA_LIGHT
        ///  
        /// resolve the wireName/userType pair to a CLR type
        /// 
        /// type name sent by server
        /// type passed by user or on propertyType from a class 
        /// mapped clr type
        internal static Type ResolveFromName(string wireName, Type userType) 
#else 
        /// 
        /// resolve the wireName/userType pair to a CLR type 
        /// 
        /// type name sent by server
        /// type passed by user or on propertyType from a class
        /// typeof context for strongly typed assembly 
        /// mapped clr type
        internal static Type ResolveFromName(string wireName, Type userType, Type contextType) 
#endif 
        {
            Type foundType; 

            TypeName typename;
            typename.Type = userType;
            typename.Name = wireName; 

            lock (ClientType.namedTypes) 
            { 
                ClientType.namedTypes.TryGetValue(typename, out foundType);
            } 

            if (null == foundType)
            {
                string name = wireName; 
                int index = wireName.LastIndexOf('.');
                if ((0 <= index) && (index < wireName.Length - 1)) 
                { 
                    name = wireName.Substring(index + 1);
                } 

                if (userType.Name == name)
                {
                    foundType = userType; 
                }
                else 
                { 
#if !ASTORIA_LIGHT
                    // searching only loaded assemblies, not referenced assemblies 
                    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
#else
                    foreach (Assembly assembly in new Assembly[] { userType.Assembly, contextType.Assembly }.Distinct())
#endif 
                    {
                        Type found = assembly.GetType(wireName, false); 
                        ResolveSubclass(name, userType, found, ref foundType); 

                        if (null == found) 
                        {
                            Type[] types = null;
                            try
                            { 
                                types = assembly.GetTypes();
                            } 
                            catch (ReflectionTypeLoadException) 
                            {
                            } 

                            if (null != types)
                            {
                                foreach (Type t in types) 
                                {
                                    ResolveSubclass(name, userType, t, ref foundType); 
                                } 
                            }
                        } 
                    }
                }

                if (null != foundType) 
                {
                    lock (ClientType.namedTypes) 
                    { 
                        ClientType.namedTypes[typename] = foundType;
                    } 
                }
            }

            return foundType; 
        }
 
        ///  
        /// get concrete type that implements the genericTypeDefinitation
        ///  
        /// starting type
        /// the generic type definition to find
        /// concrete type that implementats the generic type
        internal static Type GetImplementationType(Type propertyType, Type genericTypeDefinition) 
        {
            if (IsConstructedGeneric(propertyType, genericTypeDefinition)) 
            {   // propertyType is ICollection 
                return propertyType;
            } 
            else
            {
                Type implementationType = null;
                foreach (Type interfaceType in propertyType.GetInterfaces()) 
                {
                    if (IsConstructedGeneric(interfaceType, genericTypeDefinition)) 
                    { 
                        if (null == implementationType)
                        {   // found implmentation of ICollection 
                            implementationType = interfaceType;
                        }
                        else
                        {   // ICollection and ICollection 
                            throw Error.NotSupported(Strings.ClientType_MultipleImplementationNotSupported);
                        } 
                    } 
                }
 
                return implementationType;
            }
        }
 
        /// 
        /// get element type, resolves ICollection and Nullable 
        ///  
        /// starting type
        /// the generic type definition to find 
        /// the method to search for
        /// the collection type if found
        /// element types
        internal static MethodInfo GetCollectionMethod(Type propertyType, Type genericTypeDefinition, string methodName, out Type type) 
        {
            Debug.Assert(null != propertyType, "null propertyType"); 
            Debug.Assert(null != genericTypeDefinition, "null genericTypeDefinition"); 
            Debug.Assert(genericTypeDefinition.IsGenericTypeDefinition, "!IsGenericTypeDefinition");
 
            type = null;

            Type implementationType = GetImplementationType(propertyType, genericTypeDefinition);
            if (null != implementationType) 
            {
                Type[] genericArguments = implementationType.GetGenericArguments(); 
                MethodInfo methodInfo = implementationType.GetMethod(methodName); 
                Debug.Assert(null != methodInfo, "should have found the method");
 
#if DEBUG
                Debug.Assert(null != genericArguments, "null genericArguments");
                ParameterInfo[] parameters = methodInfo.GetParameters();
                if (0 < parameters.Length) 
                {
                    // following assert was disabled for Contains which returns bool 
                    // Debug.Assert(typeof(void) == methodInfo.ReturnParameter.ParameterType, "method doesn't return void"); 

                    Debug.Assert(genericArguments.Length == parameters.Length, "genericArguments don't match parameters"); 
                    for (int i = 0; i < genericArguments.Length; ++i)
                    {
                        Debug.Assert(genericArguments[i] == parameters[i].ParameterType, "parameter doesn't match generic argument");
                    } 
                }
#endif 
                type = genericArguments[genericArguments.Length - 1]; 
                return methodInfo;
            } 

            return null;
        }
 
        /// 
        /// create object using default constructor 
        ///  
        /// instance of propertyType
        internal object CreateInstance() 
        {
            return Activator.CreateInstance(this.ElementType);
        }
 
        /// 
        /// get property wrapper for a property name, might be method around open types for otherwise unknown properties 
        ///  
        /// property name
        /// are missing properties ignored 
        /// property wrapper
        /// for unknown properties on closed types
        internal ClientProperty GetProperty(string propertyName, bool ignoreMissingProperties)
        { 
            int index = this.IndexOfProperty(propertyName);
            if (0 <= index) 
            { 
                return this.properties[index];
            } 
#if ASTORIA_OPEN_OBJECT
            else if (null != this.openProperties)
            {
                return this.openProperties; 
            }
#endif 
            else if (!ignoreMissingProperties) 
            {
                throw Error.InvalidOperation(Strings.ClientType_MissingProperty(this.ElementTypeName, propertyName)); 
            }

            return null;
        } 

        ///  
        /// Checks whether the specified  is a 
        /// closed constructed type of the generic type.
        ///  
        /// Type to check.
        /// Generic type for checkin.
        /// true if  is a constructed type of .
        /// The check is an immediate check; no inheritance rules are applied. 
        private static bool IsConstructedGeneric(Type type, Type genericTypeDefinition)
        { 
            Debug.Assert(type != null, "type != null"); 
            Debug.Assert(!type.ContainsGenericParameters, "remove when test case is found that encounters this");
            Debug.Assert(genericTypeDefinition != null, "genericTypeDefinition != null"); 

            return type.IsGenericType && (type.GetGenericTypeDefinition() == genericTypeDefinition) && !type.ContainsGenericParameters;
        }
 
        /// 
        /// is the type a visible subclass with correct name 
        ///  
        /// type name from server
        /// the type from user for materialization or property type 
        /// type being tested
        /// the previously discovered matching type
        /// if the mapping is ambiguous
        private static void ResolveSubclass(string wireClassName, Type userType, Type type, ref Type existing) 
        {
            if ((null != type) && type.IsVisible && (wireClassName == type.Name) && userType.IsAssignableFrom(type)) 
            { 
                if (null != existing)
                { 
                    throw Error.InvalidOperation(Strings.Deserialize_Current(existing, type));
                }

                existing = type; 
            }
        } 
 
        /// get the index of a property
        /// propertyName 
        /// index else -1
        private int IndexOfProperty(string propertyName)
        {
            return this.properties.IndexOf(propertyName, ClientProperty.GetPropertyName, String.Equals); 
        }
 
        ///  
        /// Find properties with dynamic MIME type related properties and
        /// set the references from each ClientProperty to its related MIME type property 
        /// 
        private void WireUpMimeTypeProperties()
        {
            MimeTypePropertyAttribute attribute = (MimeTypePropertyAttribute)this.ElementType.GetCustomAttributes(typeof(MimeTypePropertyAttribute), true).SingleOrDefault(); 
            if (null != attribute)
            { 
                int dataIndex, mimeTypeIndex; 
                if ((0 > (dataIndex = this.IndexOfProperty(attribute.DataPropertyName))) ||
                    (0 > (mimeTypeIndex = this.IndexOfProperty(attribute.MimeTypePropertyName)))) 
                {
                    throw Error.InvalidOperation(Strings.ClientType_MissingMimeTypeProperty(attribute.DataPropertyName, attribute.MimeTypePropertyName));
                }
 
                Debug.Assert(0 <= dataIndex, "missing data property");
                Debug.Assert(0 <= mimeTypeIndex, "missing mime type property"); 
                this.Properties[dataIndex].MimeTypeProperty = this.Properties[mimeTypeIndex]; 
            }
        } 

        /// 
        /// Check if this type represents an ATOM-style media link entry and
        /// if so mark the ClientType as such 
        /// 
        private void CheckMediaLinkEntry() 
        { 
            object[] attributes = this.ElementType.GetCustomAttributes(typeof(MediaEntryAttribute), true);
            if (attributes != null && attributes.Length > 0) 
            {
                Debug.Assert(attributes.Length == 1, "The AttributeUsage in the attribute definition should be presenting more than 1 per property");

                int index = this.IndexOfProperty(((MediaEntryAttribute)attributes[0]).MediaMemberName); 
                if (index < 0)
                { 
                    throw Error.InvalidOperation(Strings.ClientType_MissingMediaEntryProperty( 
                        ((MediaEntryAttribute)attributes[0]).MediaMemberName));
                } 

                this.mediaDataMember = this.properties[index];
            }
        } 

        /// type + wireName combination 
        private struct TypeName 
        {
            /// type 
            internal Type Type;

            /// type name from server
            internal string Name; 
        }
 
        ///  
        /// wrapper around property methods
        ///  
        [DebuggerDisplay("{PropertyName}")]
        internal sealed class ClientProperty
        {
            /// property name for debugging 
            internal readonly string PropertyName;
 
            /// type of the property 
            internal readonly Type NullablePropertyType;
 
            /// type of the property
            internal readonly Type PropertyType;

            /// what is the nested collection element 
            internal readonly Type CollectionType;
 
            ///  
            /// Is this a known primitive/reference type or an entity/complex/collection type?
            ///  
            internal readonly bool IsKnownType;

#if ASTORIA_OPEN_OBJECT
            /// IDictionary<string,object> OpenProperites { get; } 
            internal readonly bool OpenObjectProperty;
#endif 
 
            /// property getter
            private readonly MethodInfo propertyGetter; 

            /// property setter
            private readonly MethodInfo propertySetter;
 
            /// "set_Item" method supporting IDictionary properties
            private readonly MethodInfo setMethod; 
 
            /// "Add" method supporting ICollection<> properties
            private readonly MethodInfo addMethod; 

            /// "Remove" method supporting ICollection<> properties
            private readonly MethodInfo removeMethod;
 
            /// "Contains" method support ICollection<> properties
            private readonly MethodInfo containsMethod; 
 
            /// IsKeyProperty?
            private bool keyProperty; 

            /// The other property in this type that holds the MIME type for this one
            private ClientProperty mimeTypeProperty;
 
            /// 
            /// constructor 
            ///  
            /// property
            /// propertyType 
            /// keyProperty
#if ASTORIA_OPEN_OBJECT
            /// openObjectProperty
            internal ClientProperty(PropertyInfo property, Type propertyType, bool keyProperty, bool openObjectProperty) 
#else
            internal ClientProperty(PropertyInfo property, Type propertyType, bool keyProperty) 
#endif 
            {
                Debug.Assert(null != property, "null property"); 
                Debug.Assert(null != propertyType, "null propertyType");
                Debug.Assert(null == Nullable.GetUnderlyingType(propertyType), "should already have been denullified");

                this.PropertyName = property.Name; 
                this.NullablePropertyType = property.PropertyType;
                this.PropertyType = propertyType; 
                this.propertyGetter = property.GetGetMethod(); 
                this.propertySetter = property.GetSetMethod();
                this.keyProperty = keyProperty; 
#if ASTORIA_OPEN_OBJECT
                this.OpenObjectProperty = openObjectProperty;
#endif
 
                this.IsKnownType = ClientConvert.IsKnownType(propertyType);
                if (!this.IsKnownType) 
                { 
                    this.setMethod = GetCollectionMethod(this.PropertyType, typeof(IDictionary<,>), "set_Item", out this.CollectionType);
                    if (null == this.setMethod) 
                    {
                        this.containsMethod = GetCollectionMethod(this.PropertyType, typeof(ICollection<>), "Contains", out this.CollectionType);
                        this.addMethod = GetCollectionMethod(this.PropertyType, typeof(ICollection<>), "Add", out this.CollectionType);
                        this.removeMethod = GetCollectionMethod(this.PropertyType, typeof(ICollection<>), "Remove", out this.CollectionType); 
                    }
                } 
 
                Debug.Assert(!this.keyProperty || this.IsKnownType, "can't have an random type as key");
            } 

            /// what type was this property declared on?
            internal Type DeclaringType
            { 
                get { return this.propertyGetter.DeclaringType; }
            } 
 
            /// Does this property particpate in the primary key?
            internal bool KeyProperty 
            {
                get { return this.keyProperty; }
                set { this.keyProperty = value; }
            } 

            /// The other property in this type that holds the MIME type for this one 
            internal ClientProperty MimeTypeProperty 
            {
                get { return this.mimeTypeProperty; } 
                set { this.mimeTypeProperty = value; }
            }

            /// get KeyProperty 
            /// x
            /// KeyProperty 
            internal static bool GetKeyProperty(ClientProperty x) 
            {
                return x.KeyProperty; 
            }

            /// get property name
            /// x 
            /// PropertyName
            internal static string GetPropertyName(ClientProperty x) 
            { 
                return x.PropertyName;
            } 

            /// compare name equality
            /// x
            /// y 
            /// true if the property names are equal; false otherwise.
            internal static bool NameEquality(ClientProperty x, ClientProperty y) 
            { 
                return String.Equals(x.PropertyName, y.PropertyName);
            } 

            /// 
            /// get property value from an object
            ///  
            /// object to get the property value from
            /// property value 
            internal object GetValue(object instance) 
            {
                Debug.Assert(null != instance, "null instance"); 
                Debug.Assert(null != this.propertyGetter, "null propertyGetter");
                return this.propertyGetter.Invoke(instance, null);
            }
 
            /// 
            /// remove a item from collection 
            ///  
            /// collection
            /// item to remove 
            internal void RemoveValue(object instance, object value)
            {
                Debug.Assert(null != instance, "null instance");
                Debug.Assert(null != this.removeMethod, "missing removeMethod"); 

                Debug.Assert(this.PropertyType.IsAssignableFrom(instance.GetType()), "unexpected collection instance"); 
                Debug.Assert((null == value) || this.CollectionType.IsAssignableFrom(value.GetType()), "unexpected collection value to add"); 
                this.removeMethod.Invoke(instance, new object[] { value });
            } 

            /// 
            /// set property value on an object
            ///  
            /// object to set the property value on
            /// property value 
            /// used for open type 
            /// allow add to a collection if available, else allow setting collection property
#if ASTORIA_OPEN_OBJECT 
            /// cached OpenProperties dictionary
            internal void SetValue(object instance, object value, string propertyName, ref object openProperties, bool allowAdd)
#else
            internal void SetValue(object instance, object value, string propertyName, bool allowAdd) 
#endif
            { 
                Debug.Assert(null != instance, "null instance"); 
                if (null != this.setMethod)
                { 
#if ASTORIA_OPEN_OBJECT
                    if (this.OpenObjectProperty)
                    {
                        if (null == openProperties) 
                        {
                            if (null == (openProperties = this.propertyGetter.Invoke(instance, null))) 
                            { 
                                throw Error.NotSupported(Strings.ClientType_NullOpenProperties(this.PropertyName));
                            } 
                        }

                        ((IDictionary)openProperties)[propertyName] = value;
                    } 
                    else
#endif 
                    { 
                        Debug.Assert(this.PropertyType.IsAssignableFrom(instance.GetType()), "unexpected dictionary instance");
                        Debug.Assert((null == value) || this.CollectionType.IsAssignableFrom(value.GetType()), "unexpected dictionary value to set"); 

                        // ((IDictionary)instance)[propertyName] = (CollectionType)value;
                        this.setMethod.Invoke(instance, new object[] { propertyName, value });
                    } 
                }
                else if (allowAdd && (null != this.addMethod)) 
                { 
                    Debug.Assert(this.PropertyType.IsAssignableFrom(instance.GetType()), "unexpected collection instance");
                    Debug.Assert((null == value) || this.CollectionType.IsAssignableFrom(value.GetType()), "unexpected collection value to add"); 

                    // ((ICollection)instance).Add((CollectionType)value);
                    if (!(bool)this.containsMethod.Invoke(instance, new object[] { value }))
                    { 
                        this.addMethod.Invoke(instance, new object[] { value });
                    } 
                } 
                else if (null != this.propertySetter)
                { 
                    Debug.Assert((null == value) || this.PropertyType.IsAssignableFrom(value.GetType()), "unexpected property value to set");

                    // ((ElementType)instance).PropertyName = (PropertyType)value;
                    this.propertySetter.Invoke(instance, new object[] { value }); 
                }
                else 
                { 
                    throw Error.InvalidOperation(Strings.ClientType_MissingProperty(value.GetType().ToString(), propertyName));
                } 
            }
        }

        /// equality comparer for TypeName 
        private sealed class TypeNameEqualityComparer : IEqualityComparer
        { 
            /// equality comparer for TypeName 
            /// left type
            /// right type 
            /// true if x and y are equal
            public bool Equals(TypeName x, TypeName y)
            {
                return (x.Type == y.Type && x.Name == y.Name); 
            }
 
            /// compute hashcode for TypeName 
            /// object to compute hashcode for
            /// computed hashcode 
            public int GetHashCode(TypeName obj)
            {
                return obj.Type.GetHashCode() ^ obj.Name.GetHashCode();
            } 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK