1 //---------------------------------------------------------------------
2 // <copyright file="NamespaceEmitter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 //---------------------------------------------------------------------
13 using Som = System.Data.EntityModel.SchemaObjectModel;
14 using System.Collections.Generic;
15 using System.Data.Entity.Design;
16 using System.Data.Metadata.Edm;
17 using System.Collections.ObjectModel;
18 using System.Diagnostics;
19 using System.Data.Entity.Design.SsdlGenerator;
20 using System.Data.Entity.Design.Common;
23 namespace System.Data.EntityModel.Emitters
26 /// This class is responsible for Emitting the code to create the CLR namespace container and assembly level attributes
28 internal sealed class NamespaceEmitter : Emitter
31 private static Pair<Type, CreateEmitter>[] EmitterCreators = new Pair<Type, CreateEmitter>[]
33 new Pair<Type,CreateEmitter>(typeof(EntityType), delegate (ClientApiGenerator generator, GlobalItem element) { return new EntityTypeEmitter(generator,(EntityType)element); }),
34 new Pair<Type,CreateEmitter>(typeof(ComplexType), delegate (ClientApiGenerator generator, GlobalItem element) { return new ComplexTypeEmitter(generator,(ComplexType)element); }),
35 new Pair<Type,CreateEmitter>(typeof(EntityContainer), delegate (ClientApiGenerator generator, GlobalItem element) { return new EntityContainerEmitter(generator,(EntityContainer)element); }),
36 new Pair<Type,CreateEmitter>(typeof(AssociationType), delegate (ClientApiGenerator generator, GlobalItem element) { return new AssociationTypeEmitter(generator,(AssociationType)element); }),
40 #region Private Fields
42 private string _codeNamespace = null;
43 private string _targetFilePath = null;
46 #region Public Methods
50 /// <param name="generator"></param>
51 public NamespaceEmitter(ClientApiGenerator generator, string codeNamespace, string targetFilePath)
54 _codeNamespace = codeNamespace;
55 _targetFilePath = targetFilePath != null ? targetFilePath : string.Empty;
59 /// Creates the CodeTypeDeclarations necessary to generate the code
63 // it is a valid scenario for namespaceName to be empty
64 string namespaceName = Generator.SourceObjectNamespaceName;
66 // emit the namespace definition
67 CodeNamespace codeNamespace = new CodeNamespace( namespaceName );
69 // output some boiler plate comments
70 string comments = Strings.NamespaceComments(
71 System.IO.Path.GetFileName( _targetFilePath ),
72 DateTime.Now.ToString( System.Globalization.CultureInfo.CurrentCulture ));
73 CommentEmitter.EmitComments( CommentEmitter.GetFormattedLines( comments, false ), codeNamespace.Comments, false );
74 CompileUnit.Namespaces.Add( codeNamespace );
76 // Add the assembly attribute.
77 CodeAttributeDeclaration assemblyAttribute;
78 // SQLBUDT 505339: VB compiler fails if multiple assembly attributes exist in the same project.
79 // This adds a GUID to the assembly attribute so that each generated file will have a unique EdmSchemaAttribute in VB.
80 if (this.Generator.Language == System.Data.Entity.Design.LanguageOption.GenerateVBCode) //The GUID is only added in VB
82 assemblyAttribute = AttributeEmitter.EmitSimpleAttribute("System.Data.Objects.DataClasses.EdmSchemaAttribute", System.Guid.NewGuid().ToString());
86 assemblyAttribute = AttributeEmitter.EmitSimpleAttribute("System.Data.Objects.DataClasses.EdmSchemaAttribute");
88 CompileUnit.AssemblyCustomAttributes.Add(assemblyAttribute);
90 Dictionary<string, string> usedClassName = new Dictionary<string, string>(StringComparer.Ordinal);
91 // Emit the classes in the schema
92 foreach (GlobalItem element in Generator.GetSourceTypes())
94 Debug.Assert(!(element is EdmFunction), "Are you trying to emit functions now? If so add an emitter for it.");
96 if (AddElementNameToCache(element, usedClassName))
98 SchemaTypeEmitter emitter = CreateElementEmitter(element);
99 CodeTypeDeclarationCollection typeDeclaration = emitter.EmitApiClass();
100 if (typeDeclaration.Count > 0)
102 codeNamespace.Types.AddRange(typeDeclaration);
111 #region Private Properties
113 /// Gets the compile unit (top level codedom object)
116 private CodeCompileUnit CompileUnit
120 return Generator.CompileUnit;
125 private bool AddElementNameToCache(GlobalItem element, Dictionary<string, string> cache)
127 if (element.BuiltInTypeKind == BuiltInTypeKind.EntityContainer)
129 return TryAddNameToCache((element as EntityContainer).Name, element.BuiltInTypeKind.ToString(), cache);
131 else if (element.BuiltInTypeKind == BuiltInTypeKind.EntityType ||
132 element.BuiltInTypeKind == BuiltInTypeKind.ComplexType ||
133 element.BuiltInTypeKind == BuiltInTypeKind.AssociationType)
135 return TryAddNameToCache((element as StructuralType).Name, element.BuiltInTypeKind.ToString(), cache);
140 private bool TryAddNameToCache(string name, string type, Dictionary<string, string> cache)
142 if (!cache.ContainsKey(name))
144 cache.Add(name, type);
148 this.Generator.AddError(Strings.DuplicateClassName(type, name, cache[name]), ModelBuilderErrorCode.DuplicateClassName,
149 EdmSchemaErrorSeverity.Error, name);
156 /// Create an Emitter for a schema type element
158 /// <param name="element"></param>
159 /// <returns></returns>
160 private SchemaTypeEmitter CreateElementEmitter( GlobalItem element )
162 Type typeOfElement = element.GetType();
163 foreach ( Pair<Type, CreateEmitter> pair in EmitterCreators )
165 if ( pair.First.IsAssignableFrom( typeOfElement ) )
166 return pair.Second( Generator, element );
171 private delegate SchemaTypeEmitter CreateEmitter( ClientApiGenerator generator, GlobalItem item );
174 /// Reponsible for relating two objects together into a pair
176 /// <typeparam name="T1"></typeparam>
177 /// <typeparam name="T2"></typeparam>
178 private class Pair<T1, T2>
182 internal Pair( T1 first, T2 second )