Esempio n. 1
0
        public static void Constructor(string name, string thisKey, string otherKey, string[] thisKeyMembers, string[] otherKeyMembers)
        {
            var attribute = new AssociationAttribute(name, thisKey, otherKey);

            Assert.Equal(name, attribute.Name);
            Assert.Equal(thisKey, attribute.ThisKey);
            Assert.Equal(otherKey, attribute.OtherKey);

            if (thisKey == null)
            {
                Assert.Throws <NullReferenceException>(() => attribute.ThisKeyMembers);
            }
            else
            {
                Assert.Equal(thisKeyMembers, attribute.ThisKeyMembers);
            }
            if (otherKey == null)
            {
                Assert.Throws <NullReferenceException>(() => attribute.OtherKeyMembers);
            }
            else
            {
                Assert.Equal(otherKeyMembers, attribute.OtherKeyMembers);
            }
        }
 public static void Can_construct_attribute_and_get_whitespace_values()
 {
     var attribute =
         new AssociationAttribute(null, string.Empty, " \t\r\n");
     Assert.Equal(null, attribute.Name);
     Assert.Equal(string.Empty, attribute.ThisKey);
     Assert.Equal(" \t\r\n", attribute.OtherKey);
 }
 public static void Can_construct_attribute_and_get_values()
 {
     var attribute =
         new AssociationAttribute("TestName", "TestThisKey", "TestOtherKey");
     Assert.Equal("TestName", attribute.Name);
     Assert.Equal("TestThisKey", attribute.ThisKey);
     Assert.Equal("TestOtherKey", attribute.OtherKey);
 }
 public static void Can_get_and_set_IsForeignKey()
 {
     var attribute = new AssociationAttribute("Name", "ThisKey", "OtherKey");
     Assert.Equal(false, attribute.IsForeignKey);
     attribute.IsForeignKey = true;
     Assert.Equal(true, attribute.IsForeignKey);
     attribute.IsForeignKey = false;
     Assert.Equal(false, attribute.IsForeignKey);
 }
 public static void Can_get_ThisKeyMembers_and_OtherKeyMembers()
 {
     var listOfThisKeys = new List<string>() { "ThisKey1", "ThisKey2", "ThisKey3" };
     var listOfOtherKeys = new List<string>() { "OtherKey1", "OtherKey2" };
     // doesn't matter how many spaces are between keys, but they must be separated by a comma
     var attribute = new AssociationAttribute("Name", "ThisKey1,  ThisKey2, ThisKey3", "OtherKey1,  OtherKey2");
     Assert.True(listOfThisKeys.SequenceEqual(attribute.ThisKeyMembers));
     Assert.True(listOfOtherKeys.SequenceEqual(attribute.OtherKeyMembers));
 }
Esempio n. 6
0
        public static void Can_construct_attribute_and_get_whitespace_values()
        {
            var attribute =
                new AssociationAttribute(null, string.Empty, " \t\r\n");

            Assert.Equal(null, attribute.Name);
            Assert.Equal(string.Empty, attribute.ThisKey);
            Assert.Equal(" \t\r\n", attribute.OtherKey);
        }
