Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity.Design / System / Data / EntityModel / Emitters / NavigationPropertyEmitter.cs
blobef0104ca7a2acaaa08e6556eb2b209fa7b89ed37
1 //---------------------------------------------------------------------
2 // <copyright file="NavigationPropertyEmitter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System;
11 using System.CodeDom;
12 using System.Data;
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
24 /// <summary>
25 /// Summary description for NavigationPropertyEmitter.
26 /// </summary>
27 internal sealed class NavigationPropertyEmitter : PropertyEmitterBase
29 private const string ValuePropertyName = "Value";
31 /// <summary>
32 ///
33 /// </summary>
34 /// <param name="generator"></param>
35 /// <param name="navigationProperty"></param>
36 public NavigationPropertyEmitter(ClientApiGenerator generator, NavigationProperty navigationProperty, bool declaringTypeUsesStandardBaseType)
37 : base(generator, navigationProperty, declaringTypeUsesStandardBaseType)
41 /// <summary>
42 /// Generate the navigation property
43 /// </summary>
44 /// <param name="typeDecl">The type to add the property to.</param>
45 protected override void EmitProperty(CodeTypeDeclaration typeDecl)
47 EmitNavigationProperty(typeDecl);
50 /// <summary>
51 /// Generate the navigation property specified
52 /// </summary>
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);
69 /// <summary>
70 /// Generate a navigation property
71 /// </summary>
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
82 typeRef);
83 this.Generator.RaisePropertyGeneratedEvent(eventArgs);
85 // [System.ComponentModel.Browsable(false)]
86 // public TargetType TargetName
87 // public EntityReference<TargetType> TargetName
88 // or
89 // public EntityCollection<targetType> TargetNames
90 CodeMemberProperty property = new CodeMemberProperty();
91 if (referenceProperty)
93 AttributeEmitter.AddBrowsableAttribute(property);
94 Generator.AttributeEmitter.EmitGeneratedCodeAttribute(property);
96 else
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;
125 else
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,
160 ex);
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)
173 // get
174 // return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<TTargetEntity>("CSpaceQualifiedRelationshipName", "TargetRoleName");
175 getReturnExpression = getMethod;
177 // set
178 // if (value != null)
179 // {
180 // ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedReference<TTargetEntity>"CSpaceQualifiedRelationshipName", "TargetRoleName", value);
181 // }
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}))));
203 else
205 CodePropertyReferenceExpression valueProperty = new CodePropertyReferenceExpression(getMethod, ValuePropertyName);
207 // get
208 // return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<TTargetEntity>("CSpaceQualifiedRelationshipName", "TargetRoleName").Value;
209 getReturnExpression = valueProperty;
211 // set
212 // ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<TTargetEntity>("CSpaceQualifiedRelationshipName", "TargetRoleName").Value = value;
213 property.SetStatements.Add(
214 new CodeAssignStatement(valueProperty, valueRef));
217 else
219 // get
220 // return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<TTargetEntity>("CSpaceQualifiedRelationshipName", "TargetRoleName");
221 getReturnExpression = getMethod;
223 // set
224 // if (value != null)
225 // {
226 // ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<TTargetEntity>"CSpaceQualifiedRelationshipName", "TargetRoleName", value);
227 // }
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,
266 ex);
270 property.GetStatements.Add(new CodeMethodReturnStatement(getReturnExpression));
272 return property;
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))
282 return true;
286 return false;
289 private string GetFullyQualifiedPropertyName(string propertyName)
291 return Item.DeclaringType.FullName + "." + propertyName;
294 /// <summary>
295 /// Gives the SchemaElement back cast to the most
296 /// appropriate type
297 /// </summary>
298 private new NavigationProperty Item
302 return base.Item as NavigationProperty;
306 /// <summary>
307 /// Get the return type for the get method, given the target end
308 /// </summary>
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));
315 if (referenceMethod)
317 returnType = TypeReference.AdoFrameworkGenericDataClass("EntityReference", returnType);
319 else if (target.RelationshipMultiplicity == RelationshipMultiplicity.Many)
321 returnType = TypeReference.AdoFrameworkGenericDataClass("EntityCollection", returnType);
324 return 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;
332 return type;
335 /// <summary>
336 /// Emit the GetRelatedCollection or GetRelatedReference methods
337 /// </summary>
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");
343 // or
344 // ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<TargetType>("CSpaceQualifiedRelationshipName", "TargetRoleName");
346 CodeMethodReferenceExpression getMethod = new CodeMethodReferenceExpression();
347 if (target.RelationshipMultiplicity != RelationshipMultiplicity.Many)
348 getMethod.MethodName = "GetRelatedReference";
349 else
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)});