public TypePropertyMetadata(PropertyDescriptor descriptor)
        {
            _propertyDescriptor = descriptor;
            ValidationRules = new List<TypePropertyValidationRuleMetadata>();

            var elementType = TypeUtility.GetElementType(descriptor.PropertyType);

            IsArray = !(elementType == descriptor.PropertyType);

            var propertyAttributes = descriptor.ExplicitAttributes();

            IsKey = null != propertyAttributes[typeof (KeyAttribute)];

            // TODO, 336102, ReadOnlyAttribute for editability?  RIA used EditableAttribute?
            IsReadOnly = propertyAttributes.OfType<ReadOnlyAttribute>().Any(a => a.IsReadOnly);

            var associationAttr = propertyAttributes.OfType<AssociationAttribute>().SingleOrDefault();
            if (associationAttr != null)
                Association = new TypePropertyAssociationMetadata(associationAttr);

            var requiredAttribute = propertyAttributes.OfType<RequiredAttribute>().SingleOrDefault();
            if (requiredAttribute != null)
                ValidationRules.Add(new TypePropertyValidationRuleMetadata(requiredAttribute));

            #region Validation Rules

            var rangeAttribute = (RangeAttribute) propertyAttributes[typeof (RangeAttribute)];
            if (rangeAttribute != null) {
                var operandType = rangeAttribute.OperandType;
                operandType = Nullable.GetUnderlyingType(operandType) ?? operandType;
                if (operandType == typeof (Double)
                    || operandType == typeof (Int16)
                    || operandType == typeof (Int32)
                    || operandType == typeof (Int64)
                    || operandType == typeof (Single))
                    ValidationRules.Add(new TypePropertyValidationRuleMetadata(rangeAttribute));
            }

            var stringLengthAttribute = (StringLengthAttribute) propertyAttributes[typeof (StringLengthAttribute)];
            if (stringLengthAttribute != null)
                ValidationRules.Add(new TypePropertyValidationRuleMetadata(stringLengthAttribute));

            var dataTypeAttribute = (DataTypeAttribute) propertyAttributes[typeof (DataTypeAttribute)];
            if (dataTypeAttribute != null) {
                if (dataTypeAttribute.DataType.Equals(DataType.EmailAddress)
                    || dataTypeAttribute.DataType.Equals(DataType.Url))
                    ValidationRules.Add(new TypePropertyValidationRuleMetadata(dataTypeAttribute));
            }

            #endregion
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="TypePropertyMetadata" /> class.
 /// </summary>
 /// <param name="descriptor">The descriptor.</param>
 public TypePropertyMetadata(PropertyDescriptor descriptor)
 {
     this.Name = descriptor.Name;
     Type elementType = TypeUtility.GetElementType(descriptor.PropertyType);
     this.IsArray = !elementType.Equals(descriptor.PropertyType);
     this.TypeName = elementType.Name;
     this.TypeNamespace = elementType.Namespace;
     AttributeCollection attributeCollection = descriptor.ExplicitAttributes();
     ReadOnlyAttribute readOnlyAttribute = (ReadOnlyAttribute)attributeCollection[typeof(ReadOnlyAttribute)];
     this.IsReadOnly = (readOnlyAttribute != null && readOnlyAttribute.IsReadOnly);
     AssociationAttribute associationAttribute = (AssociationAttribute)attributeCollection[typeof(AssociationAttribute)];
     if (associationAttribute != null)
     {
         this.Association = new MetadataGenerator.TypePropertyAssociationMetadata(associationAttribute);
     }
     RequiredAttribute requiredAttribute = (RequiredAttribute)attributeCollection[typeof(RequiredAttribute)];
     if (requiredAttribute != null)
     {
         this.validationRules.Add(new MetadataGenerator.TypePropertyValidationRuleMetadata(requiredAttribute));
     }
     RangeAttribute rangeAttribute = (RangeAttribute)attributeCollection[typeof(RangeAttribute)];
     if (rangeAttribute != null)
     {
         Type type = rangeAttribute.OperandType;
         type = (Nullable.GetUnderlyingType(type) ?? type);
         if (type.Equals(typeof(double)) || type.Equals(typeof(short)) || type.Equals(typeof(int)) || type.Equals(typeof(long)) || type.Equals(typeof(float)))
         {
             this.validationRules.Add(new MetadataGenerator.TypePropertyValidationRuleMetadata(rangeAttribute));
         }
     }
     StringLengthAttribute stringLengthAttribute = (StringLengthAttribute)attributeCollection[typeof(StringLengthAttribute)];
     if (stringLengthAttribute != null)
     {
         this.validationRules.Add(new MetadataGenerator.TypePropertyValidationRuleMetadata(stringLengthAttribute));
     }
     DataTypeAttribute dataTypeAttribute = (DataTypeAttribute)attributeCollection[typeof(DataTypeAttribute)];
     if (dataTypeAttribute != null && (dataTypeAttribute.DataType.Equals(DataType.EmailAddress) || dataTypeAttribute.DataType.Equals(DataType.Url)))
     {
         this.validationRules.Add(new MetadataGenerator.TypePropertyValidationRuleMetadata(dataTypeAttribute));
     }
 }
        internal override bool CanGenerateProperty(PropertyDescriptor propertyDescriptor)
        {
            // Check if it is an excluded property
            if (propertyDescriptor.Attributes[typeof(ExcludeAttribute)] != null)
            {
                return false;
            }

            // Check if it is an external reference.
            if (propertyDescriptor.Attributes[typeof(ExternalReferenceAttribute)] != null)
            {
                return true;
            }

            // The base can't generate the property, it could be an association which we know how to generate.
            if (!base.CanGenerateProperty(propertyDescriptor))
            {
                AttributeCollection propertyAttributes = propertyDescriptor.ExplicitAttributes();
                bool hasKeyAttr = (propertyAttributes[typeof(KeyAttribute)] != null);

                // If we can't generate Key property, log a VS error (this will cancel CodeGen effectively)
                if (hasKeyAttr)
                {
                    // Property must not be serializable based on attributes (e.g. no DataMember), because 
                    // we already checked its type which was fine.
                    this.ClientCodeGenerator.CodeGenerationHost.LogError(string.Format(
                        CultureInfo.CurrentCulture,
                        Resource.EntityCodeGen_EntityKey_PropertyNotSerializable,
                        this.Type, propertyDescriptor.Name));

                    return false;
                }

                // Get the implied element type (e.g. int32[], Nullable<int32>, IEnumerable<int32>)
                // If the ultimate element type is not allowed, it's not acceptable, no matter whether
                // this is an array, Nullable<T> or whatever
                Type elementType = TypeUtility.GetElementType(propertyDescriptor.PropertyType);
                if (!this._domainServiceDescriptionAggregate.EntityTypes.Contains(elementType) || (propertyDescriptor.Attributes[typeof(AssociationAttribute)] == null))
                {
                    // If the base class says we can't generate the property, it is because the property is not serializable.
                    // The only other type entity would serialize is associations. Since it is not, return now.
                    return false;
                }
            }

            // Ensure the property is not virtual, abstract or new
            // If there is a violation, we log the error and keep
            // running to accumulate all such errors.  This function
            // may return an "okay" for non-error case polymorphics.
            if (!this.CanGeneratePropertyIfPolymorphic(propertyDescriptor))
            {
                return false;
            }

            return true;
        }
        internal override void OnPropertySkipped(PropertyDescriptor pd)
        {
            AttributeCollection propertyAttributes = pd.ExplicitAttributes();
            bool hasKeyAttr = (propertyAttributes[typeof(KeyAttribute)] != null);

            // If we can't generate Key property, log a VS error (this will cancel CodeGen effectively)
            if (hasKeyAttr)
            {
                // Property must not be serializable based on attributes (e.g. no DataMember), because 
                // we already checked its type which was fine.
                this.ClientCodeGenerator.CodeGenerationHost.LogError(string.Format(
                    CultureInfo.CurrentCulture,
                    Resource.EntityCodeGen_EntityKey_PropertyNotSerializable,
                    this.Type, pd.Name));
            }
        }
        internal override bool IsPropertyShared(PropertyDescriptor pd)
        {
            if (base.IsPropertyShared(pd))
            {
                if (pd.ExplicitAttributes()[typeof(KeyAttribute)] != null)
                {
                    // If there are any shared key members, don't generate GetIdentity method. 
                    // Default implementation of GetIdentity on the Entity class will be used.
                    this.GenerateGetIdentity = false;
                }
                return true;
            }

            return false;
        }
        internal override bool HandleNonSerializableProperty(PropertyDescriptor propertyDescriptor)
        {
            AttributeCollection propertyAttributes = propertyDescriptor.ExplicitAttributes();
            AssociationAttribute associationAttr = (AssociationAttribute)propertyAttributes[typeof(AssociationAttribute)];
            bool externalReference = propertyAttributes[typeof(ExternalReferenceAttribute)] != null;

            if (associationAttr != null)
            {
                this.AddAssociationToGenerate(propertyDescriptor);
                return true;
            }

            return false;
        }
        internal override bool ShouldDeclareProperty(PropertyDescriptor pd)
        {
            if (!base.ShouldDeclareProperty(pd))
            {
                return false;
            }

            // Inheritance: when dealing with derived entities, we need to
            // avoid generating a property already on the base.  But we also
            // need to account for flattening (holes in the exposed hiearchy.
            // This helper method encapsulates that logic.
            if (!this.ShouldFlattenProperty(pd))
            {
                return false;
            }

            AttributeCollection propertyAttributes = pd.ExplicitAttributes();
            Type propertyType = pd.PropertyType;

            // The [Key] attribute means this property is part of entity key
            bool hasKeyAttr = (propertyAttributes[typeof(KeyAttribute)] != null);

            if (hasKeyAttr)
            {
                if (!TypeUtility.IsPredefinedSimpleType(propertyType))
                {
                    this.ClientCodeGenerator.CodeGenerationHost.LogError(string.Format(
                        CultureInfo.CurrentCulture,
                        Resource.EntityCodeGen_EntityKey_KeyTypeNotSupported,
                        this.Type, pd.Name, propertyType));
                    return false;
                }
            }

            return true;
        }
        internal virtual bool ShouldDeclareProperty(PropertyDescriptor pd)
        {
            AttributeCollection propertyAttributes = pd.ExplicitAttributes();

            if (this.IsExcluded(pd, propertyAttributes))
            {
                // Ignore the [Include] because that's what we do during serialization as well. (We don't want to 
                // check for [Exclude] + [Include] everywhere in our code base.)
                return false;
            }

            if (this.IsPropertyShared(pd))
            {
                return false;
            }

            return true;
        }
        internal IEnumerable<Attribute> GetPropertyAttributes(PropertyDescriptor propertyDescriptor, Type propertyType)
        {
            List<Attribute> propertyAttributes = propertyDescriptor.ExplicitAttributes().Cast<Attribute>().ToList();
            if (!propertyAttributes.OfType<DataMemberAttribute>().Any())
            {
                propertyAttributes.Add(new DataMemberAttribute());
            }

            ReadOnlyAttribute readOnlyAttr = propertyAttributes.OfType<ReadOnlyAttribute>().SingleOrDefault();
            if (readOnlyAttr != null && !propertyAttributes.OfType<EditableAttribute>().Any())
            {
                propertyAttributes.Add(new EditableAttribute(!readOnlyAttr.IsReadOnly));
            }

            if (TypeUtility.IsSupportedComplexType(propertyType) && !propertyAttributes.OfType<DisplayAttribute>().Any())
            {
                DisplayAttribute displayAttribute = new DisplayAttribute() { AutoGenerateField = false };
                propertyAttributes.Add(displayAttribute);
            }

            // If the data contract type already contains the RoundtripOriginalAttribute, then we remove the attribute from properties.
            if (this.Type.Attributes()[typeof(RoundtripOriginalAttribute)] != null)
            {
                propertyAttributes.RemoveAll(attr => attr.GetType() == typeof(RoundtripOriginalAttribute));
            }

            return propertyAttributes;
        }
        /// <summary>
        /// Generates a property getter/setter pair into the given proxy class to match the given property info.
        /// </summary>
        /// <param name="propertyDescriptor">PropertyDescriptor for the property to generate for.</param>
        protected virtual void GenerateProperty(PropertyDescriptor propertyDescriptor)
        {
            string propertyName = propertyDescriptor.Name;
            Type propertyType = CodeGenUtilities.TranslateType(propertyDescriptor.PropertyType);

            // ----------------------------------------------------------------
            // Property type ref
            // ----------------------------------------------------------------
            var propTypeReference = CodeGenUtilities.GetTypeReference(propertyType, this.ClientProxyGenerator, this.ProxyClass);

            // ----------------------------------------------------------------
            // Property decl
            // ----------------------------------------------------------------
            var property = new CodeMemberProperty();
            property.Name = propertyName;
            property.Type = propTypeReference;
            property.Attributes = MemberAttributes.Public | MemberAttributes.Final; // final needed, else becomes virtual
            List<Attribute> propertyAttributes = propertyDescriptor.ExplicitAttributes().Cast<Attribute>().ToList();

            // Generate <summary> for property
            string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_Property_Summary_Comment, propertyName);
            property.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp));

            // ----------------------------------------------------------------
            // [DataMember] -> Add if not already present.
            // ----------------------------------------------------------------
            // Add if not already present.

            if (!propertyAttributes.OfType<DataMemberAttribute>().Any())
            {
                CodeAttributeDeclaration dataMemberAtt = CodeGenUtilities.CreateAttributeDeclaration(typeof(DataMemberAttribute), this.ClientProxyGenerator, this.ProxyClass);
                property.CustomAttributes.Add(dataMemberAtt);
            }

            // Here, we check for the existence of [ReadOnly(true)] attributes generated when
            // the property does not not have a setter.  We want to inject an [Editable(false)]
            // attribute into the pipeline.
            ReadOnlyAttribute readOnlyAttr = propertyAttributes.OfType<ReadOnlyAttribute>().SingleOrDefault();
            if (readOnlyAttr != null && !propertyAttributes.OfType<EditableAttribute>().Any())
            {
                propertyAttributes.Add(new EditableAttribute(!readOnlyAttr.IsReadOnly));

                // REVIEW:  should we strip out [ReadOnly] attributes here?
            }

            // Here we check if the type has a RoundtripOriginalAttribute. In that case we strip it out from the property
            if (this._isRoundtripType)
            {
                propertyAttributes.RemoveAll(attr => attr.GetType() == typeof(RoundtripOriginalAttribute));
            }

            // Here we check for database generated fields. In that case we strip any RequiredAttribute from the property.
            if (propertyAttributes.Any(a=>a.GetType().Name == "DatabaseGeneratedAttribute"))
            {
                propertyAttributes.RemoveAll(attr => attr.GetType() == typeof (RequiredAttribute));
            }

            // Here, we check for the presence of a complex type. If it exists we need to add a DisplayAttribute
            // if not already there. DataSources windows do not handle complex types
            if (TypeUtility.IsSupportedComplexType(propertyType) && !propertyAttributes.OfType<DisplayAttribute>().Any())
            {
                CodeAttributeDeclaration displayAttribute = CodeGenUtilities.CreateDisplayAttributeDeclaration(this.ClientProxyGenerator, this.ProxyClass);
                property.CustomAttributes.Add(displayAttribute);
            }

            // ----------------------------------------------------------------
            // Propagate the custom attributes
            // ----------------------------------------------------------------

            CustomAttributeGenerator.GenerateCustomAttributes(
                this.ClientProxyGenerator,
                this.ProxyClass,
                ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, ex.Message, property.Name, this.ProxyClass.Name, ex.InnerException.Message),
                propertyAttributes.Cast<Attribute>(),
                property.CustomAttributes,
                property.Comments);

            // ----------------------------------------------------------------
            // backing private field (CodeDom doesn't yet know about auto properties)
            // ----------------------------------------------------------------
            string fieldName = CodeGenUtilities.MakeCompliantFieldName(propertyName);
            var field = new CodeMemberField(propTypeReference, fieldName);
            this.ProxyClass.Members.Add(field);
            var fieldRef = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName);
            var valueRef = new CodePropertySetValueReferenceExpression();

            // ----------------------------------------------------------------
            // getter body
            // ----------------------------------------------------------------
            property.GetStatements.Add(new CodeMethodReturnStatement(fieldRef));

            // ----------------------------------------------------------------
            // setter body
            // ----------------------------------------------------------------
            List<CodeStatement> bodyStatements = new List<CodeStatement>();

            // this.OnPropertyXxxChanging(PropType value);
            bodyStatements.Add(this.NotificationMethodGen.GetMethodInvokeExpressionStatementFor(propertyName + "Changing"));

            bool propertyIsReadOnly = this.IsPropertyReadOnly(propertyDescriptor);
            if (!propertyIsReadOnly)
            {
                bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaiseDataMemberChanging", new CodePrimitiveExpression(propertyDescriptor.Name))));
            }

            // Generate the validation tests.
            CodeStatement validationCode = GeneratePropertySetterValidation(propertyDescriptor.Name);
            bodyStatements.Add(validationCode);

            // this._field = value
            bodyStatements.Add(new CodeAssignStatement(fieldRef, valueRef));

            if (!propertyIsReadOnly)
            {
                bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaiseDataMemberChanged", new CodePrimitiveExpression(propertyDescriptor.Name))));
            }
            else
            {
                // even read-only members need to raise PropertyChanged
                bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaisePropertyChanged", new CodePrimitiveExpression(propertyDescriptor.Name))));
            }

            // this.OnPropertyXxxChanged();
            bodyStatements.Add(this.NotificationMethodGen.GetMethodInvokeExpressionStatementFor(propertyName + "Changed"));

            // if (this._field != value)...
            CodeExpression valueTest = CodeGenUtilities.MakeNotEqual(propertyType, fieldRef, valueRef, this.ClientProxyGenerator.IsCSharp);

            CodeConditionStatement body = new CodeConditionStatement(valueTest, bodyStatements.ToArray<CodeStatement>());

            property.SetStatements.Add(body);

            // add property
            this.ProxyClass.Members.Add(property);
        }
		public AssociationMetadata(PropertyDescriptor pd)
		{
			this.PropertyDescriptor = pd;
			AttributeCollection propertyAttributes = pd.ExplicitAttributes();
			this.AssociationAttribute = (AssociationAttribute) propertyAttributes[typeof (AssociationAttribute)];
			this.IsExternal = propertyAttributes[typeof (ExternalReferenceAttribute)] != null;
			this.IsCollection = EntityGenerator.IsCollectionType(pd.PropertyType);

			if (!this.IsCollection)
			{
				this.PropTypeName = CodeGenUtilities.GetTypeName(pd.PropertyType);
				this.AssociationTypeName = @"OpenRiaServices.DomainServices.Client.EntityRef<" + this.PropTypeName + ">";
				this.Attributes = propertyAttributes.Cast<Attribute>().Where(a => a.GetType() != typeof (DataMemberAttribute));
			}
			else
			{
				this.PropTypeName = CodeGenUtilities.GetTypeName(TypeUtility.GetElementType(pd.PropertyType));
				this.AssociationTypeName = "OpenRiaServices.DomainServices.Client.EntityCollection<" + this.PropTypeName + ">";

				List<Attribute> attributeList = propertyAttributes.Cast<Attribute>().ToList();
				ReadOnlyAttribute readOnlyAttr = propertyAttributes.OfType<ReadOnlyAttribute>().SingleOrDefault();
				if (readOnlyAttr != null && !propertyAttributes.OfType<EditableAttribute>().Any())
				{
					attributeList.Add(new EditableAttribute(!readOnlyAttr.IsReadOnly));
				}
				this.Attributes = attributeList.Where(a => a.GetType() != typeof (DataMemberAttribute));
			}

			this.PropertyName = CodeGenUtilities.GetSafeName(pd.Name);
			this.FieldName = CodeGenUtilities.MakeCompliantFieldName(this.PropertyName);
		}
        private void GenerateSingletonAssociation(CodeTypeDeclaration proxyClass, PropertyDescriptor pd, AssociationAttribute associationAttribute, bool isExternal)
        {
            CodeTypeReference propType =
                isExternal ?
                    new CodeTypeReference(pd.PropertyType.FullName) { Options = CodeTypeReferenceOptions.GlobalReference } :
                    CodeGenUtilities.GetTypeReference(pd.PropertyType, this.ClientProxyGenerator, proxyClass);

            // generate field:
            // private EntityRef<Product> _Product;
            CodeTypeReference fldType = CodeGenUtilities.GetTypeReference(TypeConstants.EntityRefTypeFullName, this.Type.Namespace, false);
            fldType.TypeArguments.Add(propType);

            CodeMemberField fld = new CodeMemberField();
            fld.Attributes = MemberAttributes.Private;
            fld.Name = CodeGenUtilities.MakeCompliantFieldName(pd.Name);
            fld.Type = fldType;
            proxyClass.Members.Add(fld);

            // generate property:
            // public Product Product { get {...} set {...} }
            CodeMemberProperty prop = new CodeMemberProperty();
            prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            prop.Name = pd.Name;
            prop.Type = propType;
            prop.HasGet = true;
            prop.HasSet = true;

            // For the FK side of external associations, we generate a "reduced" setter that
            // does validation and synchronizes FK members
            if (isExternal && !associationAttribute.IsForeignKey)
            {
                prop.HasSet = false;
            }

            // Generate <summary> comment for property
            string format = prop.HasSet
                            ? Resource.CodeGen_Entity_Singleton_Association_Property_Summary_Comment 
                            : Resource.CodeGen_Entity_Singleton_Association_ReadOnly_Property_Summary_Comment;
            string comment = string.Format(CultureInfo.CurrentCulture, format, pd.PropertyType.Name);
            prop.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp));

            // ----------------------------------------------------------------
            // Propagate the custom attributes (except DataMember)
            // ----------------------------------------------------------------
            AttributeCollection propertyAttributes = pd.ExplicitAttributes();
            CustomAttributeGenerator.GenerateCustomAttributes(
                this.ClientProxyGenerator,
                proxyClass,
                ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, ex.Message, prop.Name, proxyClass.Name, ex.InnerException.Message),
                propertyAttributes.Cast<Attribute>().Where(a => a.GetType() != typeof(DataMemberAttribute)),
                prop.CustomAttributes,
                prop.Comments);

            // --------------------------
            // Generate the filter method
            // --------------------------
            // private bool filter_Product(Product entity) {
            //     return entity.ProductID == ProductID;
            // }
            string[] thisKeyProps = associationAttribute.ThisKeyMembers.ToArray();
            string[] otherKeyProps = associationAttribute.OtherKeyMembers.ToArray();
            CodeMemberMethod filterMethod = this.GenerateFilterMethod(proxyClass, pd.Name, pd.PropertyType, thisKeyProps, otherKeyProps, isExternal);

            // --------------------------
            // Generate getter
            // --------------------------
            // generate delayed initialization
            // if (_Product == null) {
            //    _Product = new EntityRef<Product>(this, filter_Product);
            // }
            CodeExpression entityExpr = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fld.Name);
            entityExpr = new CodePropertyReferenceExpression(entityExpr, "Entity");
            CodeExpression isRefNullExpr =
                CodeGenUtilities.MakeEqualToNull(
                    new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fld.Name));

            CodeExpression filterDelegate =
                CodeGenUtilities.MakeDelegateCreateExpression(this.ClientProxyGenerator.IsCSharp, new CodeTypeReference("System.Func"), filterMethod.Name);

            CodeAssignStatement initExpr = new CodeAssignStatement(
                new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fld.Name),
                new CodeObjectCreateExpression(
                    fldType,
                    new CodeThisReferenceExpression(),
                    new CodePrimitiveExpression(prop.Name),
                    filterDelegate));
            prop.GetStatements.Add(new CodeConditionStatement(isRefNullExpr, initExpr));

            // generate : return _Product.Entity;
            prop.GetStatements.Add(new CodeMethodReturnStatement(entityExpr));

            // --------------------------
            // Generate setter
            // --------------------------
            if (prop.HasSet)
            {
                PropertyDescriptor reverseAssociationMember = GetReverseAssociation(pd, associationAttribute);

                CodeStatement detachStatement = null;
                CodeStatement attachStatement = null;
                bool reverseIsSingleton = false;

                // we need to emit back-reference fixup code if this association is bi-directional, and the other side 
                // of the association will also be generated (don't want to generate code that references non-existent
                // members)
                bool isBiDirectionalAssociation = (reverseAssociationMember != null) && this.CanGenerateProperty(reverseAssociationMember);

                if (!isExternal && isBiDirectionalAssociation)
                {
                    // currently relying on our naming convention for association names to get the name
                    // of the reverse collection property
                    string revName = reverseAssociationMember.Name;

                    reverseIsSingleton = !IsCollectionType(reverseAssociationMember.PropertyType);
                    if (!reverseIsSingleton)
                    {
                        detachStatement = new CodeExpressionStatement(
                            new CodeMethodInvokeExpression(
                                new CodePropertyReferenceExpression(
                                    new CodeVariableReferenceExpression("previous"),
                                    revName),
                                "Remove",
                                new CodeThisReferenceExpression()));
                        attachStatement = new CodeExpressionStatement(
                            new CodeMethodInvokeExpression(
                                new CodePropertyReferenceExpression(
                                    new CodePropertySetValueReferenceExpression(),
                                    revName),
                                "Add",
                                new CodeThisReferenceExpression()));
                    }
                    else
                    {
                        detachStatement = new CodeAssignStatement(
                            new CodePropertyReferenceExpression(
                                new CodeVariableReferenceExpression("previous"),
                                revName),
                                new CodePrimitiveExpression(null));
                        attachStatement = new CodeAssignStatement(
                            new CodePropertyReferenceExpression(
                                new CodePropertySetValueReferenceExpression(),
                                revName),
                                new CodeThisReferenceExpression());
                    }
                }

                // code to sync FK values from the new property value
                List<CodeStatement> statements1 = null;
                List<CodeStatement> statements2 = null;
                statements1 = new List<CodeStatement>();
                if (associationAttribute.IsForeignKey)
                {
                    // only emit FK sync code if this is a foreign key association
                    for (int i = 0; i < thisKeyProps.Length; i++)
                    {
                        statements1.Add(
                            new CodeAssignStatement(
                                new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), thisKeyProps[i]),
                                new CodePropertyReferenceExpression(new CodePropertySetValueReferenceExpression(), otherKeyProps[i])));
                    }

                    // code to set FK values to default values if the new property value is null
                    statements2 = new List<CodeStatement>();
                    for (int i = 0; i < thisKeyProps.Length; i++)
                    {
                        Type foreignKeyType = TypeDescriptor.GetProperties(this.Type).Find(thisKeyProps[i], false).PropertyType;
                        statements2.Add(
                            new CodeAssignStatement(
                                new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), thisKeyProps[i]),
                                new CodeDefaultValueExpression(
                                    CodeGenUtilities.GetTypeReference(foreignKeyType, this.ClientProxyGenerator, proxyClass))));
                    }
                }

                // if(previous != value)
                CodeExpression prevValueExpr = CodeGenUtilities.MakeNotEqual(null, new CodeVariableReferenceExpression("previous"), new CodePropertySetValueReferenceExpression(), this.ClientProxyGenerator.IsCSharp);

                // Product previous = Product;
                prop.SetStatements.Add(new CodeVariableDeclarationStatement(prop.Type, "previous", new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), pd.Name)));

                List<CodeStatement> stmts = new List<CodeStatement>();

                // Generate the validation test
                CodeStatement validationCode = GeneratePropertySetterValidation(prop.Name);
                stmts.Add(validationCode);

                if (!isExternal && isBiDirectionalAssociation)
                {
                    List<CodeStatement> detachStmts = new List<CodeStatement>();

                    // Generate : this._Product.Entity = null;
                    // This prevents infinite recursion in 1:1 association case,
                    // and in general ensures that change notifications don't get
                    // raised for the ref and fk properties during the transition
                    // time when the ref is set to null (detached) before setting
                    // to the new value.
                    detachStmts.Add(new CodeAssignStatement(entityExpr, new CodePrimitiveExpression(null)));

                    // previous.PurchaseOrderDetails.Remove(this);
                    detachStmts.Add(detachStatement);

                    // if (v != null) {
                    //    . . .
                    // }
                    stmts.Add(new CodeConditionStatement(CodeGenUtilities.MakeNotEqualToNull(new CodeVariableReferenceExpression("previous")), detachStmts.ToArray()));
                }

                // this._Product.Entity = value
                CodeAssignStatement setEntityStmt = new CodeAssignStatement(
                    entityExpr, new CodePropertySetValueReferenceExpression());

                // if (value != null)
                CodeConditionStatement stmt;
                if (associationAttribute.IsForeignKey)
                {
                    stmt = new CodeConditionStatement(
                    CodeGenUtilities.MakeNotEqualToNull(
                        new CodePropertySetValueReferenceExpression()),
                        statements1.ToArray(),
                        statements2.ToArray());

                    stmts.Add(stmt);

                    if (!isExternal)
                    {
                        // for FK sides of an association, we must set the entity
                        // reference AFTER FK member sync
                        stmts.Add(setEntityStmt);

                        // add the attach statement for bidirectional associations
                        if (isBiDirectionalAssociation)
                        {
                            stmts.Add(new CodeConditionStatement(
                                CodeGenUtilities.MakeNotEqualToNull(new CodePropertySetValueReferenceExpression()), attachStatement));
                        }
                    }
                }
                else
                {
                    stmts.Add(setEntityStmt);

                    // add the attach statement for bidirectional associations
                    if (isBiDirectionalAssociation)
                    {
                        stmts.Add(new CodeConditionStatement(
                            CodeGenUtilities.MakeNotEqualToNull(new CodePropertySetValueReferenceExpression()), attachStatement));
                    }

                    stmt = new CodeConditionStatement(
                        CodeGenUtilities.MakeNotEqualToNull(
                        new CodePropertySetValueReferenceExpression()),
                        statements1.ToArray());
                }

                if (!isExternal)
                {
                    // Generate : this.RaisePropertyChanged(<propName>);
                    stmts.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                        new CodeThisReferenceExpression(), "RaisePropertyChanged",
                        new CodePrimitiveExpression(prop.Name))));
                }

                prop.SetStatements.Add(new CodeConditionStatement(prevValueExpr, stmts.ToArray()));
            }

            proxyClass.Members.Add(prop);
            proxyClass.Members.Add(filterMethod);
        }
        private void GenerateCollectionSideAssociation(CodeTypeDeclaration proxyClass, PropertyDescriptor pd, AssociationAttribute associationAttribute, bool isExternal)
        {
            CodeTypeReference propType = CodeGenUtilities.GetTypeReference(TypeConstants.EntityCollectionTypeFullName, this.Type.Namespace, false);
            CodeTypeReference fldType;
            Type elementType = TypeUtility.GetElementType(pd.PropertyType);

            if (isExternal)
            {
                CodeTypeReference externalTypeRef = new CodeTypeReference(elementType.FullName);
                propType.TypeArguments.Add(externalTypeRef);

                // Note: we set the global flag *after* adding as a TypeArgument
                //       to override the GenericTypeArgument flag.  This is required
                //       for proper global references.
                externalTypeRef.Options = CodeTypeReferenceOptions.GlobalReference;
            }
            else
            {
                propType.TypeArguments.Add(
                    CodeGenUtilities.GetTypeReference(
                        elementType,
                        this.ClientProxyGenerator,
                        proxyClass));
            }

            fldType = propType;

            CodeMemberField fld = new CodeMemberField();
            fld.Attributes = MemberAttributes.Private;
            fld.Name = CodeGenUtilities.MakeCompliantFieldName(pd.Name);
            fld.Type = fldType;
            proxyClass.Members.Add(fld);

            // --------------------------
            // Generate the filter method
            // --------------------------
            // private bool filter_PurchaseOrderDetails(PurchaseOrderDetail entity) {
            //     return entity.ProductID == ProductID;
            // }
            string[] thisKeyProps = associationAttribute.ThisKeyMembers.ToArray();
            string[] otherKeyProps = associationAttribute.OtherKeyMembers.ToArray();
            CodeMemberMethod filterMethod = this.GenerateFilterMethod(proxyClass, pd.Name, elementType, thisKeyProps, otherKeyProps, isExternal);

            CodeMemberProperty prop = new CodeMemberProperty();
            prop.Name = pd.Name;
            prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            prop.Type = propType;
            prop.HasGet = true;

            // Generate <summary> comment for property
            string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_Collection_Association_Property_Summary_Comment, elementType.Name);
            prop.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp));

            // ----------------------------------------------------------------
            // Propagate the custom attributes (except DataMember)
            // ----------------------------------------------------------------
            List<Attribute> propertyAttributes = pd.ExplicitAttributes().Cast<Attribute>().ToList();

            // Here, we check for the existence of [ReadOnly(true)] attributes generated when
            // the property does not not have a setter.  We want to inject an [Editable(false)]
            // attribute into the pipeline.
            ReadOnlyAttribute readOnlyAttr = propertyAttributes.OfType<ReadOnlyAttribute>().SingleOrDefault();
            if (readOnlyAttr != null && !propertyAttributes.OfType<EditableAttribute>().Any())
            {
                propertyAttributes.Add(new EditableAttribute(!readOnlyAttr.IsReadOnly));

                // REVIEW:  should we strip out [ReadOnly] attributes here?
            }

            CustomAttributeGenerator.GenerateCustomAttributes(
                this.ClientProxyGenerator,
                proxyClass,
                ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, ex.Message, prop.Name, proxyClass.Name, ex.InnerException.Message),
                propertyAttributes.Where(a => a.GetType() != typeof(DataMemberAttribute)),
                prop.CustomAttributes,
                prop.Comments);

            // Generate "if (fld == null)" test for common use below
            CodeExpression isRefNullExpr =
                CodeGenUtilities.MakeEqualToNull(
                    new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fld.Name));

            // Generate a delegate style invoke of our filter method for common use below
            CodeExpression filterDelegate =
                CodeGenUtilities.MakeDelegateCreateExpression(this.ClientProxyGenerator.IsCSharp, new CodeTypeReference("System.Func"), filterMethod.Name);

            // only generate attach and detach when the relation is two-way
            PropertyDescriptor reverseAssociationMember = GetReverseAssociation(pd, associationAttribute);
            bool isBiDirectionalAssociation = (reverseAssociationMember != null) && this.CanGenerateProperty(reverseAssociationMember);
            if (isBiDirectionalAssociation)
            {
                if (IsCollectionType(pd.PropertyType))
                {
                    CodeMemberMethod attach = this.GenerateAttach(proxyClass, elementType, associationAttribute, pd);
                    CodeMemberMethod detach = this.GenerateDetach(proxyClass, elementType, associationAttribute, pd);

                    //// generate : 
                    //// if (_PurchaseOrderDetails == null) {
                    ////    _PurchaseOrderDetails = new EntityCollection<PurchaseOrderDetail>(this, filter_PurchaseOrderDetails, attach_PurchaseOrderDetails, detach_PurchaseOrderDetails);
                    //// }

                    CodeExpression attachDelegate =
                        CodeGenUtilities.MakeDelegateCreateExpression(this.ClientProxyGenerator.IsCSharp, new CodeTypeReference("System.Action"), attach.Name);

                    CodeExpression detachDelegate =
                        CodeGenUtilities.MakeDelegateCreateExpression(this.ClientProxyGenerator.IsCSharp, new CodeTypeReference("System.Action"), detach.Name);


                    CodeAssignStatement initExpr = new CodeAssignStatement(
                        new CodeFieldReferenceExpression(
                            new CodeThisReferenceExpression(), fld.Name),
                            new CodeObjectCreateExpression(
                                propType,
                                new CodeThisReferenceExpression(),
                                new CodePrimitiveExpression(prop.Name),
                                filterDelegate,
                                attachDelegate,
                                detachDelegate));
                    prop.GetStatements.Add(new CodeConditionStatement(isRefNullExpr, initExpr));
                }
            }
            else
            {
                CodeAssignStatement initExpr = new CodeAssignStatement(
                        new CodeFieldReferenceExpression(
                            new CodeThisReferenceExpression(), fld.Name),
                            new CodeObjectCreateExpression(
                                propType,
                                new CodeThisReferenceExpression(),
                                new CodePrimitiveExpression(prop.Name),
                                filterDelegate));
                prop.GetStatements.Add(new CodeConditionStatement(isRefNullExpr, initExpr));
            }

            prop.GetStatements.Add(
                new CodeMethodReturnStatement(
                    new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fld.Name)));

            proxyClass.Members.Add(prop);
            proxyClass.Members.Add(filterMethod);
        }
        protected override void GenerateProperty(PropertyDescriptor propertyDescriptor)
        {
            base.GenerateProperty(propertyDescriptor);

            AttributeCollection propertyAttributes = propertyDescriptor.ExplicitAttributes();
            bool hasKeyAttr = (propertyAttributes[typeof(KeyAttribute)] != null);
            if (hasKeyAttr)
            {
                this._keyProperties.Add(propertyDescriptor);
            }
        }
        protected override bool GenerateNonSerializableProperty(PropertyDescriptor propertyDescriptor)
        {
            AttributeCollection propertyAttributes = propertyDescriptor.ExplicitAttributes();
            AssociationAttribute associationAttr = (AssociationAttribute)propertyAttributes[typeof(AssociationAttribute)];
            bool externalReference = propertyAttributes[typeof(ExternalReferenceAttribute)] != null;

            if (associationAttr != null)
            {
                // generate Association members for members marked Association and of a Type that is exposed by the provider
                this.GenEntityAssocationProperty(this.ProxyClass, propertyDescriptor, associationAttr, externalReference);
                return true;
            }

            return false;
        }