Esempio n. 7
0
        public static void Can_construct_attribute_and_get_values()
        {
            var attribute =
                new AssociationAttribute("TestName", "TestThisKey", "TestOtherKey");

            Assert.Equal("TestName", attribute.Name);
            Assert.Equal("TestThisKey", attribute.ThisKey);
            Assert.Equal("TestOtherKey", attribute.OtherKey);
        }
        private void AddAttachMethod(Type linkTableType, AssociationAttribute assocAttrThisEnd, AssociationAttribute assocAttrOtherEnd, PropertyDescriptor prop1, PropertyDescriptor prop2)
        {
            var linkTableFullTypeName = linkTableType.FullName;
            var navProp1TypeFullName = prop1.PropertyType.FullName;
            var navProp2TypeName = prop2.PropertyType.Name;
            var navProp2TypeFullName = prop2.PropertyType.FullName;
            var navProp1NameLower = ToLowerInitial(prop1.Name);
            var navProp2NameLower = ToLowerInitial(prop2.Name);
            var thisKeyMembers = assocAttrThisEnd.ThisKeyMembers.ToList();
            var otherKeyMembers = assocAttrThisEnd.OtherKeyMembers.ToList();

            WriteLine(@"#region Lines added by m2m4ria code generator");
            WriteLine(@"/// <summary>");
            WriteLine(
                @"/// This method attaches {0} and {1} to the current link table entity, in such a way",
                navProp1TypeFullName,
                navProp2TypeFullName);
            WriteLine(@"/// that both navigation properties are set before an INotifyCollectionChanged event is fired.");
            WriteLine(@"/// </summary>");
            WriteLine(@"/// <param name=""r""/>");
            WriteLine(@"/// <param name=""{0}""/>", navProp1NameLower);
            WriteLine(@"/// <param name=""{0}""/>", navProp2NameLower);
            WriteLine(
                @"[System.ObsoleteAttribute(""This property is only intended for use by the M2M4Ria solution."")]");
            WriteLine(
                @"public static void Attach{0}To{1}({2} r, {3} {4}, {5} {6})",
                navProp2TypeName,
                assocAttrOtherEnd.Name,
                linkTableFullTypeName,
                navProp1TypeFullName,
                navProp1NameLower,
                navProp2TypeFullName,
                navProp2NameLower);
            WriteLine(@"{");
            WriteLine(@"    var dummy = r.{0}; // this is to instantiate the EntityRef<{0}>", prop2.Name);
            WriteLine(@"    r._{0}.Entity = {0};", navProp2NameLower);

            for(var i = 0; i < thisKeyMembers.Count(); i++)
            {
                var thisKeyLower = ToLowerInitial(thisKeyMembers[i]);
                WriteLine(@"    r._{0} = {1}.{2};", thisKeyLower, navProp2NameLower, otherKeyMembers[i]);
            }
            WriteLine(@"    r.{0} = {1};", prop1.Name, navProp1NameLower);
            WriteLine(@"    r._{0}.Entity = null;", navProp2NameLower);

            for(var i = 0; i < thisKeyMembers.Count(); i++)
            {
                var thisKeyLower = ToLowerInitial(thisKeyMembers[i]);
                var keyType = linkTableType.GetProperty(thisKeyMembers[i]).PropertyType;
                WriteLine(@"    r._{0} = default({1});", thisKeyLower, keyType.FullName);
            }

            WriteLine(@"    r.{0} = {1};", prop2.Name, navProp2NameLower);
            WriteLine(@"}");
            WriteLine(@"#endregion");
        }
Esempio n. 9
0
        public static void Can_get_and_set_IsForeignKey()
        {
            var attribute = new AssociationAttribute("Name", "ThisKey", "OtherKey");

            Assert.Equal(false, attribute.IsForeignKey);
            attribute.IsForeignKey = true;
            Assert.Equal(true, attribute.IsForeignKey);
            attribute.IsForeignKey = false;
            Assert.Equal(false, attribute.IsForeignKey);
        }
 private static string GetLinkTableOtherNavigationPropertyName(
     AssociationAttribute association, Type linkTableType)
 {
     var properties =
         from p in TypeDescriptor.GetProperties(linkTableType).OfType<PropertyDescriptor>()
         let x = p.Attributes[typeof(AssociationAttribute)] as AssociationAttribute
         where x != null
         where x.Name != association.Name
         select p;
     return properties.Single().Name;
 }
        public static void IsForeignKey_GetSet_ReturnsExpected()
        {
            var attribute = new AssociationAttribute("Name", "ThisKey", "OtherKey");
            Assert.False(attribute.IsForeignKey);

            attribute.IsForeignKey = true;
            Assert.True(attribute.IsForeignKey);

            attribute.IsForeignKey = false;
            Assert.False(attribute.IsForeignKey);
        }
Esempio n. 12
0
        public static void IsForeignKey_GetSet_ReturnsExpected()
        {
            var attribute = new AssociationAttribute("Name", "ThisKey", "OtherKey");

            Assert.False(attribute.IsForeignKey);

            attribute.IsForeignKey = true;
            Assert.True(attribute.IsForeignKey);

            attribute.IsForeignKey = false;
            Assert.False(attribute.IsForeignKey);
        }
        public void TestAssociationAttribute()
        {
            AssociationAttribute attr = new AssociationAttribute("name", "thisKey", "otherKey");
            attr.IsForeignKey = false;

            Assert.AreEqual("name", attr.Name);
            Assert.AreEqual("thisKey", attr.ThisKey);
            Assert.AreEqual("otherKey", attr.OtherKey);
            Assert.AreEqual(false, attr.IsForeignKey);

            // Verify can reverse polarity of foreign key
            attr.IsForeignKey = true;
            Assert.AreEqual(true, attr.IsForeignKey);

        }
