Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity.Design / System / Data / Entity / Design / EntityViewGeneration / EntityViewGenerator.cs
1 //---------------------------------------------------------------------
2 // <copyright file="EntityViewGenerator.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 using System;
11 using System.CodeDom;
12 using System.CodeDom.Compiler;
13 using System.IO;
14 using System.Collections.Generic;
15 using System.Text;
16 using System.Data;
17 using System.Data.SqlClient;
18 using System.Data.Metadata.Edm;
19 using System.Data.Mapping;
20 using System.Data.EntityClient;
21 using System.Data.EntityModel;
22 using System.Data.Common.CommandTrees;
23 using System.Reflection;
24 using System.Security.Cryptography;
25 using System.Data.Entity.Design.Common;
26 using System.Diagnostics;
27 using System.Globalization;
28 using System.Data.Common.Utils;
29 using Microsoft.Build.Utilities;
30 using System.Runtime.Versioning;
31 using System.Linq;
32
33 namespace System.Data.Entity.Design
34 {
35     /// <summary>
36     /// EntityViewGenerator class produces the views for the extents in the passed in StorageMappingItemCollection.
37     /// The views are written as code to the passed in output stream. There are a set of options that user
38     /// can use to control the code generation process. The options should be apssed into the constrcutor.
39     /// While storing the views in the code, the view generator class also stores a Hash value produced based
40     /// on the content of the views and the names of extents. We also generate a hash for each schema file( csdl, ssdl and msl) 
41     /// that was used in view generation process and store the hash in the generated code.The entity runtime will try to discover this
42     /// type and if it does discover it will use the generated views in this type. The discovery process is
43     /// explained in detail in the comments for StorageMappingItemCollection class. 
44     /// The runtime will throw an exception if any of the the hash values produced in the design time does not match
45     /// the hash values produced at the runtime.
46     /// </summary>
47     public class EntityViewGenerator
48     {
49         #region Constructors
50         /// <summary>
51         /// Create the instance of ViewGenerator with the given language option.
52         /// </summary>
53         /// <param name="languageOption">Language Option for generated code.</param>
54         public EntityViewGenerator(LanguageOption languageOption)
55         {
56             m_languageOption = EDesignUtil.CheckLanguageOptionArgument(languageOption, "languageOption");
57         }
58
59         /// <summary>
60         /// Create the instance of ViewGenerator using C# as the default 
61         /// language option.
62         /// </summary>
63         public EntityViewGenerator()
64             : this(LanguageOption.GenerateCSharpCode)
65         {
66         }
67
68         #endregion
69
70         #region Fields
71         private LanguageOption m_languageOption;
72         private static readonly int MAXONELINELENGTH = 2046;
73         private static readonly int ONELINELENGTH = 80;
74         private static readonly int ERRORCODE_MAPPINGALLQUERYVIEWATCOMPILETIME = 2088;
75         #endregion
76
77         #region Properties
78         /// <summary>
79         /// Language Option for generated code.
80         /// </summary>
81         public LanguageOption LanguageOption
82         {
83             get { return m_languageOption; }
84             set { m_languageOption = EDesignUtil.CheckLanguageOptionArgument(value, "value"); }
85         }
86         #endregion
87
88         #region Methods
89         /// <summary>
90         /// Generates the views for the extents in the mapping item collection and produces
91         /// the code for a type that will cache these views. The methods also produces
92         /// a hash based on the StorageEntityContainerMapping, which contains all the 
93         /// metadata and mapping. It also produces a hash based
94         /// on the view content and the name of the extents.
95         /// </summary>
96         /// <param name="mappingCollection">Mapping Item Collection for which views should be generated</param>
97         /// <param name="outputUri">Uri to which generated code needs to be written</param>
98         [ResourceExposure(ResourceScope.Machine)] //Exposes the outputPath as part of ConnectionString which are a Machine resource.
99         [ResourceConsumption(ResourceScope.Machine)] //For StreamWriter constructor call. But the path to the stream is not created in this method.
100         [CLSCompliant(false)]
101         public IList<EdmSchemaError> GenerateViews(StorageMappingItemCollection mappingCollection, string outputPath)
102         {
103             EDesignUtil.CheckArgumentNull(mappingCollection, "mappingCollection");
104             EDesignUtil.CheckStringArgument(outputPath, "outputPath");
105
106             TextWriter outputWriter = null;
107             try
108             {
109                 return InternalGenerateViews(mappingCollection, () => new StreamWriter(outputPath), out outputWriter);
110             }
111             finally
112             {
113                 if (outputWriter != null)
114                 {
115                     outputWriter.Dispose();
116                 }
117             }
118         }
119
120         /// <summary>
121         /// Generates the views for the extents in the mapping item collection and produces
122         /// the code for a type that will cache these views. The methods also produces
123         /// a hash based on the storageEntityContainerMapping object, which contains all the 
124         /// metadata and mapping. It also produces a hash based
125         /// on the view content and the name of the extents.
126         /// </summary>
127         /// <param name="mappingCollection">Mapping Item Collection for which views should be generated</param>
128         /// <param name="outputWriter">Output writer to which we want to write the code</param>
129         [CLSCompliant(false)]
130         public IList<EdmSchemaError> GenerateViews(StorageMappingItemCollection mappingCollection, TextWriter outputWriter)
131         {
132             EDesignUtil.CheckArgumentNull(mappingCollection, "mappingCollection");
133
134             Version targetEntityFrameworkVersion;
135             IList<EdmSchemaError> errorList = GetMinimumTargetFrameworkVersion(mappingCollection, out targetEntityFrameworkVersion);
136
137             return GenerateViews(mappingCollection, outputWriter, targetEntityFrameworkVersion).Concat(errorList).ToList();
138         }
139
140         /// <summary>
141         /// Generates the views for the extents in the mapping item collection and produces
142         /// the code for a type that will cache these views. The methods also produces
143         /// a hash based on the storageEntityContainerMapping object, which contains all the 
144         /// metadata and mapping. It also produces a hash based
145         /// on the view content and the name of the extents.
146         /// </summary>
147         /// <param name="mappingCollection">Mapping Item Collection for which views should be generated</param>
148         /// <param name="outputWriter">Output writer to which we want to write the code</param>
149         [CLSCompliant(false)]
150         public IList<EdmSchemaError> GenerateViews(StorageMappingItemCollection mappingCollection, TextWriter outputWriter, Version targetEntityFrameworkVersion)
151         {
152             EDesignUtil.CheckArgumentNull(mappingCollection, "mappingCollection");
153             EDesignUtil.CheckArgumentNull(outputWriter, "outputWriter");
154
155             EDesignUtil.CheckTargetEntityFrameworkVersionArgument(targetEntityFrameworkVersion, "targetEntityFrameworkVersion");
156             CheckForCompatibleSchemaAndTarget(mappingCollection, targetEntityFrameworkVersion);
157
158             TextWriter writer;
159             return InternalGenerateViews(mappingCollection, () => outputWriter, out writer);
160         }
161
162         private static void CheckForCompatibleSchemaAndTarget(StorageMappingItemCollection mappingCollection, Version targetEntityFrameworkVersion)
163         {
164             Version mappingVersion = EntityFrameworkVersionsUtil.ConvertToVersion(mappingCollection.MappingVersion);
165             if (targetEntityFrameworkVersion < mappingVersion)
166             {
167                 throw EDesignUtil.Argument(Strings.TargetVersionSchemaVersionMismatch(targetEntityFrameworkVersion, mappingVersion), null);
168             }
169
170             Version edmVersion = EntityFrameworkVersionsUtil.ConvertToVersion(mappingCollection.EdmItemCollection.EdmVersion);
171             if (targetEntityFrameworkVersion < edmVersion)
172             {
173                 throw EDesignUtil.Argument(Strings.TargetVersionSchemaVersionMismatch(targetEntityFrameworkVersion, edmVersion), null);
174             }
175         }
176
177         private IList<EdmSchemaError> InternalGenerateViews(
178             StorageMappingItemCollection mappingCollection,
179             Func<TextWriter> GetWriter,
180             out TextWriter outputWriter)
181         {
182             IList<EdmSchemaError> schemaErrors;
183             CodeDomProvider provider;
184             Dictionary<EntitySetBase, string> generatedViews;
185             if (GetViewsWithErrors(mappingCollection, out provider, out schemaErrors, out generatedViews))
186             {
187                 outputWriter = null;
188                 return schemaErrors;
189             }
190
191             outputWriter = GetWriter();
192             GenerateAndStoreViews(mappingCollection, generatedViews,
193                 outputWriter, provider, schemaErrors);
194             
195             return schemaErrors;
196         }
197
198         /// <summary>
199         /// Validates the mappingCollections and returns the schemaErrors.
200         /// </summary>
201         /// <param name="mappingCollection"></param>
202         /// <returns>list of EdmSchemaError</returns>
203         [CLSCompliant(false)]
204         public static IList<EdmSchemaError> Validate(StorageMappingItemCollection mappingCollection)
205         {
206             EDesignUtil.CheckArgumentNull(mappingCollection, "mappingCollection");
207
208             Version targetEntityFrameworkVersion;
209             IList<EdmSchemaError> errorList = GetMinimumTargetFrameworkVersion(mappingCollection, out targetEntityFrameworkVersion);
210
211             return Validate(mappingCollection, targetEntityFrameworkVersion).Concat(errorList).ToList();
212         }
213         
214         /// <summary>
215         /// Validates the mappingCollections and returns the schemaErrors.
216         /// </summary>
217         /// <param name="mappingCollection"></param>
218         /// <returns>list of EdmSchemaError</returns>
219         [CLSCompliant(false)]
220         public static IList<EdmSchemaError> Validate(StorageMappingItemCollection mappingCollection, Version targetEntityFrameworkVersion)
221         {
222             EDesignUtil.CheckTargetEntityFrameworkVersionArgument(targetEntityFrameworkVersion, "targetEntityFrameworkVersion");
223             CheckForCompatibleSchemaAndTarget(mappingCollection, targetEntityFrameworkVersion);
224
225             // purpose of this API is to validate the mappingCollection, it basically will call GetEntitySetViews
226
227             EDesignUtil.CheckArgumentNull(mappingCollection, "mappingCollection");
228
229             // we need a temp var to to pass it to GetViews (since we will directly invoke GetViews)
230             Dictionary<EntitySetBase, string> generatedViews;
231             // mappingCollection will be validated and schemaErrors will be returned from GetViews API
232             IList<EdmSchemaError> schemaErrors;
233
234             // Validate entity set views.
235             GetEntitySetViews(mappingCollection, out schemaErrors, out generatedViews);
236
237             // Validate function imports and their mapping.
238             foreach (var containerMapping in mappingCollection.GetItems<StorageEntityContainerMapping>())
239             {
240                 foreach (var functionImport in containerMapping.EdmEntityContainer.FunctionImports)
241                 {
242                     FunctionImportMapping functionImportMapping;
243                     if (containerMapping.TryGetFunctionImportMapping(functionImport, out functionImportMapping))
244                     {
245                         if (functionImport.IsComposableAttribute)
246                         {
247                             ((FunctionImportMappingComposable)functionImportMapping).ValidateFunctionView(schemaErrors);
248                         }
249                     }
250                     else
251                     {
252                         schemaErrors.Add(new EdmSchemaError(
253                             Strings.UnmappedFunctionImport(functionImport.Identity),
254                             (int)StorageMappingErrorCode.UnmappedFunctionImport,
255                             EdmSchemaErrorSeverity.Warning));
256                     }
257                 }
258             }
259
260             Debug.Assert(schemaErrors != null, "schemaErrors is null");
261             return HandleValidationErrors(schemaErrors);
262         }
263
264         private static IList<EdmSchemaError> HandleValidationErrors(IList<EdmSchemaError> schemaErrors)
265         {
266             IEnumerable<EdmSchemaError> warningsToRemove = 
267                 schemaErrors.Where(s => 
268                     s.ErrorCode == ERRORCODE_MAPPINGALLQUERYVIEWATCOMPILETIME); // When EntityContainerMapping only has QueryView, the mapping is valid
269
270             return schemaErrors.Except(warningsToRemove).ToList();
271         }
272
273         private bool GetViewsWithErrors(StorageMappingItemCollection mappingCollection, out CodeDomProvider provider, out IList<EdmSchemaError> schemaErrors, out Dictionary<EntitySetBase, string> generatedViews)
274         {
275             GetViewsAndCodeDomProvider(mappingCollection, out provider, out schemaErrors, out generatedViews);
276             //If the generated views are empty because of errors or warnings, then don't create an output file.
277             if (generatedViews.Count == 0 && schemaErrors.Count > 0)
278             {                
279                 return true;
280             }
281             return false;
282         }
283
284         private void GetViewsAndCodeDomProvider(StorageMappingItemCollection mappingCollection, out CodeDomProvider provider, out IList<EdmSchemaError> schemaErrors, out Dictionary<EntitySetBase, string> generatedViews)
285         {
286             //Create a CodeDomProvider based on options.
287             provider = null;
288             switch (m_languageOption)
289             {
290                 case LanguageOption.GenerateCSharpCode:
291                     provider = new Microsoft.CSharp.CSharpCodeProvider();
292                     break;
293
294                 case LanguageOption.GenerateVBCode:
295                     provider = new Microsoft.VisualBasic.VBCodeProvider();
296                     break;
297             }
298
299             //Get the views for the Entity Sets and Association Sets in the mapping item collection
300             GetEntitySetViews(mappingCollection, out schemaErrors, out generatedViews);
301         }
302
303         private static void GetEntitySetViews(StorageMappingItemCollection mappingCollection,
304             out IList<EdmSchemaError> schemaErrors, out Dictionary<EntitySetBase, string> generatedViews)
305         {
306             generatedViews = mappingCollection.GenerateEntitySetViews(out schemaErrors);
307         }
308         
309         private static IList<EdmSchemaError> GetMinimumTargetFrameworkVersion(StorageMappingItemCollection mappingCollection, out Version targetFrameworkVersion)
310         {
311             List<EdmSchemaError> errorList = new List<EdmSchemaError>();
312
313             targetFrameworkVersion = EntityFrameworkVersionsUtil.ConvertToVersion(mappingCollection.EdmItemCollection.EdmVersion);
314
315             if (targetFrameworkVersion > EntityFrameworkVersions.Default)
316             {
317                 errorList.Add(new EdmSchemaError(Strings.DefaultTargetVersionTooLow(EntityFrameworkVersions.Default, targetFrameworkVersion), (int)System.Data.Entity.Design.SsdlGenerator.ModelBuilderErrorCode.SchemaVersionHigherThanTargetVersion, EdmSchemaErrorSeverity.Warning));
318             }
319
320             return errorList;
321         }
322
323         /// <summary>
324         /// Generates the code to store the views in a C# or a VB file based on the
325         /// options passed in by the user.
326         /// </summary>
327         /// <param name="mappingCollection"></param>
328         /// <param name="generatedViews"></param>
329         /// <param name="sourceWriter"></param>
330         /// <param name="provider"></param>
331         /// <returns></returns>
332         private static void GenerateAndStoreViews(StorageMappingItemCollection mappingCollection,
333             Dictionary<EntitySetBase, string> generatedViews, TextWriter sourceWriter, CodeDomProvider provider, IList<EdmSchemaError> schemaErrors)
334         {
335             EdmItemCollection edmCollection = mappingCollection.EdmItemCollection;
336             StoreItemCollection storeCollection = mappingCollection.StoreItemCollection;
337
338             //Create an emtpty compile unit and build up the generated code
339             CodeCompileUnit compileUnit = new CodeCompileUnit();
340
341             //Add the namespace for generated code
342             CodeNamespace codeNamespace = new CodeNamespace(EntityViewGenerationConstants.NamespaceName);
343             //Add copyright notice to the namespace comment.
344             compileUnit.Namespaces.Add(codeNamespace);
345
346             foreach (var storageEntityContainerMapping in mappingCollection.GetItems<StorageEntityContainerMapping>())
347             {
348                 //Throw warning when containerMapping contains query view for bug 547285.
349                 if (HasQueryView(storageEntityContainerMapping))
350                 {
351                     schemaErrors.Add(new EdmSchemaError(
352                         Strings.UnsupportedQueryViewInEntityContainerMapping(storageEntityContainerMapping.Identity), 
353                         (int)StorageMappingErrorCode.UnsupportedQueryViewInEntityContainerMapping, 
354                         EdmSchemaErrorSeverity.Warning));
355                     continue;
356                 }
357
358                 #region Class Declaration
359
360                 string edmContainerName = storageEntityContainerMapping.EdmEntityContainer.Name;
361                 string storeContainerName = storageEntityContainerMapping.StorageEntityContainer.Name;
362
363                 string hashOverMappingClosure = MetadataMappingHasherVisitor.GetMappingClosureHash(edmCollection.EdmVersion, storageEntityContainerMapping);
364
365                 StringBuilder inputForTypeNameContent = new StringBuilder(hashOverMappingClosure);
366
367                 string viewStorageTypeName = EntityViewGenerationConstants.ViewGenerationTypeNamePrefix + StringHashBuilder.ComputeHash(MetadataHelper.CreateMetadataHashAlgorithm(edmCollection.EdmVersion),  inputForTypeNameContent.ToString()).ToUpperInvariant();
368
369                 //Add typeof expression to get the type that contains ViewGen type. This will help us in avoiding to go through 
370                 //all the types in the assembly. I have also verified that this works with VB with a root namespace prepended to the
371                 //namespace since VB is picking up the type correctly as long as it is in the same assembly even with out the root namespace.
372                 CodeTypeOfExpression viewGenTypeOfExpression = new CodeTypeOfExpression(EntityViewGenerationConstants.NamespaceName + "." + viewStorageTypeName);
373                 //Add the assembly attribute that marks the assembly as the one that contains the generated views
374                 CodeAttributeDeclaration viewGenAttribute = new CodeAttributeDeclaration(EntityViewGenerationConstants.ViewGenerationCustomAttributeName);
375                 CodeAttributeArgument viewGenTypeArgument = new CodeAttributeArgument(viewGenTypeOfExpression);
376                 viewGenAttribute.Arguments.Add(viewGenTypeArgument);
377                 compileUnit.AssemblyCustomAttributes.Add(viewGenAttribute);
378
379                 //Add the type which will be the class that contains all the views in this assembly
380                 CodeTypeDeclaration viewStoringType = CreateTypeForStoringViews(viewStorageTypeName);
381
382                 //Add the constructor, this will be the only method that this type will contain
383                 //Create empty constructor.
384                 CodeConstructor viewStoringTypeConstructor = CreateConstructorForViewStoringType();
385                 viewStoringType.Attributes = MemberAttributes.Public;
386
387
388                 //Get an expression that expresses this instance
389                 CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression();
390
391
392                 string viewHash = MetadataHelper.GenerateHashForAllExtentViewsContent(edmCollection.EdmVersion, GenerateDictionaryForEntitySetNameAndView(generatedViews));
393
394
395                 CodeAssignStatement EdmEntityContainerNameStatement = 
396                     new CodeAssignStatement(
397                         new CodeFieldReferenceExpression(thisRef, EntityViewGenerationConstants.EdmEntityContainerName), 
398                         new CodePrimitiveExpression(edmContainerName));
399                 CodeAssignStatement StoreEntityContainerNameStatement =
400                     new CodeAssignStatement(
401                         new CodeFieldReferenceExpression(thisRef, EntityViewGenerationConstants.StoreEntityContainerName),
402                         new CodePrimitiveExpression(storeContainerName));
403                 CodeAssignStatement HashOverMappingClosureStatement =
404                     new CodeAssignStatement(
405                         new CodeFieldReferenceExpression(thisRef, EntityViewGenerationConstants.HashOverMappingClosure),
406                         new CodePrimitiveExpression(hashOverMappingClosure));
407                 CodeAssignStatement HashOverAllExtentViewsStatement =
408                     new CodeAssignStatement(
409                         new CodeFieldReferenceExpression(thisRef, EntityViewGenerationConstants.HashOverAllExtentViews),
410                         new CodePrimitiveExpression(viewHash));
411                 CodeAssignStatement ViewCountStatement =
412                     new CodeAssignStatement(
413                         new CodeFieldReferenceExpression(thisRef, EntityViewGenerationConstants.ViewCountPropertyName),
414                         new CodePrimitiveExpression(generatedViews.Count));
415
416                 viewStoringTypeConstructor.Statements.Add(EdmEntityContainerNameStatement);
417                 viewStoringTypeConstructor.Statements.Add(StoreEntityContainerNameStatement);
418                 viewStoringTypeConstructor.Statements.Add(HashOverMappingClosureStatement);
419                 viewStoringTypeConstructor.Statements.Add(HashOverAllExtentViewsStatement);
420                 viewStoringTypeConstructor.Statements.Add(ViewCountStatement);
421
422                 //Add the constructor to the type
423                 viewStoringType.Members.Add(viewStoringTypeConstructor);
424                 //Add the method to store views to the type
425                 CreateAndAddGetViewAtMethod(viewStoringType, generatedViews);
426
427                 //Add the type to the namespace
428                 codeNamespace.Types.Add(viewStoringType);
429
430                 #endregion
431             }
432
433             if (codeNamespace.Types.Count > 0)
434             {
435                 GenerateCode(sourceWriter, provider, compileUnit);
436                 sourceWriter.Flush();
437             }
438         }
439
440         private static bool HasQueryView(StorageEntityContainerMapping storageEntityContainerMapping)
441         {
442             foreach (EntitySetBase extent in storageEntityContainerMapping.EdmEntityContainer.BaseEntitySets)
443             {
444                 if (storageEntityContainerMapping.HasQueryViewForSetMap(extent.Name))
445                 {
446                     return true;
447                 }
448             }
449             return false;
450         }
451
452         private static Dictionary<string, string> GenerateDictionaryForEntitySetNameAndView(Dictionary<EntitySetBase, string> dictionary)
453         {
454             Dictionary<string, string> newDictionary = new Dictionary<string, string>();
455             foreach (var item in dictionary)
456             {
457                 newDictionary.Add(GetExtentFullName(item.Key), item.Value);
458             }
459             return newDictionary;
460         }
461
462         /// <summary>
463         /// Write code to the given stream from the compile unit.
464         /// </summary>
465         /// <param name="sourceWriter"></param>
466         /// <param name="provider"></param>
467         /// <param name="compileUnit"></param>
468         private static void GenerateCode(TextWriter sourceWriter, CodeDomProvider provider, CodeCompileUnit compileUnit)
469         {
470             CodeGeneratorOptions styleOptions = new CodeGeneratorOptions();
471             styleOptions.BracingStyle = "C";
472             styleOptions.BlankLinesBetweenMembers = true;
473             styleOptions.VerbatimOrder = true;
474             provider.GenerateCodeFromCompileUnit(compileUnit, sourceWriter, styleOptions);
475         }
476
477         /// <summary>
478         /// Generate Code to put the views in the generated code.
479         /// </summary>
480         /// <param name="typeDeclaration"></param>
481         /// <param name="generatedViews"></param>
482         /// <returns></returns>
483         private static void CreateAndAddGetViewAtMethod(CodeTypeDeclaration typeDeclaration, Dictionary<EntitySetBase, string> generatedViews)
484         {
485
486             //Add the views to a method
487             CodeMemberMethod getViewAtMethod = new CodeMemberMethod();
488             getViewAtMethod.Name = EntityViewGenerationConstants.GetViewAtMethodName;
489             getViewAtMethod.ReturnType = new CodeTypeReference(typeof(KeyValuePair<,>).MakeGenericType(new Type[] { typeof(string), typeof(string) }));
490             CodeParameterDeclarationExpression parameter = new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(int)), "index");
491             getViewAtMethod.Parameters.Add(parameter);
492             getViewAtMethod.Comments.Add(new CodeCommentStatement(EntityViewGenerationConstants.SummaryStartElement, true /*docComment*/));
493             getViewAtMethod.Comments.Add(new CodeCommentStatement(Strings.GetViewAtMethodComments, true /*docComment*/));
494             getViewAtMethod.Comments.Add(new CodeCommentStatement(EntityViewGenerationConstants.SummaryEndElement, true /*docComment*/));
495
496             getViewAtMethod.Attributes = MemberAttributes.Family | MemberAttributes.Override;
497
498             typeDeclaration.Members.Add(getViewAtMethod);
499             
500             int index = 0;
501             CodeVariableReferenceExpression indexParameterReference = new CodeVariableReferenceExpression(getViewAtMethod.Parameters[0].Name);
502
503             foreach (KeyValuePair<EntitySetBase, string> generatedViewPair in generatedViews)
504             {
505                 // the CodeDom does not support the following scenarios
506                 // 1. switch statement
507                 // 2. if(){} else if(){}
508                 // The original design here was to have the following code,
509                 // if() { else { if(){} } }
510                 // but this had some drawbacks as described in TFS workitem 590996
511                 // Given the not supported scenarios in CodeDom, we choose only use if statement in this case
512
513                 // if(index == 0)
514                 CodeConditionStatement currentIf = new CodeConditionStatement(new CodeBinaryOperatorExpression(
515                 indexParameterReference, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(index)));
516
517                 getViewAtMethod.Statements.Add(currentIf);
518
519                 EntitySetBase entitySet = generatedViewPair.Key;
520                 string extentFullName = GetExtentFullName(entitySet);
521                 CodeMemberMethod viewMethod = CreateViewReturnMethod(extentFullName, index, generatedViewPair.Value);
522                 typeDeclaration.Members.Add(viewMethod);
523
524                 // return GetNorthwindContext_Customers();
525                 CodeMethodReturnStatement returnViewMethodCall = new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, viewMethod.Name)));
526                 currentIf.TrueStatements.Add(returnViewMethodCall);
527
528                 index++;
529             }
530             
531             // if an invalid index is asked for throw
532             getViewAtMethod.Statements.Add(new CodeThrowExceptionStatement(
533                                             new CodeObjectCreateExpression(new CodeTypeReference(typeof(IndexOutOfRangeException)))));
534         }
535
536         private static CodeMemberMethod CreateViewReturnMethod(string extentFullName, int index, string viewText)
537         {
538             //Add the views to a method
539             CodeMemberMethod viewMethod = new CodeMemberMethod();
540             viewMethod.Name = "GetView" + index.ToString(CultureInfo.InvariantCulture);
541
542             viewMethod.Attributes = MemberAttributes.Private;
543             viewMethod.ReturnType = new CodeTypeReference(typeof(KeyValuePair<,>).MakeGenericType(new Type[] { typeof(string), typeof(string) }));
544             viewMethod.Comments.Add(new CodeCommentStatement(EntityViewGenerationConstants.SummaryStartElement, true /*docComment*/));
545             viewMethod.Comments.Add(new CodeCommentStatement(Strings.IndividualViewComments(extentFullName), true /*docComment*/));
546             viewMethod.Comments.Add(new CodeCommentStatement(EntityViewGenerationConstants.SummaryEndElement, true /*docComment*/));
547
548             CodeExpression viewTextExpression;
549             // only use the StringBuilder if we have to.
550             if (viewText.Length > MAXONELINELENGTH)
551             {
552                 CreateSizedStringBuilder(viewMethod.Statements, viewText.Length);
553                 foreach (var appendExpression in GetAppendViewStringsExpressions(viewText))
554                 {
555                     // viewString.Append(xxx);
556                     viewMethod.Statements.Add(appendExpression);
557                 }
558
559                 viewTextExpression = new CodeMethodInvokeExpression(GetViewStringBuilderVariable(), "ToString");
560             }
561             else
562             {
563                 viewTextExpression = new CodePrimitiveExpression(viewText);
564             }
565
566             // return new System.Collections.Generic.KeyValuePair<string, string>("dbo.Products", viewString.ToString());
567             // or
568             // return new System.Collections.Generic.KeyValuePair<string, string>("dbo.Products", "SELECT value c...");
569             CodeObjectCreateExpression newExpression =
570                 new CodeObjectCreateExpression(
571                     viewMethod.ReturnType,
572                     new CodePrimitiveExpression(extentFullName),
573                     viewTextExpression);
574             viewMethod.Statements.Add(new CodeMethodReturnStatement(newExpression));
575
576             return viewMethod;
577         }
578
579         private static void CreateSizedStringBuilder(CodeStatementCollection statements, int capacity)
580         {
581             // StringBuilder viewString = new StringBuilder(237);
582             CodeVariableDeclarationStatement viewStringDeclaration = new CodeVariableDeclarationStatement(typeof(StringBuilder), "viewString");
583             CodeObjectCreateExpression viewStringConstruct = new CodeObjectCreateExpression(typeof(StringBuilder), new CodePrimitiveExpression(capacity));
584             viewStringDeclaration.InitExpression = viewStringConstruct;
585
586             statements.Add(viewStringDeclaration);
587         }
588
589         private static IEnumerable<string> SplitViewStrings(string largeViewString)
590         {
591             for (int i = 0; i <= largeViewString.Length / ONELINELENGTH; i++)
592             {
593                 if (i * ONELINELENGTH + ONELINELENGTH < largeViewString.Length)
594                 {
595                     yield return largeViewString.Substring(i * ONELINELENGTH, ONELINELENGTH);
596                 }
597                 else
598                 {
599                     // the very last part of the splited string
600                     yield return largeViewString.Substring(i * ONELINELENGTH);
601                 }
602             }
603         }
604
605         private static IEnumerable<CodeMethodInvokeExpression> GetViewStringsAppendToStringBuilder(
606             params string[] viewStrings)
607         {
608             foreach (var viewString in viewStrings)
609             {
610                 // viewString.Append("xxx");
611                 yield return AppendStringToStringBuilder(GetViewStringBuilderVariable(), viewString);
612             }
613         }
614
615         private static CodeVariableReferenceExpression GetViewStringBuilderVariable()
616         {
617             return new CodeVariableReferenceExpression("viewString");
618         }
619
620         private static CodeMethodInvokeExpression AppendStringToStringBuilder(
621             CodeVariableReferenceExpression stringBuilder, string stringToAppend)
622         {
623             return new CodeMethodInvokeExpression(
624                 stringBuilder, "Append", new CodePrimitiveExpression(stringToAppend));
625         }
626
627         private static IEnumerable<CodeMethodInvokeExpression> GetAppendViewStringsExpressions(string viewString)
628         {
629             if (viewString.Length > MAXONELINELENGTH)
630             {
631                 // if the string is longer than 2046 charactors, we splitted them in to 80 each
632                 // and append them using StringBuilder
633                 return GetViewStringsAppendToStringBuilder(SplitViewStrings(viewString).ToArray<string>());
634             }
635             else
636             {
637                 return GetViewStringsAppendToStringBuilder(viewString);
638             }
639         }
640
641         private static string GetExtentFullName(EntitySetBase entitySet)
642         {
643             //We store the full Extent Name in the generated code which is
644             //EntityContainer name + "." + entitysetName
645             return entitySet.EntityContainer.Name + EntityViewGenerationConstants.QualificationCharacter + entitySet.Name;
646
647         }
648
649         /// <summary>
650         /// Get the constructor for the type that will contain the generated views
651         /// </summary>
652         /// <returns></returns>
653         private static CodeConstructor CreateConstructorForViewStoringType()
654         {
655             CodeConstructor constructor = new CodeConstructor();
656             //Mark it as public
657             constructor.Attributes = MemberAttributes.Public;
658             //Add constructor comments
659             constructor.Comments.Add(new CodeCommentStatement(EntityViewGenerationConstants.SummaryStartElement, true /*docComment*/));
660             constructor.Comments.Add(new CodeCommentStatement(Strings.ConstructorComments, true /*docComment*/));
661             constructor.Comments.Add(new CodeCommentStatement(EntityViewGenerationConstants.SummaryEndElement, true /*docComment*/));
662             return constructor;
663         }
664
665         /// <summary>
666         /// Get the type declaration for the type that will contain the views.
667         /// </summary>
668         /// <returns></returns>
669         private static CodeTypeDeclaration CreateTypeForStoringViews(string viewStorageTypeName)
670         {
671             CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(viewStorageTypeName);
672             typeDecl.TypeAttributes = TypeAttributes.Sealed | TypeAttributes.Public;
673             //This type should derive from the framework type EntityViewContainer which reduces the amount
674             //of generated code
675             typeDecl.BaseTypes.Add(EntityViewGenerationConstants.BaseTypeName);
676             //Add type comments
677             typeDecl.Comments.Add(new CodeCommentStatement(EntityViewGenerationConstants.SummaryStartElement, true /*docComment*/));
678             typeDecl.Comments.Add(new CodeCommentStatement(Strings.TypeComments, true /*docComment*/));
679             typeDecl.Comments.Add(new CodeCommentStatement(EntityViewGenerationConstants.SummaryEndElement, true /*docComment*/));
680             return typeDecl;
681         }
682         #endregion
683     }
684 }