1 //---------------------------------------------------------------------
2 // <copyright file="NavigationPropertyEmitter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
13 using System
.Collections
.Generic
;
14 using System
.Data
.Entity
.Design
;
15 using Som
=System
.Data
.EntityModel
.SchemaObjectModel
;
16 using System
.Data
.Metadata
.Edm
;
17 using System
.Diagnostics
;
18 using System
.Data
.Entity
.Design
.SsdlGenerator
;
19 using System
.Data
.Entity
.Design
.Common
;
22 namespace System
.Data
.EntityModel
.Emitters
25 /// Summary description for NavigationPropertyEmitter.
27 internal sealed class NavigationPropertyEmitter
: PropertyEmitterBase
29 private const string ValuePropertyName
= "Value";
34 /// <param name="generator"></param>
35 /// <param name="navigationProperty"></param>
36 public NavigationPropertyEmitter(ClientApiGenerator generator
, NavigationProperty navigationProperty
, bool declaringTypeUsesStandardBaseType
)
37 : base(generator
, navigationProperty
, declaringTypeUsesStandardBaseType
)
42 /// Generate the navigation property
44 /// <param name="typeDecl">The type to add the property to.</param>
45 protected override void EmitProperty(CodeTypeDeclaration typeDecl
)
47 EmitNavigationProperty(typeDecl
);
51 /// Generate the navigation property specified
53 /// <param name="typeDecl">The type to add the property to.</param>
54 private void EmitNavigationProperty( CodeTypeDeclaration typeDecl
)
56 // create a regular property
57 CodeMemberProperty property
= EmitNavigationProperty(Item
.ToEndMember
, false);
58 typeDecl
.Members
.Add(property
);
60 if (Item
.ToEndMember
.RelationshipMultiplicity
!= RelationshipMultiplicity
.Many
)
62 // create a ref property
63 property
= EmitNavigationProperty(Item
.ToEndMember
, true);
64 typeDecl
.Members
.Add(property
);
70 /// Generate a navigation property
72 /// <param name="target">the other end</param>
73 /// <param name="referenceProperty">True to emit Reference navigation property</param>
74 /// <returns>the generated property</returns>
75 private CodeMemberProperty
EmitNavigationProperty(RelationshipEndMember target
, bool referenceProperty
)
77 CodeTypeReference typeRef
= GetReturnType(target
, referenceProperty
);
79 // raise the PropertyGenerated event
80 PropertyGeneratedEventArgs eventArgs
= new PropertyGeneratedEventArgs(Item
,
81 null, // no backing field
83 this.Generator
.RaisePropertyGeneratedEvent(eventArgs
);
85 // [System.ComponentModel.Browsable(false)]
86 // public TargetType TargetName
87 // public EntityReference<TargetType> TargetName
89 // public EntityCollection<targetType> TargetNames
90 CodeMemberProperty property
= new CodeMemberProperty();
91 if (referenceProperty
)
93 AttributeEmitter
.AddBrowsableAttribute(property
);
94 Generator
.AttributeEmitter
.EmitGeneratedCodeAttribute(property
);
98 Generator
.AttributeEmitter
.EmitNavigationPropertyAttributes(Generator
, target
, property
, eventArgs
.AdditionalAttributes
);
100 // Only reference navigation properties are currently currently supported with XML serialization
101 // and thus we should use the XmlIgnore and SoapIgnore attributes on other property types.
102 AttributeEmitter
.AddIgnoreAttributes(property
);
105 AttributeEmitter
.AddDataMemberAttribute(property
);
107 CommentEmitter
.EmitSummaryComments(Item
, property
.Comments
);
109 property
.Name
= Item
.Name
;
110 if (referenceProperty
)
112 property
.Name
+= "Reference";
113 if (IsNameAlreadyAMemberName(Item
.DeclaringType
, property
.Name
, Generator
.LanguageAppropriateStringComparer
))
115 Generator
.AddError(Strings
.GeneratedNavigationPropertyNameConflict(Item
.Name
, Item
.DeclaringType
.Name
, property
.Name
),
116 ModelBuilderErrorCode
.GeneratedNavigationPropertyNameConflict
,
117 EdmSchemaErrorSeverity
.Error
, Item
.DeclaringType
.FullName
, property
.Name
);
121 if (eventArgs
.ReturnType
!= null && !eventArgs
.ReturnType
.Equals(typeRef
))
123 property
.Type
= eventArgs
.ReturnType
;
127 property
.Type
= typeRef
;
130 property
.Attributes
= MemberAttributes
.Final
;
132 CodeMethodInvokeExpression getMethod
= EmitGetMethod(target
);
133 CodeExpression getReturnExpression
;
135 property
.Attributes
|= AccessibilityFromGettersAndSetters(Item
);
136 // setup the accessibility of the navigation property setter and getter
137 MemberAttributes propertyAccessibility
= property
.Attributes
& MemberAttributes
.AccessMask
;
138 PropertyEmitter
.AddGetterSetterFixUp(Generator
.FixUps
, GetFullyQualifiedPropertyName(property
.Name
),
139 PropertyEmitter
.GetGetterAccessibility(Item
), propertyAccessibility
, true);
140 PropertyEmitter
.AddGetterSetterFixUp(Generator
.FixUps
, GetFullyQualifiedPropertyName(property
.Name
),
141 PropertyEmitter
.GetSetterAccessibility(Item
), propertyAccessibility
, false);
143 if (target
.RelationshipMultiplicity
!= RelationshipMultiplicity
.Many
)
146 // insert user-supplied Set code here, before the assignment
148 List
<CodeStatement
> additionalSetStatements
= eventArgs
.AdditionalSetStatements
;
149 if (additionalSetStatements
!= null && additionalSetStatements
.Count
> 0)
153 property
.SetStatements
.AddRange(additionalSetStatements
.ToArray());
155 catch (ArgumentNullException ex
)
157 Generator
.AddError(Strings
.InvalidSetStatementSuppliedForProperty(Item
.Name
),
158 ModelBuilderErrorCode
.InvalidSetStatementSuppliedForProperty
,
159 EdmSchemaErrorSeverity
.Error
,
164 CodeExpression valueRef
= new CodePropertySetValueReferenceExpression();
165 if(typeRef
!= eventArgs
.ReturnType
)
167 // we need to cast to the actual type
168 valueRef
= new CodeCastExpression(typeRef
, valueRef
);
171 if (referenceProperty
)
174 // return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<TTargetEntity>("CSpaceQualifiedRelationshipName", "TargetRoleName");
175 getReturnExpression
= getMethod
;
178 // if (value != null)
180 // ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedReference<TTargetEntity>"CSpaceQualifiedRelationshipName", "TargetRoleName", value);
183 CodeMethodReferenceExpression initReferenceMethod
= new CodeMethodReferenceExpression();
184 initReferenceMethod
.MethodName
= "InitializeRelatedReference";
186 initReferenceMethod
.TypeArguments
.Add(Generator
.GetLeastPossibleQualifiedTypeReference(GetEntityType(target
)));
187 initReferenceMethod
.TargetObject
= new CodePropertyReferenceExpression(
188 new CodeCastExpression(TypeReference
.IEntityWithRelationshipsTypeBaseClass
, ThisRef
),
189 "RelationshipManager");
191 // relationships aren't backed by types so we won't map the namespace
192 // or we can't find the relationship again later
193 string cspaceNamespaceNameQualifiedRelationshipName
= target
.DeclaringType
.FullName
;
195 property
.SetStatements
.Add(
196 new CodeConditionStatement(
197 EmitExpressionDoesNotEqualNull(valueRef
),
198 new CodeExpressionStatement(
199 new CodeMethodInvokeExpression(
200 initReferenceMethod
, new CodeExpression
[] {
201 new CodePrimitiveExpression(cspaceNamespaceNameQualifiedRelationshipName
), new CodePrimitiveExpression(target
.Name
), valueRef
}))));
205 CodePropertyReferenceExpression valueProperty
= new CodePropertyReferenceExpression(getMethod
, ValuePropertyName
);
208 // return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<TTargetEntity>("CSpaceQualifiedRelationshipName", "TargetRoleName").Value;
209 getReturnExpression
= valueProperty
;
212 // ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<TTargetEntity>("CSpaceQualifiedRelationshipName", "TargetRoleName").Value = value;
213 property
.SetStatements
.Add(
214 new CodeAssignStatement(valueProperty
, valueRef
));
220 // return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<TTargetEntity>("CSpaceQualifiedRelationshipName", "TargetRoleName");
221 getReturnExpression
= getMethod
;
224 // if (value != null)
226 // ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<TTargetEntity>"CSpaceQualifiedRelationshipName", "TargetRoleName", value);
228 CodeExpression valueRef
= new CodePropertySetValueReferenceExpression();
230 CodeMethodReferenceExpression initCollectionMethod
= new CodeMethodReferenceExpression();
231 initCollectionMethod
.MethodName
= "InitializeRelatedCollection";
233 initCollectionMethod
.TypeArguments
.Add(Generator
.GetLeastPossibleQualifiedTypeReference(GetEntityType(target
)));
234 initCollectionMethod
.TargetObject
= new CodePropertyReferenceExpression(
235 new CodeCastExpression(TypeReference
.IEntityWithRelationshipsTypeBaseClass
, ThisRef
),
236 "RelationshipManager");
238 // relationships aren't backed by types so we won't map the namespace
239 // or we can't find the relationship again later
240 string cspaceNamespaceNameQualifiedRelationshipName
= target
.DeclaringType
.FullName
;
242 property
.SetStatements
.Add(
243 new CodeConditionStatement(
244 EmitExpressionDoesNotEqualNull(valueRef
),
245 new CodeExpressionStatement(
246 new CodeMethodInvokeExpression(
247 initCollectionMethod
, new CodeExpression
[] {
248 new CodePrimitiveExpression(cspaceNamespaceNameQualifiedRelationshipName
), new CodePrimitiveExpression(target
.Name
), valueRef
}))));
252 // if additional Get statements were specified by the event subscriber, insert them now
254 List
<CodeStatement
> additionalGetStatements
= eventArgs
.AdditionalGetStatements
;
255 if (additionalGetStatements
!= null && additionalGetStatements
.Count
> 0)
259 property
.GetStatements
.AddRange(additionalGetStatements
.ToArray());
261 catch (ArgumentNullException ex
)
263 Generator
.AddError(Strings
.InvalidGetStatementSuppliedForProperty(Item
.Name
),
264 ModelBuilderErrorCode
.InvalidGetStatementSuppliedForProperty
,
265 EdmSchemaErrorSeverity
.Error
,
270 property
.GetStatements
.Add(new CodeMethodReturnStatement(getReturnExpression
));
275 internal static bool IsNameAlreadyAMemberName(StructuralType type
, string generatedPropertyName
, StringComparison comparison
)
277 foreach (EdmMember member
in type
.Members
)
279 if (member
.DeclaringType
== type
&&
280 member
.Name
.Equals(generatedPropertyName
, comparison
))
289 private string GetFullyQualifiedPropertyName(string propertyName
)
291 return Item
.DeclaringType
.FullName
+ "." + propertyName
;
295 /// Gives the SchemaElement back cast to the most
298 private new NavigationProperty Item
302 return base.Item
as NavigationProperty
;
307 /// Get the return type for the get method, given the target end
309 /// <param name="target"></param>
310 /// <param name="referenceMethod">true if the is the return type for a reference property</param>
311 /// <returns>the return type for a target</returns>
312 private CodeTypeReference
GetReturnType(RelationshipEndMember target
, bool referenceMethod
)
314 CodeTypeReference returnType
= Generator
.GetLeastPossibleQualifiedTypeReference(GetEntityType(target
));
317 returnType
= TypeReference
.AdoFrameworkGenericDataClass("EntityReference", returnType
);
319 else if (target
.RelationshipMultiplicity
== RelationshipMultiplicity
.Many
)
321 returnType
= TypeReference
.AdoFrameworkGenericDataClass("EntityCollection", returnType
);
328 private static EntityTypeBase
GetEntityType(RelationshipEndMember endMember
)
330 Debug
.Assert(endMember
.TypeUsage
.EdmType
.BuiltInTypeKind
== BuiltInTypeKind
.RefType
, "not a reference type");
331 EntityTypeBase type
= ((RefType
)endMember
.TypeUsage
.EdmType
).ElementType
;
336 /// Emit the GetRelatedCollection or GetRelatedReference methods
338 /// <param name="target">Target end of the relationship</param>
339 /// <returns>Expression to invoke the appropriate method</returns>
340 private CodeMethodInvokeExpression
EmitGetMethod(RelationshipEndMember target
)
342 // ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<TargetType>("CSpaceQualifiedRelationshipName", "TargetRoleName");
344 // ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<TargetType>("CSpaceQualifiedRelationshipName", "TargetRoleName");
346 CodeMethodReferenceExpression getMethod
= new CodeMethodReferenceExpression();
347 if (target
.RelationshipMultiplicity
!= RelationshipMultiplicity
.Many
)
348 getMethod
.MethodName
= "GetRelatedReference";
350 getMethod
.MethodName
= "GetRelatedCollection";
352 getMethod
.TypeArguments
.Add(Generator
.GetLeastPossibleQualifiedTypeReference(GetEntityType(target
)));
353 getMethod
.TargetObject
= new CodePropertyReferenceExpression(
354 new CodeCastExpression(TypeReference
.IEntityWithRelationshipsTypeBaseClass
, ThisRef
),
355 "RelationshipManager");
357 // relationships aren't backed by types so we won't map the namespace
358 // or we can't find the relationship again later
359 string cspaceNamespaceNameQualifiedRelationshipName
= target
.DeclaringType
.FullName
;
360 return new CodeMethodInvokeExpression(
361 getMethod
, new CodeExpression
[] { new CodePrimitiveExpression(cspaceNamespaceNameQualifiedRelationshipName), new CodePrimitiveExpression(target.Name)}
);