Esempio n. 14
0
        public static void Can_get_ThisKeyMembers_and_OtherKeyMembers()
        {
            var listOfThisKeys = new List <string>()
            {
                "ThisKey1", "ThisKey2", "ThisKey3"
            };
            var listOfOtherKeys = new List <string>()
            {
                "OtherKey1", "OtherKey2"
            };
            // doesn't matter how many spaces are between keys, but they must be separated by a comma
            var attribute = new AssociationAttribute("Name", "ThisKey1,  ThisKey2, ThisKey3", "OtherKey1,  OtherKey2");

            Assert.True(listOfThisKeys.SequenceEqual(attribute.ThisKeyMembers));
            Assert.True(listOfOtherKeys.SequenceEqual(attribute.OtherKeyMembers));
        }
        public static void Constructor(string name, string thisKey, string otherKey, string[] thisKeyMembers, string[] otherKeyMembers)
        {
            var attribute = new AssociationAttribute(name, thisKey, otherKey);
            Assert.Equal(name, attribute.Name);
            Assert.Equal(thisKey, attribute.ThisKey);
            Assert.Equal(otherKey, attribute.OtherKey);

            if (thisKey == null)
            {
                Assert.Throws<NullReferenceException>(() => attribute.ThisKeyMembers);
            }
            else
            {
                Assert.Equal(thisKeyMembers, attribute.ThisKeyMembers);
            }
            if (otherKey == null)
            {
                Assert.Throws<NullReferenceException>(() => attribute.OtherKeyMembers);
            }
            else
            {
                Assert.Equal(otherKeyMembers, attribute.OtherKeyMembers);
            }
        }
        /// <summary>
        /// Returns a collection of all the <see cref="Attribute"/>s we infer from the metadata associated
        /// with the metadata member corresponding to the given property descriptor
        /// </summary>
        /// <param name="pd">A <see cref="PropertyDescriptor"/>.</param>
        /// <returns>A collection of attributes inferred from metadata in the given descriptor.</returns>
        protected override IEnumerable <Attribute> GetMemberAttributes(PropertyDescriptor pd)
        {
            List <Attribute> attributes = new List <Attribute>();
            MetaDataMember   member     = this._metaType.DataMembers.Where(p => p.Name == pd.Name).SingleOrDefault();

            if (member != null)
            {
                EditableAttribute editableAttribute = null;
                bool hasKeyAttribute = (pd.Attributes[typeof(KeyAttribute)] != null);
                if (member.IsPrimaryKey && !hasKeyAttribute)
                {
                    attributes.Add(new KeyAttribute());
                    hasKeyAttribute = true;
                }

                // Check if the member is DB generated and add the DatabaseGeneratedAttribute to it if not already present.
                if (member.IsDbGenerated && pd.Attributes[typeof(DatabaseGeneratedAttribute)] == null)
                {
                    if (member.AutoSync == AutoSync.OnInsert)
                    {
                        attributes.Add(new DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity));
                    }
                    else if (member.AutoSync == AutoSync.Always)
                    {
                        attributes.Add(new DatabaseGeneratedAttribute(DatabaseGeneratedOption.Computed));
                    }
                }

                if (hasKeyAttribute && !this._keyIsEditable)
                {
                    editableAttribute = new EditableAttribute(false)
                    {
                        AllowInitialValue = true
                    };
                }

                if (member.IsAssociation &&
                    pd.Attributes[typeof(System.ComponentModel.DataAnnotations.AssociationAttribute)] == null)
                {
                    System.ComponentModel.DataAnnotations.AssociationAttribute assocAttrib = this.TypeDescriptionContext.CreateAssociationAttribute(member);
                    attributes.Add(assocAttrib);
                }

                // Add Required attribute to metdata if the member cannot be null and it is either a reference type or a Nullable<T>
                bool isStringType = pd.PropertyType == typeof(string) || pd.PropertyType == typeof(char[]);
                if (!member.CanBeNull && (!pd.PropertyType.IsValueType || IsNullableType(pd.PropertyType)) &&
                    pd.Attributes[typeof(RequiredAttribute)] == null)
                {
                    attributes.Add(new RequiredAttribute());
                }

                // Add implicit ConcurrencyCheck attribute to metadata if UpdateCheck is anything other than UpdateCheck.Never
                if (member.UpdateCheck != UpdateCheck.Never &&
                    pd.Attributes[typeof(ConcurrencyCheckAttribute)] == null)
                {
                    attributes.Add(new ConcurrencyCheckAttribute());
                }

                bool hasTimestampAttribute = (pd.Attributes[typeof(TimestampAttribute)] != null);
                if (member.IsVersion && !hasTimestampAttribute)
                {
                    attributes.Add(new TimestampAttribute());
                    hasTimestampAttribute = true;
                }

                // All members marked with TimestampAttribute (inferred or explicit) need to
                // have [Editable(false)] applied
                if (hasTimestampAttribute && editableAttribute == null)
                {
                    editableAttribute = new EditableAttribute(false);
                }

                // Add RoundtripOriginal attribute to this member unless
                // - this entity has a timestamp member, in which case that member should be the ONLY
                //   member we apply RTO to.
                // - the member is marked with AssociationAttribute
                if (!member.IsAssociation &&
                    pd.Attributes[typeof(System.ComponentModel.DataAnnotations.AssociationAttribute)] == null &&
                    (this._metaType.VersionMember == null || member.IsVersion))
                {
                    if (pd.Attributes[typeof(RoundtripOriginalAttribute)] == null)
                    {
                        attributes.Add(new RoundtripOriginalAttribute());
                    }
                }

                if (isStringType && member.DbType != null && member.DbType.Length > 0 &&
                    pd.Attributes[typeof(StringLengthAttribute)] == null)
                {
                    InferStringLengthAttribute(member.DbType, attributes);
                }

                // Add EditableAttribute if required
                if (editableAttribute != null && pd.Attributes[typeof(EditableAttribute)] == null)
                {
                    attributes.Add(editableAttribute);
                }
            }
            return(attributes.ToArray());
        }
		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);
		}
