1 //---------------------------------------------------------------------
2 // <copyright file="StructuredTypeEmitter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System.Collections;
11 using System.Collections.Generic;
14 using System.Data.EntityModel.SchemaObjectModel;
15 using Som = System.Data.EntityModel.SchemaObjectModel;
16 using System.Data.Entity.Design;
17 using System.Data.Metadata.Edm;
18 using System.Diagnostics;
19 using System.Reflection;
20 using System.Data.Objects.DataClasses;
21 using System.Data.Entity.Design.Common;
22 using System.Data.Entity.Design.SsdlGenerator;
25 namespace System.Data.EntityModel.Emitters
28 /// Summary description for StructuredTypeEmitter.
30 internal abstract class StructuredTypeEmitter : SchemaTypeEmitter
32 #region Public Methods
33 private bool _usingStandardBaseClass = true;
40 /// <returns></returns>
41 public override CodeTypeDeclarationCollection EmitApiClass()
43 Validate(); // emitter-specific validation
45 CodeTypeReference baseType = this.GetBaseType();
47 // raise the TypeGenerated event
48 TypeGeneratedEventArgs eventArgs = new TypeGeneratedEventArgs(Item, baseType);
49 this.Generator.RaiseTypeGeneratedEvent(eventArgs);
51 // public [abstract] partial class ClassName
52 CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(Item.Name);
53 typeDecl.IsPartial = true;
54 typeDecl.TypeAttributes = System.Reflection.TypeAttributes.Class;
57 typeDecl.TypeAttributes |= System.Reflection.TypeAttributes.Abstract;
60 SetTypeVisibility(typeDecl);
62 EmitTypeAttributes(Item.Name, typeDecl, eventArgs.AdditionalAttributes);
65 AssignBaseType(typeDecl, baseType, eventArgs.BaseType);
67 AddInterfaces(Item.Name, typeDecl, eventArgs.AdditionalInterfaces);
69 CommentEmitter.EmitSummaryComments(Item, typeDecl.Comments);
71 // Since abstract types cannot be instantiated, skip the factory method for abstract types
72 if ( (typeDecl.TypeAttributes & System.Reflection.TypeAttributes.Abstract) == 0)
73 EmitFactoryMethod(typeDecl);
75 EmitProperties(typeDecl);
77 // additional members, if provided by the event subscriber
78 this.AddMembers(Item.Name, typeDecl, eventArgs.AdditionalMembers);
80 CodeTypeDeclarationCollection typeDecls = new CodeTypeDeclarationCollection();
81 typeDecls.Add(typeDecl);
87 #region Protected Methods
92 /// <returns></returns>
93 protected virtual CodeTypeReference GetBaseType()
95 if (Item.BaseType == null)
98 return Generator.GetLeastPossibleQualifiedTypeReference(Item.BaseType);
104 /// <param name="generator"></param>
105 /// <param name="structuredType"></param>
106 protected StructuredTypeEmitter(ClientApiGenerator generator, StructuralType structuralType)
107 : base(generator, structuralType)
114 /// <param name="typeDecl"></param>
115 protected override void EmitTypeAttributes(CodeTypeDeclaration typeDecl)
117 Generator.AttributeEmitter.EmitTypeAttributes(this, typeDecl);
118 base.EmitTypeAttributes(typeDecl);
124 /// <param name="typeDecl"></param>
125 protected virtual void EmitProperties(CodeTypeDeclaration typeDecl)
128 foreach (EdmProperty property in Item.GetDeclaredOnlyMembers<EdmProperty>())
130 PropertyEmitter propertyEmitter = new PropertyEmitter(Generator, property, _usingStandardBaseClass);
131 propertyEmitter.Emit(typeDecl);
135 protected abstract ReadOnlyMetadataCollection<EdmProperty> GetProperties();
138 /// Emit static factory method which creates an instance of the class and initializes
139 /// non-nullable properties (taken as arguments)
141 /// <param name="typeDecl"></param>
142 protected virtual void EmitFactoryMethod(CodeTypeDeclaration typeDecl)
146 // build list of non-nullable properties
147 ReadOnlyMetadataCollection<EdmProperty> properties = GetProperties();
148 List<EdmProperty> parameters = new List<EdmProperty>(properties.Count);
149 foreach (EdmProperty property in properties)
151 bool include = IncludeFieldInFactoryMethod(property);
154 parameters.Add(property);
158 // if there are no parameters, we don't emit anything (1 is for the null element)
159 // nor do we emit everything if this is the Ref propertied ctor and the parameter list is the same as the many parametered ctor
160 if (parameters.Count < 1)
165 CodeMemberMethod method = new CodeMemberMethod();
166 Generator.AttributeEmitter.EmitGeneratedCodeAttribute(method);
168 CodeTypeReference typeRef = TypeReference.FromString(Item.Name);
169 UniqueIdentifierService uniqueIdentifierService = new UniqueIdentifierService(Generator.IsLanguageCaseSensitive, name => Utils.FixParameterName(name));
170 string instanceName = uniqueIdentifierService.AdjustIdentifier(Item.Name);
172 // public static Class CreateClass(...)
173 method.Attributes = MemberAttributes.Static|MemberAttributes.Public;
174 method.Name = "Create" + Item.Name;
175 if (NavigationPropertyEmitter.IsNameAlreadyAMemberName(Item, method.Name, Generator.LanguageAppropriateStringComparer))
177 Generator.AddError(Strings.GeneratedFactoryMethodNameConflict(method.Name, Item.Name),
178 ModelBuilderErrorCode.GeneratedFactoryMethodNameConflict,
179 EdmSchemaErrorSeverity.Error, Item.FullName);
182 method.ReturnType = typeRef;
184 // output method summary comments
185 CommentEmitter.EmitSummaryComments(Strings.FactoryMethodSummaryComment(Item.Name), method.Comments);
188 // Class class = new Class();
189 CodeVariableDeclarationStatement createNewInstance = new CodeVariableDeclarationStatement(
190 typeRef, instanceName, new CodeObjectCreateExpression(typeRef));
191 method.Statements.Add(createNewInstance);
192 CodeVariableReferenceExpression instanceRef = new CodeVariableReferenceExpression(instanceName);
194 // iterate over the properties figuring out which need included in the factory method
195 foreach (EdmProperty property in parameters)
197 // CreateClass( ... , propType propName ...)
198 PropertyEmitter propertyEmitter = new PropertyEmitter(Generator, property, UsingStandardBaseClass);
199 CodeTypeReference propertyTypeReference = propertyEmitter.PropertyType;
200 String parameterName = uniqueIdentifierService.AdjustIdentifier(propertyEmitter.PropertyName);
201 CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression(
202 propertyTypeReference, parameterName);
203 CodeArgumentReferenceExpression paramRef = new CodeArgumentReferenceExpression(paramDecl.Name);
204 method.Parameters.Add(paramDecl);
206 // add comment describing the parameter
207 CommentEmitter.EmitParamComments(paramDecl, Strings.FactoryParamCommentGeneral(propertyEmitter.PropertyName), method.Comments);
209 CodeExpression newPropertyValue;
210 if (MetadataUtil.IsComplexType(propertyEmitter.Item.TypeUsage.EdmType))
212 List<CodeExpression> complexVerifyParameters = new List<CodeExpression>();
213 complexVerifyParameters.Add(paramRef);
214 complexVerifyParameters.Add(new CodePrimitiveExpression(propertyEmitter.PropertyName));
217 new CodeMethodInvokeExpression(
218 PropertyEmitter.CreateEdmStructuralObjectRef(TypeReference),
219 Utils.VerifyComplexObjectIsNotNullName,
220 complexVerifyParameters.ToArray());
224 newPropertyValue = paramRef;
230 // Property = StructuralObject.VerifyComplexObjectIsNotNull(param, propertyName);
232 method.Statements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(instanceRef, propertyEmitter.PropertyName), newPropertyValue));
236 method.Statements.Add(new CodeMethodReturnStatement(instanceRef));
238 // actually add the method to the class
239 typeDecl.Members.Add(method);
245 #region Protected Properties
247 internal new StructuralType Item
251 return base.Item as StructuralType;
255 protected bool UsingStandardBaseClass
257 get { return _usingStandardBaseClass; }
262 #region Private Methods
267 /// <param name="property"></param>
268 /// <returns></returns>
269 private bool IncludeFieldInFactoryMethod(EdmProperty property)
271 if (property.Nullable)
276 if (PropertyEmitter.HasDefault(property))
281 if ((PropertyEmitter.GetGetterAccessibility(property) != MemberAttributes.Public &&
282 PropertyEmitter.GetSetterAccessibility(property) != MemberAttributes.Public) ||
283 // declared in a sub type, but not setter accessbile from this type
284 (Item != property.DeclaringType && PropertyEmitter.GetSetterAccessibility(property) == MemberAttributes.Private)
293 private void AssignBaseType(CodeTypeDeclaration typeDecl,
294 CodeTypeReference baseType,
295 CodeTypeReference eventReturnedBaseType)
297 if (eventReturnedBaseType != null && !eventReturnedBaseType.Equals(baseType))
299 _usingStandardBaseClass = false;
300 typeDecl.BaseTypes.Add(eventReturnedBaseType);
304 if (baseType != null)
306 typeDecl.BaseTypes.Add(baseType);