1 //---------------------------------------------------------------------
2 // <copyright file="EntityViewGenerator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
12 using System.CodeDom.Compiler;
14 using System.Collections.Generic;
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;
33 namespace System.Data.Entity.Design
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.
47 public class EntityViewGenerator
51 /// Create the instance of ViewGenerator with the given language option.
53 /// <param name="languageOption">Language Option for generated code.</param>
54 public EntityViewGenerator(LanguageOption languageOption)
56 m_languageOption = EDesignUtil.CheckLanguageOptionArgument(languageOption, "languageOption");
60 /// Create the instance of ViewGenerator using C# as the default
63 public EntityViewGenerator()
64 : this(LanguageOption.GenerateCSharpCode)
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;
79 /// Language Option for generated code.
81 public LanguageOption LanguageOption
83 get { return m_languageOption; }
84 set { m_languageOption = EDesignUtil.CheckLanguageOptionArgument(value, "value"); }
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.
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)
103 EDesignUtil.CheckArgumentNull(mappingCollection, "mappingCollection");
104 EDesignUtil.CheckStringArgument(outputPath, "outputPath");
106 TextWriter outputWriter = null;
109 return InternalGenerateViews(mappingCollection, () => new StreamWriter(outputPath), out outputWriter);
113 if (outputWriter != null)
115 outputWriter.Dispose();
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.
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)
132 EDesignUtil.CheckArgumentNull(mappingCollection, "mappingCollection");
134 Version targetEntityFrameworkVersion;
135 IList<EdmSchemaError> errorList = GetMinimumTargetFrameworkVersion(mappingCollection, out targetEntityFrameworkVersion);
137 return GenerateViews(mappingCollection, outputWriter, targetEntityFrameworkVersion).Concat(errorList).ToList();
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.
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)
152 EDesignUtil.CheckArgumentNull(mappingCollection, "mappingCollection");
153 EDesignUtil.CheckArgumentNull(outputWriter, "outputWriter");
155 EDesignUtil.CheckTargetEntityFrameworkVersionArgument(targetEntityFrameworkVersion, "targetEntityFrameworkVersion");
156 CheckForCompatibleSchemaAndTarget(mappingCollection, targetEntityFrameworkVersion);
159 return InternalGenerateViews(mappingCollection, () => outputWriter, out writer);
162 private static void CheckForCompatibleSchemaAndTarget(StorageMappingItemCollection mappingCollection, Version targetEntityFrameworkVersion)
164 Version mappingVersion = EntityFrameworkVersionsUtil.ConvertToVersion(mappingCollection.MappingVersion);
165 if (targetEntityFrameworkVersion < mappingVersion)
167 throw EDesignUtil.Argument(Strings.TargetVersionSchemaVersionMismatch(targetEntityFrameworkVersion, mappingVersion), null);
170 Version edmVersion = EntityFrameworkVersionsUtil.ConvertToVersion(mappingCollection.EdmItemCollection.EdmVersion);
171 if (targetEntityFrameworkVersion < edmVersion)
173 throw EDesignUtil.Argument(Strings.TargetVersionSchemaVersionMismatch(targetEntityFrameworkVersion, edmVersion), null);
177 private IList<EdmSchemaError> InternalGenerateViews(
178 StorageMappingItemCollection mappingCollection,
179 Func<TextWriter> GetWriter,
180 out TextWriter outputWriter)
182 IList<EdmSchemaError> schemaErrors;
183 CodeDomProvider provider;
184 Dictionary<EntitySetBase, string> generatedViews;
185 if (GetViewsWithErrors(mappingCollection, out provider, out schemaErrors, out generatedViews))
191 outputWriter = GetWriter();
192 GenerateAndStoreViews(mappingCollection, generatedViews,
193 outputWriter, provider, schemaErrors);
199 /// Validates the mappingCollections and returns the schemaErrors.
201 /// <param name="mappingCollection"></param>
202 /// <returns>list of EdmSchemaError</returns>
203 [CLSCompliant(false)]
204 public static IList<EdmSchemaError> Validate(StorageMappingItemCollection mappingCollection)
206 EDesignUtil.CheckArgumentNull(mappingCollection, "mappingCollection");
208 Version targetEntityFrameworkVersion;
209 IList<EdmSchemaError> errorList = GetMinimumTargetFrameworkVersion(mappingCollection, out targetEntityFrameworkVersion);
211 return Validate(mappingCollection, targetEntityFrameworkVersion).Concat(errorList).ToList();
215 /// Validates the mappingCollections and returns the schemaErrors.
217 /// <param name="mappingCollection"></param>
218 /// <returns>list of EdmSchemaError</returns>
219 [CLSCompliant(false)]
220 public static IList<EdmSchemaError> Validate(StorageMappingItemCollection mappingCollection, Version targetEntityFrameworkVersion)
222 EDesignUtil.CheckTargetEntityFrameworkVersionArgument(targetEntityFrameworkVersion, "targetEntityFrameworkVersion");
223 CheckForCompatibleSchemaAndTarget(mappingCollection, targetEntityFrameworkVersion);
225 // purpose of this API is to validate the mappingCollection, it basically will call GetEntitySetViews
227 EDesignUtil.CheckArgumentNull(mappingCollection, "mappingCollection");
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;
234 // Validate entity set views.
235 GetEntitySetViews(mappingCollection, out schemaErrors, out generatedViews);
237 // Validate function imports and their mapping.
238 foreach (var containerMapping in mappingCollection.GetItems<StorageEntityContainerMapping>())
240 foreach (var functionImport in containerMapping.EdmEntityContainer.FunctionImports)
242 FunctionImportMapping functionImportMapping;
243 if (containerMapping.TryGetFunctionImportMapping(functionImport, out functionImportMapping))
245 if (functionImport.IsComposableAttribute)
247 ((FunctionImportMappingComposable)functionImportMapping).ValidateFunctionView(schemaErrors);
252 schemaErrors.Add(new EdmSchemaError(
253 Strings.UnmappedFunctionImport(functionImport.Identity),
254 (int)StorageMappingErrorCode.UnmappedFunctionImport,
255 EdmSchemaErrorSeverity.Warning));
260 Debug.Assert(schemaErrors != null, "schemaErrors is null");
261 return HandleValidationErrors(schemaErrors);
264 private static IList<EdmSchemaError> HandleValidationErrors(IList<EdmSchemaError> schemaErrors)
266 IEnumerable<EdmSchemaError> warningsToRemove =
267 schemaErrors.Where(s =>
268 s.ErrorCode == ERRORCODE_MAPPINGALLQUERYVIEWATCOMPILETIME); // When EntityContainerMapping only has QueryView, the mapping is valid
270 return schemaErrors.Except(warningsToRemove).ToList();
273 private bool GetViewsWithErrors(StorageMappingItemCollection mappingCollection, out CodeDomProvider provider, out IList<EdmSchemaError> schemaErrors, out Dictionary<EntitySetBase, string> generatedViews)
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)
284 private void GetViewsAndCodeDomProvider(StorageMappingItemCollection mappingCollection, out CodeDomProvider provider, out IList<EdmSchemaError> schemaErrors, out Dictionary<EntitySetBase, string> generatedViews)
286 //Create a CodeDomProvider based on options.
288 switch (m_languageOption)
290 case LanguageOption.GenerateCSharpCode:
291 provider = new Microsoft.CSharp.CSharpCodeProvider();
294 case LanguageOption.GenerateVBCode:
295 provider = new Microsoft.VisualBasic.VBCodeProvider();
299 //Get the views for the Entity Sets and Association Sets in the mapping item collection
300 GetEntitySetViews(mappingCollection, out schemaErrors, out generatedViews);
303 private static void GetEntitySetViews(StorageMappingItemCollection mappingCollection,
304 out IList<EdmSchemaError> schemaErrors, out Dictionary<EntitySetBase, string> generatedViews)
306 generatedViews = mappingCollection.GenerateEntitySetViews(out schemaErrors);
309 private static IList<EdmSchemaError> GetMinimumTargetFrameworkVersion(StorageMappingItemCollection mappingCollection, out Version targetFrameworkVersion)
311 List<EdmSchemaError> errorList = new List<EdmSchemaError>();
313 targetFrameworkVersion = EntityFrameworkVersionsUtil.ConvertToVersion(mappingCollection.EdmItemCollection.EdmVersion);
315 if (targetFrameworkVersion > EntityFrameworkVersions.Default)
317 errorList.Add(new EdmSchemaError(Strings.DefaultTargetVersionTooLow(EntityFrameworkVersions.Default, targetFrameworkVersion), (int)System.Data.Entity.Design.SsdlGenerator.ModelBuilderErrorCode.SchemaVersionHigherThanTargetVersion, EdmSchemaErrorSeverity.Warning));
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.
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)
335 EdmItemCollection edmCollection = mappingCollection.EdmItemCollection;
336 StoreItemCollection storeCollection = mappingCollection.StoreItemCollection;
338 //Create an emtpty compile unit and build up the generated code
339 CodeCompileUnit compileUnit = new CodeCompileUnit();
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);
346 foreach (var storageEntityContainerMapping in mappingCollection.GetItems<StorageEntityContainerMapping>())
348 //Throw warning when containerMapping contains query view for bug 547285.
349 if (HasQueryView(storageEntityContainerMapping))
351 schemaErrors.Add(new EdmSchemaError(
352 Strings.UnsupportedQueryViewInEntityContainerMapping(storageEntityContainerMapping.Identity),
353 (int)StorageMappingErrorCode.UnsupportedQueryViewInEntityContainerMapping,
354 EdmSchemaErrorSeverity.Warning));
358 #region Class Declaration
360 string edmContainerName = storageEntityContainerMapping.EdmEntityContainer.Name;
361 string storeContainerName = storageEntityContainerMapping.StorageEntityContainer.Name;
363 string hashOverMappingClosure = MetadataMappingHasherVisitor.GetMappingClosureHash(edmCollection.EdmVersion, storageEntityContainerMapping);
365 StringBuilder inputForTypeNameContent = new StringBuilder(hashOverMappingClosure);
367 string viewStorageTypeName = EntityViewGenerationConstants.ViewGenerationTypeNamePrefix + StringHashBuilder.ComputeHash(MetadataHelper.CreateMetadataHashAlgorithm(edmCollection.EdmVersion), inputForTypeNameContent.ToString()).ToUpperInvariant();
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);
379 //Add the type which will be the class that contains all the views in this assembly
380 CodeTypeDeclaration viewStoringType = CreateTypeForStoringViews(viewStorageTypeName);
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;
388 //Get an expression that expresses this instance
389 CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression();
392 string viewHash = MetadataHelper.GenerateHashForAllExtentViewsContent(edmCollection.EdmVersion, GenerateDictionaryForEntitySetNameAndView(generatedViews));
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));
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);
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);
427 //Add the type to the namespace
428 codeNamespace.Types.Add(viewStoringType);
433 if (codeNamespace.Types.Count > 0)
435 GenerateCode(sourceWriter, provider, compileUnit);
436 sourceWriter.Flush();
440 private static bool HasQueryView(StorageEntityContainerMapping storageEntityContainerMapping)
442 foreach (EntitySetBase extent in storageEntityContainerMapping.EdmEntityContainer.BaseEntitySets)
444 if (storageEntityContainerMapping.HasQueryViewForSetMap(extent.Name))
452 private static Dictionary<string, string> GenerateDictionaryForEntitySetNameAndView(Dictionary<EntitySetBase, string> dictionary)
454 Dictionary<string, string> newDictionary = new Dictionary<string, string>();
455 foreach (var item in dictionary)
457 newDictionary.Add(GetExtentFullName(item.Key), item.Value);
459 return newDictionary;
463 /// Write code to the given stream from the compile unit.
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)
470 CodeGeneratorOptions styleOptions = new CodeGeneratorOptions();
471 styleOptions.BracingStyle = "C";
472 styleOptions.BlankLinesBetweenMembers = true;
473 styleOptions.VerbatimOrder = true;
474 provider.GenerateCodeFromCompileUnit(compileUnit, sourceWriter, styleOptions);
478 /// Generate Code to put the views in the generated code.
480 /// <param name="typeDeclaration"></param>
481 /// <param name="generatedViews"></param>
482 /// <returns></returns>
483 private static void CreateAndAddGetViewAtMethod(CodeTypeDeclaration typeDeclaration, Dictionary<EntitySetBase, string> generatedViews)
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*/));
496 getViewAtMethod.Attributes = MemberAttributes.Family | MemberAttributes.Override;
498 typeDeclaration.Members.Add(getViewAtMethod);
501 CodeVariableReferenceExpression indexParameterReference = new CodeVariableReferenceExpression(getViewAtMethod.Parameters[0].Name);
503 foreach (KeyValuePair<EntitySetBase, string> generatedViewPair in generatedViews)
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
514 CodeConditionStatement currentIf = new CodeConditionStatement(new CodeBinaryOperatorExpression(
515 indexParameterReference, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(index)));
517 getViewAtMethod.Statements.Add(currentIf);
519 EntitySetBase entitySet = generatedViewPair.Key;
520 string extentFullName = GetExtentFullName(entitySet);
521 CodeMemberMethod viewMethod = CreateViewReturnMethod(extentFullName, index, generatedViewPair.Value);
522 typeDeclaration.Members.Add(viewMethod);
524 // return GetNorthwindContext_Customers();
525 CodeMethodReturnStatement returnViewMethodCall = new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, viewMethod.Name)));
526 currentIf.TrueStatements.Add(returnViewMethodCall);
531 // if an invalid index is asked for throw
532 getViewAtMethod.Statements.Add(new CodeThrowExceptionStatement(
533 new CodeObjectCreateExpression(new CodeTypeReference(typeof(IndexOutOfRangeException)))));
536 private static CodeMemberMethod CreateViewReturnMethod(string extentFullName, int index, string viewText)
538 //Add the views to a method
539 CodeMemberMethod viewMethod = new CodeMemberMethod();
540 viewMethod.Name = "GetView" + index.ToString(CultureInfo.InvariantCulture);
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*/));
548 CodeExpression viewTextExpression;
549 // only use the StringBuilder if we have to.
550 if (viewText.Length > MAXONELINELENGTH)
552 CreateSizedStringBuilder(viewMethod.Statements, viewText.Length);
553 foreach (var appendExpression in GetAppendViewStringsExpressions(viewText))
555 // viewString.Append(xxx);
556 viewMethod.Statements.Add(appendExpression);
559 viewTextExpression = new CodeMethodInvokeExpression(GetViewStringBuilderVariable(), "ToString");
563 viewTextExpression = new CodePrimitiveExpression(viewText);
566 // return new System.Collections.Generic.KeyValuePair<string, string>("dbo.Products", viewString.ToString());
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),
574 viewMethod.Statements.Add(new CodeMethodReturnStatement(newExpression));
579 private static void CreateSizedStringBuilder(CodeStatementCollection statements, int capacity)
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;
586 statements.Add(viewStringDeclaration);
589 private static IEnumerable<string> SplitViewStrings(string largeViewString)
591 for (int i = 0; i <= largeViewString.Length / ONELINELENGTH; i++)
593 if (i * ONELINELENGTH + ONELINELENGTH < largeViewString.Length)
595 yield return largeViewString.Substring(i * ONELINELENGTH, ONELINELENGTH);
599 // the very last part of the splited string
600 yield return largeViewString.Substring(i * ONELINELENGTH);
605 private static IEnumerable<CodeMethodInvokeExpression> GetViewStringsAppendToStringBuilder(
606 params string[] viewStrings)
608 foreach (var viewString in viewStrings)
610 // viewString.Append("xxx");
611 yield return AppendStringToStringBuilder(GetViewStringBuilderVariable(), viewString);
615 private static CodeVariableReferenceExpression GetViewStringBuilderVariable()
617 return new CodeVariableReferenceExpression("viewString");
620 private static CodeMethodInvokeExpression AppendStringToStringBuilder(
621 CodeVariableReferenceExpression stringBuilder, string stringToAppend)
623 return new CodeMethodInvokeExpression(
624 stringBuilder, "Append", new CodePrimitiveExpression(stringToAppend));
627 private static IEnumerable<CodeMethodInvokeExpression> GetAppendViewStringsExpressions(string viewString)
629 if (viewString.Length > MAXONELINELENGTH)
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>());
637 return GetViewStringsAppendToStringBuilder(viewString);
641 private static string GetExtentFullName(EntitySetBase entitySet)
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;
650 /// Get the constructor for the type that will contain the generated views
652 /// <returns></returns>
653 private static CodeConstructor CreateConstructorForViewStoringType()
655 CodeConstructor constructor = new CodeConstructor();
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*/));
666 /// Get the type declaration for the type that will contain the views.
668 /// <returns></returns>
669 private static CodeTypeDeclaration CreateTypeForStoringViews(string viewStorageTypeName)
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
675 typeDecl.BaseTypes.Add(EntityViewGenerationConstants.BaseTypeName);
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*/));