Esempio n. 18
0
        /// <summary>
        /// Sets the parent association info for this entity.
        /// </summary>
        /// <remarks>
        /// Since a Type can have multiple compositions of the same Type, to
        /// identify a parent association, we must track both the parent instance
        /// AND the association.
        /// </remarks>
        /// <param name="parent">The parent.</param>
        /// <param name="association">The parent association.</param>
        internal void SetParent(Entity parent, AssociationAttribute association)
        {
            if (this._parent != parent)
            {
                if (parent == this)
                {
                    throw new InvalidOperationException(Resource.Entity_ChildCannotBeItsParent);
                }

                this._parent = parent;
            }

            this._parentAssociation = association;
        }
Esempio n. 19
0
		public void Constructor ()
		{
			AssociationAttribute attr;

			attr = new AssociationAttribute (null, "key1,key2", "key3,key4");
			Assert.AreEqual (null, attr.Name, "#A1-1");
			Assert.AreEqual ("key1,key2", attr.ThisKey, "#A1-2");
			Assert.AreEqual ("key3,key4", attr.OtherKey, "#A1-3");
			Assert.IsNotNull (attr.OtherKeyMembers, "#A2-1");

			int count = 0;
			var list = new List<string> ();
			foreach (string m in attr.OtherKeyMembers) {
				count++;
				list.Add (m);
			}
			Assert.AreEqual (2, count, "#A2-2");
			Assert.AreEqual ("key3", list [0], "#A2-3");
			Assert.AreEqual ("key4", list [1], "#A2-4");

			Assert.IsNotNull (attr.ThisKeyMembers, "#A3-1");

			count = 0;
			list = new List<string> ();
			foreach (string m in attr.ThisKeyMembers) {
				count++;
				list.Add (m);
			}
			Assert.AreEqual (2, count, "#A3-2");
			Assert.AreEqual ("key1", list [0], "#A3-3");
			Assert.AreEqual ("key2", list [1], "#A3-4");

			attr = new AssociationAttribute ("name", null, "key3,key4");
			Assert.AreEqual ("name", attr.Name, "#B1-1");
			Assert.AreEqual (null, attr.ThisKey, "#B1-2");
			Assert.AreEqual ("key3,key4", attr.OtherKey, "#B1-3");
			Assert.IsNotNull (attr.OtherKeyMembers, "#B2-1");

			count = 0;
			list = new List<string> ();
			foreach (string m in attr.OtherKeyMembers) {
				count++;
				list.Add (m);
			}
			Assert.AreEqual (2, count, "#B2-2");
			Assert.AreEqual ("key3", list [0], "#B2-3");
			Assert.AreEqual ("key4", list [1], "#B2-4");

			// this is just sad...
			try {
				var m = attr.ThisKeyMembers;
				Assert.Fail ("#B2-5");
			} catch (NullReferenceException) {
				// success
			}

			attr = new AssociationAttribute ("name", " key1  ,   key 2  ,, ,key    3  ", "       ");
			Assert.IsNotNull (attr.ThisKeyMembers, "#C1");

			count = 0;
			list = new List<string> ();
			foreach (string m in attr.ThisKeyMembers) {
				count++;
				list.Add (m);
			}

			// It seems all the whitespace is removed from key names
			Assert.AreEqual (5, count, "#C2-1");
			Assert.AreEqual ("key1", list [0], "#C2-2");
			Assert.AreEqual ("key2", list [1], "#C2-3");
			Assert.AreEqual (String.Empty, list [2], "#C2-4");
			Assert.AreEqual (String.Empty, list [3], "#C2-5");
			Assert.AreEqual ("key3", list [4], "#C2-6");

			Assert.IsNotNull (attr.OtherKeyMembers, "#C3");
			count = 0;
			list = new List<string> ();
			foreach (string m in attr.OtherKeyMembers) {
				count++;
				list.Add (m);
			}
			Assert.AreEqual (1, count, "#C4-1");
			Assert.AreEqual (String.Empty, list [0], "#C4-2");
		}
        internal static PropertyDescriptor GetReverseAssociation(PropertyDescriptor propertyDescriptor, AssociationAttribute assocAttrib)
        {
            Type otherType = TypeUtility.GetElementType(propertyDescriptor.PropertyType);

            foreach (PropertyDescriptor entityMember in TypeDescriptor.GetProperties(otherType))
            {
                if (entityMember.Name == propertyDescriptor.Name)
                {
                    // for self associations, both ends of the association are in
                    // the same class and have the same name. Therefore, we need to 
                    // skip the member itself.
                    continue;
                }

                AssociationAttribute otherAssocAttrib = entityMember.Attributes[typeof(AssociationAttribute)] as AssociationAttribute;
                if (otherAssocAttrib != null && otherAssocAttrib.Name == assocAttrib.Name)
                {
                    return entityMember;
                }
            }

            return null;
        }
        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 GenEntityAssocationProperty(CodeTypeDeclaration proxyClass, PropertyDescriptor pd, AssociationAttribute associationAttribute, bool isExternal)
        {
            // Register property types to prevent conflicts
            Type associationType =
                IsCollectionType(pd.PropertyType) ?
                    TypeUtility.GetElementType(pd.PropertyType) :
                    pd.PropertyType;

            // Check if we're in conflict
            if (!CodeGenUtilities.RegisterTypeName(associationType, this.Type.Namespace))
            {
                // Aggressively check for potential conflicts across other DomainService entity types.
                IEnumerable<Type> potentialConflicts =
                    this.ClientProxyGenerator.DomainServiceDescriptions
                        .SelectMany<DomainServiceDescription, Type>(dsd => dsd.EntityTypes)
                            .Where(entity => entity.Namespace == associationType.Namespace).Distinct();

                foreach (Type potentialConflict in potentialConflicts)
                {
                    // Check if we plan to include any types from this potential conflict's namespace
                    CodeGenUtilities.RegisterTypeName(potentialConflict, this.Type.Namespace);
                }
            }

            if (!IsCollectionType(pd.PropertyType))
            {
                this.GenerateSingletonAssociation(proxyClass, pd, associationAttribute, isExternal);
            }
            else
            {
                this.GenerateCollectionSideAssociation(proxyClass, pd, associationAttribute, isExternal);
            }
        }
