1 //---------------------------------------------------------------------
2 // <copyright file="EntityClassGenerator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 //---------------------------------------------------------------------
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.Diagnostics;
14 using System.Globalization;
16 using System.Data.EntityModel.SchemaObjectModel;
17 using System.Data.Metadata.Edm;
18 using System.Data.EntityModel;
19 using System.Data.Entity.Design.Common;
22 using System.Data.Entity.Design.SsdlGenerator;
23 using Microsoft.Build.Utilities;
24 using System.Runtime.Versioning;
26 namespace System.Data.Entity.Design
29 /// Event handler for the OnTypeGenerated event
31 /// <param name="sender">The source of the event</param>
32 /// <param name="e">The event args</param>
33 public delegate void TypeGeneratedEventHandler(object sender, TypeGeneratedEventArgs e);
36 /// Event handler for the OnPropertyGenerated event
38 /// <param name="sender">The source of the event</param>
39 /// <param name="e">The event args</param>
40 public delegate void PropertyGeneratedEventHandler(object sender, PropertyGeneratedEventArgs e);
43 /// Summary description for CodeGenerator.
45 public sealed class EntityClassGenerator
47 #region Instance Fields
48 LanguageOption _languageOption = LanguageOption.GenerateCSharpCode;
49 EdmToObjectNamespaceMap _edmToObjectNamespaceMap = new EdmToObjectNamespaceMap();
55 /// The event that is raised when a type is generated
57 public event TypeGeneratedEventHandler OnTypeGenerated;
60 /// The event that is raised when a property is generated
62 public event PropertyGeneratedEventHandler OnPropertyGenerated;
66 #region Public Methods
70 public EntityClassGenerator()
77 public EntityClassGenerator(LanguageOption languageOption)
79 _languageOption = EDesignUtil.CheckLanguageOptionArgument(languageOption, "languageOption");
83 /// Gets and Sets the Language to use for code generation.
85 public LanguageOption LanguageOption
87 get { return _languageOption; }
88 set { _languageOption = EDesignUtil.CheckLanguageOptionArgument(value, "value"); }
92 /// Gets the map entries use to customize the namespace of .net types that are generated
93 /// and referenced by the generated code
95 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
96 public EdmToObjectNamespaceMap EdmToObjectNamespaceMap
98 get { return _edmToObjectNamespaceMap; }
102 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
103 public IList<EdmSchemaError> GenerateCode(XmlReader sourceEdmSchema, TextWriter target)
105 EDesignUtil.CheckArgumentNull(sourceEdmSchema, "sourceEdmSchema");
106 EDesignUtil.CheckArgumentNull(target, "target");
107 return GenerateCode(sourceEdmSchema, target, new XmlReader[] { });
110 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
111 [ResourceExposure(ResourceScope.None)] //No resource is exposed since we pass in null as the target paath.
112 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] //For GenerateCodeCommon method call. Since we use null as the path, we have not changed the scope of the resource.
113 public IList<EdmSchemaError> GenerateCode(XmlReader sourceEdmSchema, TextWriter target, IEnumerable<XmlReader> additionalEdmSchemas)
115 EDesignUtil.CheckArgumentNull(sourceEdmSchema, "sourceEdmSchema");
116 EDesignUtil.CheckArgumentNull(additionalEdmSchemas, "additionalEdmSchemas");
117 EDesignUtil.CheckArgumentNull(target, "target");
119 List<EdmSchemaError> errors = new List<EdmSchemaError>();
122 MetadataArtifactLoader sourceLoader = new MetadataArtifactLoaderXmlReaderWrapper(sourceEdmSchema);
123 List<MetadataArtifactLoader> loaders = new List<MetadataArtifactLoader>();
124 loaders.Add(sourceLoader);
127 foreach (XmlReader additionalEdmSchema in additionalEdmSchemas)
129 if (additionalEdmSchema == null)
131 throw EDesignUtil.Argument(Strings.NullAdditionalSchema("additionalEdmSchema", index));
136 MetadataArtifactLoader loader = new MetadataArtifactLoaderXmlReaderWrapper(additionalEdmSchema);
137 Debug.Assert(loader != null, "when is the loader ever null?");
142 if (MetadataUtil.IsCatchableExceptionType(e))
144 errors.Add(new EdmSchemaError(e.Message,
145 (int)ModelBuilderErrorCode.CodeGenAdditionalEdmSchemaIsInvalid,
146 EdmSchemaErrorSeverity.Error));
155 ThrowOnAnyNonWarningErrors(errors);
157 GenerateCodeCommon(sourceLoader,
159 new LazyTextWriterCreator(target),
161 null, // target file path
162 false, // dispose readers?
165 catch (TerminalErrorException)
168 // just a place to jump when errors are detected
174 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
175 [ResourceExposure(ResourceScope.Machine)] //Exposes the sourceEdmSchemaFilePath which is a Machine resource
176 [ResourceConsumption(ResourceScope.Machine)] //For GenerateCode method call. But the path is not created in this method.
177 public IList<EdmSchemaError> GenerateCode(string sourceEdmSchemaFilePath, string targetFilePath)
179 return GenerateCode(sourceEdmSchemaFilePath, targetFilePath, new string[] { });
182 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
183 [ResourceExposure(ResourceScope.Machine)] //Exposes the sourceEdmSchemaFilePath which is a Machine resource
184 [ResourceConsumption(ResourceScope.Machine)] //For MetadataArtifactLoader.Create method call. But the path is not created in this method.
185 public IList<EdmSchemaError> GenerateCode(string sourceEdmSchemaFilePath, string targetPath, IEnumerable<string> additionalEdmSchemaFilePaths)
187 EDesignUtil.CheckStringArgument(sourceEdmSchemaFilePath, "sourceEdmSchemaFilePath");
188 EDesignUtil.CheckArgumentNull(additionalEdmSchemaFilePaths, "additionalEdmSchemaFilePaths");
189 EDesignUtil.CheckStringArgument(targetPath, "targetPath");
191 List<EdmSchemaError> errors = new List<EdmSchemaError>();
194 // create a loader for the source
195 HashSet<string> uriRegistry = new HashSet<string>();
196 MetadataArtifactLoader sourceLoader;
199 sourceLoader = MetadataArtifactLoader.Create(sourceEdmSchemaFilePath, MetadataArtifactLoader.ExtensionCheck.Specific,
200 XmlConstants.CSpaceSchemaExtension, uriRegistry);
202 catch (MetadataException e)
204 errors.Add(CreateErrorForException(ModelBuilderErrorCode.CodeGenSourceFilePathIsInvalid, e, sourceEdmSchemaFilePath));
208 if (sourceLoader.IsComposite)
210 throw new ArgumentException(Strings.CodeGenSourceFilePathIsNotAFile, "sourceEdmSchemaPath");
213 // create loaders for all the additional schemas
214 List<MetadataArtifactLoader> loaders = new List<MetadataArtifactLoader>();
215 loaders.Add(sourceLoader);
217 foreach (string additionalSchemaFilePath in additionalEdmSchemaFilePaths)
219 if (additionalSchemaFilePath == null)
221 throw EDesignUtil.Argument(Strings.NullAdditionalSchema("additionalEdmSchemaFilePaths", index));
226 MetadataArtifactLoader loader = MetadataArtifactLoader.Create(additionalSchemaFilePath,
227 MetadataArtifactLoader.ExtensionCheck.Specific,
228 XmlConstants.CSpaceSchemaExtension, uriRegistry);
229 Debug.Assert(loader != null, "when is the loader ever null?");
234 if(MetadataUtil.IsCatchableExceptionType(e))
236 errors.Add(CreateErrorForException(ModelBuilderErrorCode.CodeGenAdditionalEdmSchemaIsInvalid, e, additionalSchemaFilePath));
246 ThrowOnAnyNonWarningErrors(errors);
249 using (LazyTextWriterCreator target = new LazyTextWriterCreator(targetPath))
251 GenerateCodeCommon(sourceLoader, loaders, target, sourceEdmSchemaFilePath, targetPath,
252 true, // dispose readers
256 catch (System.IO.IOException ex)
258 errors.Add(CreateErrorForException(System.Data.EntityModel.SchemaObjectModel.ErrorCode.IOException, ex, targetPath));
262 catch (TerminalErrorException)
265 // just a place to jump when errors are detected
270 private void GenerateCodeCommon(MetadataArtifactLoader sourceLoader,
271 List<MetadataArtifactLoader> loaders,
272 LazyTextWriterCreator target,
273 string sourceEdmSchemaFilePath,
274 string targetFilePath,
276 List<EdmSchemaError> errors)
278 MetadataArtifactLoaderComposite composite = new MetadataArtifactLoaderComposite(loaders);
280 // create the schema manager from the xml readers
281 Dictionary<MetadataArtifactLoader, XmlReader> readerSourceMap = new Dictionary<MetadataArtifactLoader, XmlReader>();
282 IList<Schema> schemas;
283 List<XmlReader> readers = composite.GetReaders(readerSourceMap);
287 IList<EdmSchemaError> schemaManagerErrors =
288 SchemaManager.ParseAndValidate(readers,
289 composite.GetPaths(),
290 SchemaDataModelOption.EntityDataModel,
291 EdmProviderManifest.Instance,
293 errors.AddRange(schemaManagerErrors);
299 MetadataUtil.DisposeXmlReaders(readers);
302 ThrowOnAnyNonWarningErrors(errors);
303 Debug.Assert(readerSourceMap.ContainsKey(sourceLoader), "the source loader didn't produce any of the xml readers...");
304 XmlReader sourceReader = readerSourceMap[sourceLoader];
307 // use the index of the "source" xml reader as the index of the "source" schema
308 Debug.Assert(readers.Contains(sourceReader), "the source reader is not in the list of readers");
309 int index = readers.IndexOf(sourceReader);
310 Debug.Assert(index >= 0, "couldn't find the source reader in the list of readers");
312 Debug.Assert(readers.Count == schemas.Count, "We have a different number of readers than schemas");
313 Schema sourceSchema = schemas[index];
314 Debug.Assert(sourceSchema != null, "sourceSchema is null");
316 // create the EdmItemCollection from the schemas
317 EdmItemCollection itemCollection = new EdmItemCollection(schemas);
318 if (EntityFrameworkVersionsUtil.ConvertToVersion(itemCollection.EdmVersion) >= EntityFrameworkVersions.Version2)
320 throw EDesignUtil.InvalidOperation(Strings.TargetEntityFrameworkVersionToNewForEntityClassGenerator);
324 ClientApiGenerator generator = new ClientApiGenerator(sourceSchema, itemCollection, this, errors);
325 generator.GenerateCode(target, targetFilePath);
331 #region Private Methods
332 private static EdmSchemaError CreateErrorForException(System.Data.EntityModel.SchemaObjectModel.ErrorCode errorCode, System.Exception exception, string sourceLocation)
334 Debug.Assert(exception != null);
335 Debug.Assert(sourceLocation != null);
337 return new EdmSchemaError(exception.Message, (int)errorCode, EdmSchemaErrorSeverity.Error, sourceLocation, 0, 0, exception);
340 internal static EdmSchemaError CreateErrorForException(ModelBuilderErrorCode errorCode, System.Exception exception, string sourceLocation)
342 Debug.Assert(exception != null);
343 Debug.Assert(sourceLocation != null);
345 return new EdmSchemaError(exception.Message, (int)errorCode, EdmSchemaErrorSeverity.Error, sourceLocation, 0, 0, exception);
348 internal static EdmSchemaError CreateErrorForException(ModelBuilderErrorCode errorCode, System.Exception exception)
350 Debug.Assert(exception != null);
352 return new EdmSchemaError(exception.Message, (int)errorCode, EdmSchemaErrorSeverity.Error, null, 0, 0, exception);
355 private void ThrowOnAnyNonWarningErrors(List<EdmSchemaError> errors)
357 foreach (EdmSchemaError error in errors)
359 if (error.Severity != EdmSchemaErrorSeverity.Warning)
361 throw new TerminalErrorException();
368 #region Event Helpers
371 /// Helper method that raises the TypeGenerated event
373 /// <param name="eventArgs">The event arguments passed to the subscriber</param>
374 internal void RaiseTypeGeneratedEvent(TypeGeneratedEventArgs eventArgs)
376 if (this.OnTypeGenerated != null)
378 this.OnTypeGenerated(this, eventArgs);
383 /// Helper method that raises the PropertyGenerated event
385 /// <param name="eventArgs">The event arguments passed to the subscriber</param>
386 internal void RaisePropertyGeneratedEvent(PropertyGeneratedEventArgs eventArgs)
388 if (this.OnPropertyGenerated != null)
390 this.OnPropertyGenerated(this, eventArgs);