1 //---------------------------------------------------------------------
2 // <copyright file="PropertyEmitter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 //---------------------------------------------------------------------
11 using System.Collections.Generic;
12 using System.Data.Entity.Design;
13 using System.Data.Entity.Design.Common;
14 using System.Data.Entity.Design.SsdlGenerator;
15 using System.Data.Metadata.Edm;
16 using System.Data.Objects.ELinq;
17 using System.Diagnostics;
18 using System.Globalization;
20 namespace System.Data.EntityModel.Emitters
22 internal sealed class PropertyEmitter : PropertyEmitterBase
24 private CodeFieldReferenceExpression _fieldRef = null;
25 private CodeFieldReferenceExpression _complexPropertyInitializedFieldRef = null;
28 private const string NestedStoreObjectCollection = "InlineObjectCollection";
29 private const string DetachFromParentMethodName = "DetachFromParent";
31 #region Public Methods
33 public PropertyEmitter(ClientApiGenerator generator, EdmProperty property, bool declaringTypeUsesStandardBaseType)
34 : base(generator, property, declaringTypeUsesStandardBaseType)
39 /// Emit the declaration of the property for the class.
41 /// <returns>The Property declaration pieces of the CodeDom.</returns>
42 public CodeMemberProperty EmitPropertyDeclaration(CodeTypeReference propertyReturnType)
44 MemberAttributes scope = AccessibilityFromGettersAndSetters(Item);
45 CodeMemberProperty memberProperty = EmitPropertyDeclaration(scope, propertyReturnType, IsVirtualProperty, HidesBaseClassProperty);
47 memberProperty.HasSet = true;
48 memberProperty.HasGet = true;
50 return memberProperty;
54 /// Main method for Emitting property code.
56 /// <param name="typeDecl">The CodeDom representation of the type that the property is being added to.</param>
57 protected override void EmitProperty(CodeTypeDeclaration typeDecl)
59 CodeTypeReference typeRef = PropertyType;
61 // raise the PropertyGenerated event
63 PropertyGeneratedEventArgs eventArgs = new PropertyGeneratedEventArgs(Item, FieldName, typeRef);
64 this.Generator.RaisePropertyGeneratedEvent(eventArgs);
66 // the event subscriber cannot change the return type of the property
68 DisallowReturnTypeChange(typeRef, eventArgs.ReturnType);
70 CodeMemberProperty memberProperty = EmitPropertyDeclaration(eventArgs.ReturnType);
71 if (memberProperty == null)
76 EmitCustomAttributes(memberProperty, eventArgs.AdditionalAttributes);
78 EmitPropertyGetter(memberProperty, eventArgs.AdditionalGetStatements);
79 EmitPropertySetter(memberProperty, eventArgs.AdditionalSetStatements);
80 typeDecl.Members.Add(memberProperty);
82 EmitField(typeDecl, eventArgs.ReturnType);
84 EmitPropertyOnChangePartialMethods(typeDecl, eventArgs.ReturnType);
88 /// Emit these methods as "abstract" and fix them up later to be "partial".
89 /// CodeDOM does not support partial methods
91 /// <param name="typeDecl"></param>
92 private void EmitPropertyOnChangePartialMethods(CodeTypeDeclaration typeDecl, CodeTypeReference returnType)
94 CodeMemberMethod onChangingDomMethod = new CodeMemberMethod();
95 Generator.AttributeEmitter.EmitGeneratedCodeAttribute(onChangingDomMethod);
96 onChangingDomMethod.Name = OnChangingPartialMethodName(PropertyName);
97 onChangingDomMethod.ReturnType = new CodeTypeReference(typeof(void));
98 onChangingDomMethod.Attributes = MemberAttributes.Abstract | MemberAttributes.Public;
99 onChangingDomMethod.Parameters.Add(new CodeParameterDeclarationExpression(returnType, "value"));
100 typeDecl.Members.Add(onChangingDomMethod);
102 CodeMemberMethod onChangedDomMethod = new CodeMemberMethod();
103 Generator.AttributeEmitter.EmitGeneratedCodeAttribute(onChangedDomMethod);
104 onChangedDomMethod.Name = OnChangedPartialMethodName(PropertyName);
105 onChangedDomMethod.ReturnType = new CodeTypeReference(typeof(void));
106 onChangedDomMethod.Attributes = MemberAttributes.Abstract | MemberAttributes.Public;
107 typeDecl.Members.Add(onChangedDomMethod);
109 Generator.FixUps.Add(new FixUp(PropertyClassName + "." + OnChangingPartialMethodName(PropertyName), FixUpType.MarkAbstractMethodAsPartial));
110 Generator.FixUps.Add(new FixUp(PropertyClassName + "." + OnChangedPartialMethodName(PropertyName), FixUpType.MarkAbstractMethodAsPartial));
113 private void EmitField(CodeTypeDeclaration typeDecl, CodeTypeReference fieldType)
115 CodeMemberField memberField = new CodeMemberField(fieldType, FieldName);
116 Generator.AttributeEmitter.EmitGeneratedCodeAttribute(memberField);
118 memberField.Attributes = MemberAttributes.Private;
119 if (HasDefault(Item))
121 memberField.InitExpression = GetDefaultValueExpression(Item);
124 typeDecl.Members.Add(memberField);
126 if (MetadataUtil.IsComplexType(Item.TypeUsage.EdmType))
128 CodeMemberField complexInitField = new CodeMemberField(TypeReference.ForType(typeof(bool)), ComplexPropertyInitializedFieldName);
129 Generator.AttributeEmitter.EmitGeneratedCodeAttribute(complexInitField);
130 complexInitField.Attributes = MemberAttributes.Private;
131 typeDecl.Members.Add(complexInitField);
136 /// Get a reference to the base class DataObject
138 public static CodeTypeReferenceExpression CreateEdmStructuralObjectRef(TypeReference typeReference)
140 return new CodeTypeReferenceExpression(typeReference.ForType(typeof(System.Data.Objects.DataClasses.StructuralObject)));
145 #region Public Properties
147 public CodeTypeReference PropertyType
151 CodeTypeReference typeRef = GetType(Item, false);
156 public new EdmProperty Item
160 return base.Item as EdmProperty;
166 #region Internal Methods
169 /// Name of the associated Entity property for Ref(T) properties
171 public string EntityPropertyName
181 #region Private Methods
186 /// <param name="memberProperty"></param>
187 /// <param name="additionalAttributes">Additional attributes to emit</param>
188 private void EmitCustomAttributes(CodeMemberProperty memberProperty,
189 List<CodeAttributeDeclaration> additionalAttributes)
191 Generator.AttributeEmitter.EmitPropertyAttributes(this, memberProperty, additionalAttributes);
194 private void EmitPropertyGetter(CodeMemberProperty memberProperty, List<CodeStatement> additionalGetStatements)
196 CodeStatementCollection statements = memberProperty.GetStatements;
198 // we need to insert user-specified code before other/existing code, including
199 // the return statement
200 if (additionalGetStatements != null && additionalGetStatements.Count > 0)
204 CodeStatementCollection getStatements = new CodeStatementCollection();
205 getStatements.AddRange(additionalGetStatements.ToArray());
206 if (statements != null && statements.Count > 0)
208 getStatements.AddRange(statements);
211 statements.AddRange(getStatements);
213 catch (ArgumentNullException e)
215 Generator.AddError(Strings.InvalidGetStatementSuppliedForProperty(Item.Name),
216 ModelBuilderErrorCode.InvalidGetStatementSuppliedForProperty,
217 EdmSchemaErrorSeverity.Error,
222 MemberAttributes access = memberProperty.Attributes & MemberAttributes.AccessMask;
224 AddGetterSetterFixUp(Generator.FixUps, PropertyFQName, GetGetterAccessibility(Item), access, true);
226 EmitPropertyGetterBody(statements);
229 private void EmitPropertyGetterBody(CodeStatementCollection statements)
231 // If the SchemaElement.Type isn't a ComplexType it better be PrimitiveType.
232 if (MetadataUtil.IsComplexType(Item.TypeUsage.EdmType))
234 //Since Complex Collections are not supported by
235 //the stack, we don't need to do anything special
236 //like doing an Attach or Detatch like the way we do for complex types.
237 if (GetCollectionKind(Item.TypeUsage) == CollectionKind.None)
239 // _field = GetValidValue( _field, FieldPropertyInfo, _fieldInitialized);
241 new CodeAssignStatement(FieldRef,
242 new CodeMethodInvokeExpression(
244 Utils.GetValidValueMethodName,
245 new CodeDirectionExpression(FieldDirection.In, FieldRef),
246 new CodePrimitiveExpression(PropertyName),
247 new CodePrimitiveExpression(Item.Nullable),
248 ComplexPropertyInitializedFieldRef)));
250 // this._complexPropertyInitialized = true;
252 new CodeAssignStatement(
253 ComplexPropertyInitializedFieldRef,
254 new CodePrimitiveExpression(true)));
257 statements.Add(new CodeMethodReturnStatement(FieldRef));
261 PrimitiveType primitiveType = Item.TypeUsage.EdmType as PrimitiveType;
262 if (primitiveType != null && primitiveType.ClrEquivalentType == typeof(byte[]))
264 // return GetValidValue(_field);
266 new CodeMethodReturnStatement(
267 new CodeMethodInvokeExpression(
268 CreateEdmStructuralObjectRef(TypeReference),
269 Utils.GetValidValueMethodName,
274 // for everything else just return the field.
275 statements.Add(new CodeMethodReturnStatement(FieldRef));
284 /// <param name="memberProperty"></param>
285 /// <param name="additionalSetStatements">Additional statements to emit</param>
286 private void EmitPropertySetter(CodeMemberProperty memberProperty, List<CodeStatement> additionalSetStatements)
288 CodeStatementCollection statements = memberProperty.SetStatements;
290 MemberAttributes access = memberProperty.Attributes & MemberAttributes.AccessMask;
292 AddGetterSetterFixUp(Generator.FixUps, PropertyFQName, GetSetterAccessibility(Item), access, false);
294 EmitPropertySetterBody(statements, additionalSetStatements);
298 /// This is a control function to delegate the creation of the
299 /// setter statments to the correct code path
301 /// <param name="statements">The collection that the setter statements should be added to.</param>
302 /// <param name="additionalSetStatements">Additional statements to emit</param>
303 private void EmitPropertySetterBody(CodeStatementCollection statements, List<CodeStatement> additionalSetStatements)
305 // Invoke the partial method "On[PropertyName]Changing();
307 new CodeMethodInvokeExpression(
309 OnChangingPartialMethodName(PropertyName), new CodePropertySetValueReferenceExpression()));
311 // ReportPropertyChanging( _piFieldName );
313 new CodeMethodInvokeExpression(
315 Utils.ReportPropertyChangingMethodName,
316 new CodePrimitiveExpression(PropertyName)));
318 // insert additional statements following the PropertyChanging event
319 if (additionalSetStatements != null && additionalSetStatements.Count > 0)
323 statements.AddRange(additionalSetStatements.ToArray());
325 catch (ArgumentNullException e)
327 Generator.AddError(Strings.InvalidSetStatementSuppliedForProperty(Item.Name),
328 ModelBuilderErrorCode.InvalidSetStatementSuppliedForProperty,
329 EdmSchemaErrorSeverity.Error,
334 if (MetadataUtil.IsPrimitiveType(Item.TypeUsage.EdmType))
336 EmitScalarTypePropertySetStatements(statements, CollectionKind.None);
338 else if (MetadataUtil.IsComplexType(Item.TypeUsage.EdmType))
340 // ComplexTypes have a completely different set pattern:
341 EmitComplexTypePropertySetStatements(statements, CollectionKind.None);
343 else if (MetadataUtil.IsCollectionType(Item.TypeUsage.EdmType))
345 if (MetadataUtil.IsComplexType(((CollectionType)Item.TypeUsage.EdmType).TypeUsage.EdmType))
347 EmitComplexTypePropertySetStatements(statements, GetCollectionKind(Item.TypeUsage));
351 Debug.Assert(MetadataUtil.IsPrimitiveType(((CollectionType)Item.TypeUsage.EdmType).TypeUsage.EdmType),
352 "Collections should be of primitive types or complex types");
353 EmitScalarTypePropertySetStatements(statements, GetCollectionKind(Item.TypeUsage));
357 else if (MetadataUtil.IsEnumerationType(Item.TypeUsage.EdmType))
359 // this.fieldName = value;
361 new CodeAssignStatement(
363 new CodePropertySetValueReferenceExpression()));
367 // ReportPropertyChanged( _piFieldName );
369 new CodeMethodInvokeExpression(
371 Utils.ReportPropertyChangedMethodName,
372 new CodePrimitiveExpression(PropertyName)));
374 // Invoke the partial method "On[PropertyName]Changed();
376 new CodeMethodInvokeExpression(
378 OnChangedPartialMethodName(PropertyName)));
382 /// Do the fixups to allow get and set statements in properties
383 /// to have different accessibility than the property itself.
385 /// <param name="accessibility">The accessibility for the getter or setter</param>
386 /// <param name="propertyAccessibility">The property's accessibility</param>
387 /// <param name="isGetter">True if this is a getter, false if a setter</param>
388 internal static void AddGetterSetterFixUp(FixUpCollection fixups, string propertyFqName, MemberAttributes accessibility, MemberAttributes propertyAccessibility, bool isGetter)
390 Debug.Assert(GetAccessibilityRank(accessibility) >= 0, "bad accessibility");
393 if (accessibility == MemberAttributes.Private && propertyAccessibility != MemberAttributes.Private)
397 fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsPrivate));
401 fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsPrivate));
406 if (accessibility == MemberAttributes.Assembly && propertyAccessibility != MemberAttributes.Assembly)
410 fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsInternal));
414 fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsInternal));
419 if (accessibility == MemberAttributes.Public && propertyAccessibility != MemberAttributes.Public)
423 fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsPublic));
427 fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsPublic));
432 if (accessibility == MemberAttributes.Family && propertyAccessibility != MemberAttributes.Family)
436 fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertyGetAsProtected));
440 fixups.Add(new FixUp(propertyFqName, FixUpType.MarkPropertySetAsProtected));
447 /// Emit the set statements for a property that is a scalar type
449 /// <param name="statements">The statement collection to add the set statements to.</param>
450 private void EmitScalarTypePropertySetStatements(CodeStatementCollection statements,
451 CollectionKind collectionKind)
453 Debug.Assert(statements != null, "statments can't be null");
454 Debug.Assert(((MetadataUtil.IsPrimitiveType(Item.TypeUsage.EdmType)) || (MetadataUtil.IsCollectionType(Item.TypeUsage.EdmType)))
455 , "Must be a primitive type or collection type property");
457 CodePropertySetValueReferenceExpression valueRef = new CodePropertySetValueReferenceExpression();
458 //Since collections are not supported by
459 //the stack, we don't need to do anything special
460 //like doing an Attach or Detatch like the way we do for complex types.
461 if (collectionKind == CollectionKind.None)
464 PrimitiveType primitiveType = (PrimitiveType)Item.TypeUsage.EdmType;
467 // this.fieldName = SetValidValue( value );
469 List<CodeExpression> parameters = new List<CodeExpression>();
470 parameters.Add(valueRef);
473 // pattern for non Nullable<T> types (string, byte[])
475 // this.fieldName = SetValidVaue( value, nullability );
477 if (primitiveType.ClrEquivalentType.IsClass)
479 // ref types have an extra boolean parameter to tell if the property is allowed to
481 parameters.Add(new CodePrimitiveExpression(Item.Nullable));
484 // now create and add the built statement
486 new CodeAssignStatement(
488 new CodeMethodInvokeExpression(
489 CreateEdmStructuralObjectRef(TypeReference),
490 Utils.SetValidValueMethodName,
491 parameters.ToArray())));
495 // this.fieldName = value;
497 new CodeAssignStatement(
498 FieldRef, valueRef));
503 private CodeExpression GetEnumValue<T>(T value)
505 Type type = typeof(T);
506 return new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(TypeReference.ForType(type)), Enum.GetName(type, value));
510 /// Emit the property set statments to properly set a ComplexType.
512 /// <param name="statements">The collection of statements that the set statements should be added to.</param>
513 private void EmitComplexTypePropertySetStatements(CodeStatementCollection statements, CollectionKind collectionKind)
515 CodePropertySetValueReferenceExpression valueRef = new CodePropertySetValueReferenceExpression();
516 //Since collections are not supported by
517 //the stack, we don't need to do anything special
518 //like doing an Attach or Detatch like the way we do for complex types.
519 if (collectionKind == CollectionKind.None)
522 // this.fieldName = SetValidValue( this.fieldName, value, _pifieldName);
524 new CodeAssignStatement(
526 new CodeMethodInvokeExpression(
528 Utils.SetValidValueMethodName,
531 new CodePrimitiveExpression(PropertyName))));
533 // this._complexPropertyInitialized = true;
535 new CodeAssignStatement(
536 ComplexPropertyInitializedFieldRef,
537 new CodePrimitiveExpression(true)));
541 // this.fieldName = value;
543 new CodeAssignStatement(
544 FieldRef, valueRef));
551 /// See if a property names will hide a base class member name
553 private bool HidesBaseClassProperty
557 StructuralType parentBaseClass = Item.DeclaringType.BaseType as StructuralType;
558 if (parentBaseClass != null && parentBaseClass.Members.Contains(PropertyName))
565 private CodeTypeReference GetType(EdmProperty property, bool getElementType)
567 PropertyTypeReferences types = default(PropertyTypeReferences);
568 EdmType propertyType = property.TypeUsage.EdmType;
571 if (MetadataUtil.IsPrimitiveType(propertyType))
573 types = new PropertyTypeReferences(TypeReference, (PrimitiveType)propertyType);
575 else if (MetadataUtil.IsComplexType(propertyType))
577 types = new PropertyTypeReferences(TypeReference, (ComplexType)propertyType, Generator);
579 else if (Helper.IsCollectionType(propertyType))
581 TypeUsage typeUsage = ((CollectionType)propertyType).TypeUsage;
582 if (MetadataUtil.IsPrimitiveType(typeUsage.EdmType))
584 types = new PropertyTypeReferences(TypeReference, (PrimitiveType)typeUsage.EdmType, GetCollectionKind(property.TypeUsage));
588 Debug.Assert(MetadataUtil.IsComplexType(typeUsage.EdmType));
589 types = new PropertyTypeReferences(TypeReference, (ComplexType)typeUsage.EdmType, GetCollectionKind(property.TypeUsage), Generator);
594 // shouldn't be able to get here....
595 Debug.Fail("Unexpected Property.Type type: " + propertyType.GetType());
598 // Set types, or retrieve existing types if they have been set in the interim
599 // Don't cache Collection types since CollectionKind is really a facet and
600 //it is not part of the key we are using for the dictionary used to cache.
601 if (!Helper.IsCollectionType(propertyType))
603 Debug.Assert(types.NonNullable != null && types.Nullable != null, "did you forget to set the types variable?");
606 if (property.Nullable)
608 return types.Nullable;
612 return types.NonNullable;
617 private static CollectionKind GetCollectionKind(TypeUsage usage)
619 Facet collectionFacet;
620 if (usage.Facets.TryGetValue(EdmConstants.CollectionKind, false, out collectionFacet))
622 return (CollectionKind)collectionFacet.Value;
625 return CollectionKind.None;
628 private string OnChangingPartialMethodName(string propertyName) { return "On" + propertyName + "Changing"; }
629 private string OnChangedPartialMethodName(string propertyName) { return "On" + propertyName + "Changed"; }
633 #region Private Properties
635 private CodeFieldReferenceExpression FieldRef
639 if (_fieldRef == null)
640 _fieldRef = new CodeFieldReferenceExpression(ThisRef, FieldName);
646 private CodeFieldReferenceExpression ComplexPropertyInitializedFieldRef
650 if (_complexPropertyInitializedFieldRef == null)
651 _complexPropertyInitializedFieldRef = new CodeFieldReferenceExpression(ThisRef, ComplexPropertyInitializedFieldName);
653 return _complexPropertyInitializedFieldRef;
657 private string FieldName
661 return Utils.FieldNameFromPropName(PropertyName);
665 private string ComplexPropertyInitializedFieldName
669 return Utils.ComplexPropertyInitializedNameFromPropName(PropertyName);
673 internal bool IsKeyProperty
677 EntityType entity = Item.DeclaringType as EntityType;
680 return entity.KeyMembers.Contains(Item.Name);
686 internal static bool HasDefault(EdmProperty property)
688 return property.DefaultValue != null;
691 private CodeExpression GetDefaultValueExpression(EdmProperty property)
693 PrimitiveTypeKind type;
694 object value = property.DefaultValue;
696 && Utils.TryGetPrimitiveTypeKind(property.TypeUsage.EdmType, out type))
698 if (!property.Nullable && value.Equals(TypeSystem.GetDefaultValue(value.GetType())))
705 case PrimitiveTypeKind.Boolean:
706 case PrimitiveTypeKind.Byte:
707 case PrimitiveTypeKind.Int16:
708 case PrimitiveTypeKind.Int32:
709 case PrimitiveTypeKind.Int64:
710 case PrimitiveTypeKind.Decimal:
711 case PrimitiveTypeKind.Single:
712 case PrimitiveTypeKind.Double:
713 case PrimitiveTypeKind.String:
715 return new CodePrimitiveExpression(value);
717 case PrimitiveTypeKind.Guid:
719 return GetCodeExpressionFromGuid(value);
721 case PrimitiveTypeKind.DateTime:
723 return GetCodeExpressionFromDateTimeDefaultValue(value, property);
725 case PrimitiveTypeKind.DateTimeOffset:
727 return GetCodeExpressionFromDateTimeOffsetDefaultValue(value, property);
729 case PrimitiveTypeKind.Time:
731 return GetCodeExpressionFromTimeSpanDefaultValue(value, property);
733 case PrimitiveTypeKind.Binary:
735 return GetCodeExpressionFromBinary(value);
738 Debug.Fail("Unsupported property type:" + type.ToString());
746 private CodeExpression GetCodeExpressionFromBinary(object value)
748 byte[] data = (byte[])value;
749 CodeExpression[] bytes = new CodeExpression[data.Length];
751 for (int iByte = 0; iByte < data.Length; ++iByte)
753 bytes[iByte] = new CodePrimitiveExpression(data[iByte]);
756 return new CodeArrayCreateExpression(TypeReference.ByteArray, bytes);
759 private CodeExpression GetCodeExpressionFromGuid(object value)
761 Guid guid = (Guid)value;
762 return new CodeObjectCreateExpression(TypeReference.Guid,
763 new CodePrimitiveExpression(guid.ToString("D", CultureInfo.InvariantCulture)));
766 private CodeExpression GetCodeExpressionFromDateTimeDefaultValue(object value, EdmProperty property)
768 DateTime utc = (DateTime)value;
769 DateTime dateTime = DateTime.SpecifyKind(utc, DateTimeKind.Unspecified);
771 return new CodeObjectCreateExpression(TypeReference.DateTime, new CodePrimitiveExpression(dateTime.Ticks), GetEnumValue(DateTimeKind.Unspecified));
774 private CodeExpression GetCodeExpressionFromDateTimeOffsetDefaultValue(object value, EdmProperty property)
776 DateTimeOffset dateTimeOffset = (DateTimeOffset)value;
778 return new CodeObjectCreateExpression(TypeReference.DateTimeOffset, new CodePrimitiveExpression(dateTimeOffset.Ticks),
779 new CodeObjectCreateExpression(TypeReference.TimeSpan, new CodePrimitiveExpression(dateTimeOffset.Offset.Ticks)));
782 private CodeExpression GetCodeExpressionFromTimeSpanDefaultValue(object value, EdmProperty property)
784 TimeSpan timeSpan = (TimeSpan)value;
785 return new CodeObjectCreateExpression(TypeReference.TimeSpan, new CodePrimitiveExpression(timeSpan.Ticks));
788 public bool IsVirtualProperty
796 private struct PropertyTypeReferences
798 CodeTypeReference _nonNullable;
799 CodeTypeReference _nullable;
800 public PropertyTypeReferences(TypeReference typeReference, PrimitiveType primitiveType)
801 : this(typeReference, primitiveType, CollectionKind.None)
805 public PropertyTypeReferences(TypeReference typeReference, PrimitiveType primitiveType, CollectionKind collectionKind)
807 Type type = primitiveType.ClrEquivalentType;
808 if (collectionKind == CollectionKind.None)
810 _nonNullable = typeReference.ForType(type);
811 if (type.IsValueType)
813 _nullable = typeReference.NullableForType(type);
817 _nullable = typeReference.ForType(type);
822 CodeTypeReference primitiveTypeRef = typeReference.ForType(type);
823 CodeTypeReference collectionType = GetCollectionTypeReference(typeReference, primitiveTypeRef, collectionKind);
824 _nonNullable = collectionType;
825 _nullable = collectionType;
829 public PropertyTypeReferences(TypeReference typeReference, ComplexType complexType, CollectionKind collectionKind, ClientApiGenerator generator)
831 CodeTypeReference baseType = generator.GetLeastPossibleQualifiedTypeReference(complexType);
832 baseType = GetCollectionTypeReference(typeReference, baseType, collectionKind);
833 _nonNullable = baseType;
834 _nullable = baseType;
837 private static CodeTypeReference GetCollectionTypeReference(TypeReference typeReference, CodeTypeReference baseType, CollectionKind collectionKind)
839 if (collectionKind == CollectionKind.Bag)
841 baseType = GetCollectionTypeReferenceForBagSemantics(typeReference, baseType);
843 else if (collectionKind == CollectionKind.List)
845 baseType = GetCollectionTypeReferenceForListSemantics(typeReference, baseType);
849 Debug.Assert(collectionKind == CollectionKind.None, "Was another CollectionKind value added");
850 // nothing more to do for .None
855 public PropertyTypeReferences(TypeReference typeReference, ComplexType complexType, ClientApiGenerator generator)
856 : this(typeReference, complexType, CollectionKind.None, generator)
860 private static CodeTypeReference GetCollectionTypeReferenceForBagSemantics(TypeReference typeReference, CodeTypeReference baseType)
862 CodeTypeReference typeRef = typeReference.ForType(typeof(System.Collections.Generic.ICollection<>), baseType);
866 private static CodeTypeReference GetCollectionTypeReferenceForListSemantics(TypeReference typeReference, CodeTypeReference baseType)
868 CodeTypeReference typeRef = typeReference.ForType(typeof(System.Collections.Generic.IList<>), baseType);
872 public CodeTypeReference NonNullable
874 get { return _nonNullable; }
876 public CodeTypeReference Nullable
878 get { return _nullable; }
883 // properties from ClassPropertyEmitter
885 public string PropertyFQName
889 return Item.DeclaringType.FullName + "." + Item.Name;
893 public string PropertyName
897 return EntityPropertyName;
901 private string PropertyClassName
905 return Item.DeclaringType.Name;
909 private CodeMemberProperty EmitPropertyDeclaration(MemberAttributes scope, CodeTypeReference propertyType, bool isVirtual,
910 bool hidesBaseProperty)
912 Debug.Assert(GetAccessibilityRank(scope) >= 0, "scope should only be an accessibility attribute");
914 CodeMemberProperty memberProperty = new CodeMemberProperty();
915 memberProperty.Name = PropertyName;
916 CommentEmitter.EmitSummaryComments(Item, memberProperty.Comments);
918 memberProperty.Attributes = scope;
922 memberProperty.Attributes |= MemberAttributes.Final;
925 if (hidesBaseProperty || AncestorClassDefinesName(memberProperty.Name))
927 memberProperty.Attributes |= MemberAttributes.New;
930 memberProperty.Type = propertyType;
932 return memberProperty;
935 private void DisallowReturnTypeChange(CodeTypeReference baseType, CodeTypeReference newType)
937 if (Helper.IsCollectionType(Item.TypeUsage.EdmType) && GetCollectionKind(Item.TypeUsage) != CollectionKind.None)
941 throw EDesignUtil.InvalidOperation(Strings.CannotChangePropertyReturnTypeToNull(Item.Name, Item.DeclaringType.Name));
944 // you can change the return type of collection properties
945 // we don't even need to check
949 if (!(baseType == null && newType == null) &&
951 (baseType != null && !baseType.Equals(newType)) ||
952 (newType != null && !newType.Equals(baseType))
956 throw EDesignUtil.InvalidOperation(Strings.CannotChangePropertyReturnType(Item.Name, Item.DeclaringType.Name));