Esempio n. 23
0
            /// <summary>
            /// Verify that the specified entity does not have any modified FK members for the
            /// specified composition.
            /// </summary>
            /// <param name="entity">The child entity to check.</param>
            /// <param name="compositionAttribute">The composition attribute.</param>
            private static void CheckInvalidChildUpdates(Entity entity, AssociationAttribute compositionAttribute)
            {
                if (compositionAttribute == null)
                {
                    return;
                }

                if (entity.EntityState == EntityState.Modified &&
                    entity.ModifiedProperties.Select(p => p.Name).Intersect(compositionAttribute.OtherKeyMembers).Any())
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.Entity_CantReparentComposedChild, entity));
                }
            }
        /// <summary>
        /// Creates an AssociationAttribute for the specified navigation property
        /// </summary>
        /// <param name="navigationProperty">The navigation property that corresponds to the association (it identifies the end points)</param>
        /// <returns>A new AssociationAttribute that describes the given navigation property association</returns>
        internal AssociationAttribute CreateAssociationAttribute(NavigationProperty navigationProperty)
        {
            AssociationInfo assocInfo = this.GetAssociationInfo(navigationProperty);
            bool isForeignKey = navigationProperty.FromEndMember.Name == assocInfo.FKRole;
            string thisKey;
            string otherKey;
            if (isForeignKey)
            {
                thisKey = FormatMemberList(assocInfo.ThisKey);
                otherKey = FormatMemberList(assocInfo.OtherKey);
            }
            else
            {
                otherKey = FormatMemberList(assocInfo.ThisKey);
                thisKey = FormatMemberList(assocInfo.OtherKey);
            }

            AssociationAttribute assocAttrib = new AssociationAttribute(assocInfo.Name, thisKey, otherKey);
            assocAttrib.IsForeignKey = isForeignKey;
            return assocAttrib;
        }
        protected virtual IEnumerable<Attribute> GetEntityMemberAttributes(PropertyDescriptor propertyDescriptor)
        {
            if (_classMetadata == null)
                return null;
            var attributes = new List<Attribute>();

            //KeyAttributes
            if (_classMetadata.Identifier != null)
            {
                foreach (Column id in _identifierCols)
                {
                    if (id.Name == propertyDescriptor.Name)
                    {
                        if (propertyDescriptor.Attributes[typeof(KeyAttribute)] == null)
                        {
                            attributes.Add(new KeyAttribute());
                        }
                        if (propertyDescriptor.Attributes[typeof(EditableAttribute)] == null)
                        {
                            //An identifier is not editable, sometimes anyway it allow an initial value
                            var editable = new EditableAttribute(false);
                            if (id.Value is SimpleValue)
                                editable.AllowInitialValue =
                                    "assigned".Equals(((SimpleValue)id.Value).IdentifierGeneratorStrategy,
                                                      StringComparison.InvariantCultureIgnoreCase);
                            attributes.Add(editable);
                        }
                        break;
                    }
                }
            }
            Property member = _classMetadata.PropertyIterator.FirstOrDefault(x => x.Name == propertyDescriptor.Name);
            if (member == null)             //If ther's no mapping in nhibernate... 
                return attributes;
            //Required
            if ((!member.IsNullable) &&
                (propertyDescriptor.PropertyType.IsValueType &&
                 (propertyDescriptor.Attributes[typeof(RequiredAttribute)] == null)))
            {

                attributes.Add(new RequiredAttribute());
            }
            //Association
            if (member.Type.IsAssociationType &&
                (propertyDescriptor.Attributes[typeof(AssociationAttribute)] == null))
            {
                string name;
                string thisKey = "";
                string otherkey = "";
                if (member.Type.IsCollectionType)
                {
                    name = propertyDescriptor.ComponentType.FullName + "_" + member.Name;

                    if (member.Type.ReturnedClass.GetGenericArguments().Length != 1)
                    {
                        throw new Exception(
                            String.Format(
                                "The property {0} is not a generic collection as expected (like IList<T>)...",
                                member.Name));
                    }
                    Type targetClassType = member.Type.ReturnedClass.GetGenericArguments()[0];

                    foreach (Column col in _identifierCols)
                    {
                        thisKey += (thisKey != "" ? ", " : "") + col.Name;

                        //*****Naming convention****
                        //Here I'm assuming that the name of each field in the type that holds the foreign key observe
                        //the following structure: 

                        string field = member.Name.Replace(Inflector.Pluralize(targetClassType.Name), "") +
                            propertyDescriptor.ComponentType.Name + "_" + col.Name;

                        otherkey += (otherkey != "" ? ", " : "") + field;

                        if (targetClassType.GetProperties(BindingFlags.Public | BindingFlags.Instance).SingleOrDefault(x => x.Name == field) == null)
                            throw new Exception(String.Format("The class {0} doesn't contain a Property named {1}",
                                                targetClassType.Name, field));
                    }

                }
                else //Member is a class type
                {
                    //Key could be composite, cycle every identifier on "the other side"
                    PersistentClass otherMappingClass = _nhibernateConfiguration.GetClassMapping(member.Type.ReturnedClass);
                    foreach (Column col in otherMappingClass.Key.ColumnIterator)
                    {
                        //Naming Convention:
                        //The name of each foreign key field be MUST BE the name of the class field + "_" + the name of the key field in the targetclass
                        thisKey += (thisKey != "" ? ", " : "") + member.Name + "_" + col.Name;
                        otherkey += (otherkey != "" ? ", " : "") + col.Name;
                    }

                    //Check: this name MUST ALWAYS BE the same on the both side of a bi-directional association
                    name = member.Type.ReturnedClass.FullName
                        + "_" + Inflector.Pluralize(member.PersistentClass.NodeName);
                }

                //CHECK: When do you want to add an IncludeAttribute ?
                if (!_classMetadata.IsLazy)
                {
                    var incAttr = new IncludeAttribute();
                    attributes.Add(incAttr);
                }

                var attribute = new AssociationAttribute(
                    name,
                    thisKey,
                    otherkey
                    );
                Type fromParent = ForeignKeyDirection.ForeignKeyFromParent.GetType();
                attribute.IsForeignKey =
                    fromParent.IsInstanceOfType(((IAssociationType)member.Type).ForeignKeyDirection);
                attributes.Add(attribute);
            }
            //RoundtripOriginal
            if (member == _classMetadata.Version)
                attributes.Add(new RoundtripOriginalAttribute());
            return attributes.ToArray();
        }
