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; }