Esempio n. 26
0
            /// <summary>
            /// For the current ChangeSetEntry, this method returns all other entries in the changeset
            /// whose entity was previously a child in the specified composition association.
            /// </summary>
            /// <param name="association">The association to check.</param>
            /// <returns>The resulting collection of entries.</returns>
            private IEnumerable<ChangeSetEntry> FindOriginalChildren(AssociationAttribute association)
            {
                foreach (ChangeSetEntry entry in this._changeSetEntries.Where(p => p.Entity.EntityState == EntityState.Deleted))
                {
                    Entity parent = entry.Entity.Parent;
                    if (parent == null)
                    {
                        // not a child entity
                        continue;
                    }

                    if (parent == this._currentChangeSetEntry.Entity &&
                        entry.Entity.ParentAssociation.Name == association.Name)
                    {
                        // if the current entity is the original parent and the association
                        // matches, return the entry
                        yield return entry;
                    }
                }
            }
        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);
        }
 public TypePropertyAssociationMetadata(AssociationAttribute associationAttr)
 {
     Name = associationAttr.Name;
     IsForeignKey = associationAttr.IsForeignKey;
     _otherKeyMembers = associationAttr.OtherKeyMembers.ToList<string>();
     _thisKeyMembers = associationAttr.ThisKeyMembers.ToList<string>();
 }
        private CodeMemberMethod GenerateDetach(CodeTypeDeclaration proxyClass, Type entityType, AssociationAttribute assoc, PropertyDescriptor pd)
        {
            // detach method
            CodeMemberMethod method = new CodeMemberMethod();
            method.Name = "Detach" + pd.Name;
            method.Attributes = MemberAttributes.Private;
            method.ReturnType = CodeGenUtilities.GetTypeReference(typeof(void), this.ClientProxyGenerator, proxyClass);

            CodeParameterDeclarationExpression pdef = new CodeParameterDeclarationExpression(
                                                        CodeGenUtilities.GetTypeReference(entityType, this.ClientProxyGenerator, proxyClass),
                                                        "entity");
            method.Parameters.Add(pdef);

            CodeVariableReferenceExpression pref = new CodeVariableReferenceExpression(pdef.Name);
            PropertyDescriptor reverseAssociationMember = GetReverseAssociation(pd, assoc);
            string revName = reverseAssociationMember.Name;

            if (!IsCollectionType(pd.PropertyType))
            {
                // pref.Prop.Remove(this)
                method.Statements.Add(
                    new CodeExpressionStatement(
                        new CodeMethodInvokeExpression(
                            new CodePropertyReferenceExpression(pref, revName),
                            "Remove",
                            new CodeThisReferenceExpression())));
            }
            else
            {
                // pref.Prop = null
                method.Statements.Add(
                    new CodeAssignStatement(
                        new CodePropertyReferenceExpression(pref, revName),
                        new CodePrimitiveExpression(null)));
            }

            proxyClass.Members.Add(method);

            return method;
        }