2016-08-03 10:59:49 +00:00
//---------------------------------------------------------------------
// <copyright file="EntityClassGenerator.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
2017-08-21 15:34:15 +00:00
// @owner Microsoft
// @backupOwner Microsoft
2016-08-03 10:59:49 +00:00
//---------------------------------------------------------------------
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Globalization ;
using System.Data ;
using System.Data.EntityModel.SchemaObjectModel ;
using System.Data.Metadata.Edm ;
using System.Data.EntityModel ;
using System.Data.Entity.Design.Common ;
using System.IO ;
using System.Xml ;
using System.Data.Entity.Design.SsdlGenerator ;
using Microsoft.Build.Utilities ;
using System.Runtime.Versioning ;
namespace System.Data.Entity.Design
{
/// <summary>
/// Event handler for the OnTypeGenerated event
/// </summary>
/// <param name="sender">The source of the event</param>
/// <param name="e">The event args</param>
public delegate void TypeGeneratedEventHandler ( object sender , TypeGeneratedEventArgs e ) ;
/// <summary>
/// Event handler for the OnPropertyGenerated event
/// </summary>
/// <param name="sender">The source of the event</param>
/// <param name="e">The event args</param>
public delegate void PropertyGeneratedEventHandler ( object sender , PropertyGeneratedEventArgs e ) ;
/// <summary>
/// Summary description for CodeGenerator.
/// </summary>
public sealed class EntityClassGenerator
{
#region Instance Fields
LanguageOption _languageOption = LanguageOption . GenerateCSharpCode ;
EdmToObjectNamespaceMap _edmToObjectNamespaceMap = new EdmToObjectNamespaceMap ( ) ;
#endregion
#region Events
/// <summary>
/// The event that is raised when a type is generated
/// </summary>
public event TypeGeneratedEventHandler OnTypeGenerated ;
/// <summary>
/// The event that is raised when a property is generated
/// </summary>
public event PropertyGeneratedEventHandler OnPropertyGenerated ;
#endregion
#region Public Methods
/// <summary>
///
/// </summary>
public EntityClassGenerator ( )
{
}
/// <summary>
///
/// </summary>
public EntityClassGenerator ( LanguageOption languageOption )
{
_languageOption = EDesignUtil . CheckLanguageOptionArgument ( languageOption , "languageOption" ) ;
}
/// <summary>
/// Gets and Sets the Language to use for code generation.
/// </summary>
public LanguageOption LanguageOption
{
get { return _languageOption ; }
set { _languageOption = EDesignUtil . CheckLanguageOptionArgument ( value , "value" ) ; }
}
/// <summary>
/// Gets the map entries use to customize the namespace of .net types that are generated
/// and referenced by the generated code
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
public EdmToObjectNamespaceMap EdmToObjectNamespaceMap
{
get { return _edmToObjectNamespaceMap ; }
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
public IList < EdmSchemaError > GenerateCode ( XmlReader sourceEdmSchema , TextWriter target )
{
EDesignUtil . CheckArgumentNull ( sourceEdmSchema , "sourceEdmSchema" ) ;
EDesignUtil . CheckArgumentNull ( target , "target" ) ;
return GenerateCode ( sourceEdmSchema , target , new XmlReader [ ] { } ) ;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
[ResourceExposure(ResourceScope.None)] //No resource is exposed since we pass in null as the target paath.
[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.
public IList < EdmSchemaError > GenerateCode ( XmlReader sourceEdmSchema , TextWriter target , IEnumerable < XmlReader > additionalEdmSchemas )
{
EDesignUtil . CheckArgumentNull ( sourceEdmSchema , "sourceEdmSchema" ) ;
EDesignUtil . CheckArgumentNull ( additionalEdmSchemas , "additionalEdmSchemas" ) ;
EDesignUtil . CheckArgumentNull ( target , "target" ) ;
List < EdmSchemaError > errors = new List < EdmSchemaError > ( ) ;
try
{
MetadataArtifactLoader sourceLoader = new MetadataArtifactLoaderXmlReaderWrapper ( sourceEdmSchema ) ;
List < MetadataArtifactLoader > loaders = new List < MetadataArtifactLoader > ( ) ;
loaders . Add ( sourceLoader ) ;
int index = 0 ;
foreach ( XmlReader additionalEdmSchema in additionalEdmSchemas )
{
if ( additionalEdmSchema = = null )
{
throw EDesignUtil . Argument ( Strings . NullAdditionalSchema ( "additionalEdmSchema" , index ) ) ;
}
try
{
MetadataArtifactLoader loader = new MetadataArtifactLoaderXmlReaderWrapper ( additionalEdmSchema ) ;
Debug . Assert ( loader ! = null , "when is the loader ever null?" ) ;
loaders . Add ( loader ) ;
}
catch ( Exception e )
{
if ( MetadataUtil . IsCatchableExceptionType ( e ) )
{
errors . Add ( new EdmSchemaError ( e . Message ,
( int ) ModelBuilderErrorCode . CodeGenAdditionalEdmSchemaIsInvalid ,
EdmSchemaErrorSeverity . Error ) ) ;
}
else
{
throw ;
}
}
index + + ;
}
ThrowOnAnyNonWarningErrors ( errors ) ;
GenerateCodeCommon ( sourceLoader ,
loaders ,
new LazyTextWriterCreator ( target ) ,
null , // source path
null , // target file path
false , // dispose readers?
errors ) ;
}
catch ( TerminalErrorException )
{
// do nothing
// just a place to jump when errors are detected
}
return errors ;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
[ResourceExposure(ResourceScope.Machine)] //Exposes the sourceEdmSchemaFilePath which is a Machine resource
[ResourceConsumption(ResourceScope.Machine)] //For GenerateCode method call. But the path is not created in this method.
public IList < EdmSchemaError > GenerateCode ( string sourceEdmSchemaFilePath , string targetFilePath )
{
return GenerateCode ( sourceEdmSchemaFilePath , targetFilePath , new string [ ] { } ) ;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
[ResourceExposure(ResourceScope.Machine)] //Exposes the sourceEdmSchemaFilePath which is a Machine resource
[ResourceConsumption(ResourceScope.Machine)] //For MetadataArtifactLoader.Create method call. But the path is not created in this method.
public IList < EdmSchemaError > GenerateCode ( string sourceEdmSchemaFilePath , string targetPath , IEnumerable < string > additionalEdmSchemaFilePaths )
{
EDesignUtil . CheckStringArgument ( sourceEdmSchemaFilePath , "sourceEdmSchemaFilePath" ) ;
EDesignUtil . CheckArgumentNull ( additionalEdmSchemaFilePaths , "additionalEdmSchemaFilePaths" ) ;
EDesignUtil . CheckStringArgument ( targetPath , "targetPath" ) ;
List < EdmSchemaError > errors = new List < EdmSchemaError > ( ) ;
try
{
// create a loader for the source
HashSet < string > uriRegistry = new HashSet < string > ( ) ;
MetadataArtifactLoader sourceLoader ;
try
{
sourceLoader = MetadataArtifactLoader . Create ( sourceEdmSchemaFilePath , MetadataArtifactLoader . ExtensionCheck . Specific ,
XmlConstants . CSpaceSchemaExtension , uriRegistry ) ;
}
catch ( MetadataException e )
{
errors . Add ( CreateErrorForException ( ModelBuilderErrorCode . CodeGenSourceFilePathIsInvalid , e , sourceEdmSchemaFilePath ) ) ;
return errors ;
}
if ( sourceLoader . IsComposite )
{
throw new ArgumentException ( Strings . CodeGenSourceFilePathIsNotAFile , "sourceEdmSchemaPath" ) ;
}
// create loaders for all the additional schemas
List < MetadataArtifactLoader > loaders = new List < MetadataArtifactLoader > ( ) ;
loaders . Add ( sourceLoader ) ;
int index = 0 ;
foreach ( string additionalSchemaFilePath in additionalEdmSchemaFilePaths )
{
if ( additionalSchemaFilePath = = null )
{
throw EDesignUtil . Argument ( Strings . NullAdditionalSchema ( "additionalEdmSchemaFilePaths" , index ) ) ;
}
try
{
MetadataArtifactLoader loader = MetadataArtifactLoader . Create ( additionalSchemaFilePath ,
MetadataArtifactLoader . ExtensionCheck . Specific ,
XmlConstants . CSpaceSchemaExtension , uriRegistry ) ;
Debug . Assert ( loader ! = null , "when is the loader ever null?" ) ;
loaders . Add ( loader ) ;
}
catch ( Exception e )
{
if ( MetadataUtil . IsCatchableExceptionType ( e ) )
{
errors . Add ( CreateErrorForException ( ModelBuilderErrorCode . CodeGenAdditionalEdmSchemaIsInvalid , e , additionalSchemaFilePath ) ) ;
}
else
{
throw ;
}
}
index + + ;
}
ThrowOnAnyNonWarningErrors ( errors ) ;
try
{
using ( LazyTextWriterCreator target = new LazyTextWriterCreator ( targetPath ) )
{
GenerateCodeCommon ( sourceLoader , loaders , target , sourceEdmSchemaFilePath , targetPath ,
true , // dispose readers
errors ) ;
}
}
catch ( System . IO . IOException ex )
{
errors . Add ( CreateErrorForException ( System . Data . EntityModel . SchemaObjectModel . ErrorCode . IOException , ex , targetPath ) ) ;
return errors ;
}
}
catch ( TerminalErrorException )
{
// do nothing
// just a place to jump when errors are detected
}
return errors ;
}
private void GenerateCodeCommon ( MetadataArtifactLoader sourceLoader ,
List < MetadataArtifactLoader > loaders ,
LazyTextWriterCreator target ,
string sourceEdmSchemaFilePath ,
string targetFilePath ,
bool closeReaders ,
List < EdmSchemaError > errors )
{
MetadataArtifactLoaderComposite composite = new MetadataArtifactLoaderComposite ( loaders ) ;
// create the schema manager from the xml readers
Dictionary < MetadataArtifactLoader , XmlReader > readerSourceMap = new Dictionary < MetadataArtifactLoader , XmlReader > ( ) ;
IList < Schema > schemas ;
List < XmlReader > readers = composite . GetReaders ( readerSourceMap ) ;
try
{
IList < EdmSchemaError > schemaManagerErrors =
SchemaManager . ParseAndValidate ( readers ,
composite . GetPaths ( ) ,
SchemaDataModelOption . EntityDataModel ,
EdmProviderManifest . Instance ,
out schemas ) ;
errors . AddRange ( schemaManagerErrors ) ;
}
finally
{
if ( closeReaders )
{
MetadataUtil . DisposeXmlReaders ( readers ) ;
}
}
ThrowOnAnyNonWarningErrors ( errors ) ;
Debug . Assert ( readerSourceMap . ContainsKey ( sourceLoader ) , "the source loader didn't produce any of the xml readers..." ) ;
XmlReader sourceReader = readerSourceMap [ sourceLoader ] ;
// use the index of the "source" xml reader as the index of the "source" schema
Debug . Assert ( readers . Contains ( sourceReader ) , "the source reader is not in the list of readers" ) ;
int index = readers . IndexOf ( sourceReader ) ;
Debug . Assert ( index > = 0 , "couldn't find the source reader in the list of readers" ) ;
Debug . Assert ( readers . Count = = schemas . Count , "We have a different number of readers than schemas" ) ;
Schema sourceSchema = schemas [ index ] ;
Debug . Assert ( sourceSchema ! = null , "sourceSchema is null" ) ;
// create the EdmItemCollection from the schemas
EdmItemCollection itemCollection = new EdmItemCollection ( schemas ) ;
if ( EntityFrameworkVersionsUtil . ConvertToVersion ( itemCollection . EdmVersion ) > = EntityFrameworkVersions . Version2 )
{
throw EDesignUtil . InvalidOperation ( Strings . TargetEntityFrameworkVersionToNewForEntityClassGenerator ) ;
}
// generate code
ClientApiGenerator generator = new ClientApiGenerator ( sourceSchema , itemCollection , this , errors ) ;
generator . GenerateCode ( target , targetFilePath ) ;
}
#endregion
#region Private Methods
private static EdmSchemaError CreateErrorForException ( System . Data . EntityModel . SchemaObjectModel . ErrorCode errorCode , System . Exception exception , string sourceLocation )
{
Debug . Assert ( exception ! = null ) ;
Debug . Assert ( sourceLocation ! = null ) ;
return new EdmSchemaError ( exception . Message , ( int ) errorCode , EdmSchemaErrorSeverity . Error , sourceLocation , 0 , 0 , exception ) ;
}
internal static EdmSchemaError CreateErrorForException ( ModelBuilderErrorCode errorCode , System . Exception exception , string sourceLocation )
{
Debug . Assert ( exception ! = null ) ;
Debug . Assert ( sourceLocation ! = null ) ;
return new EdmSchemaError ( exception . Message , ( int ) errorCode , EdmSchemaErrorSeverity . Error , sourceLocation , 0 , 0 , exception ) ;
}
internal static EdmSchemaError CreateErrorForException ( ModelBuilderErrorCode errorCode , System . Exception exception )
{
Debug . Assert ( exception ! = null ) ;
return new EdmSchemaError ( exception . Message , ( int ) errorCode , EdmSchemaErrorSeverity . Error , null , 0 , 0 , exception ) ;
}
private void ThrowOnAnyNonWarningErrors ( List < EdmSchemaError > errors )
{
foreach ( EdmSchemaError error in errors )
{
if ( error . Severity ! = EdmSchemaErrorSeverity . Warning )
{
throw new TerminalErrorException ( ) ;
}
}
}
#endregion
#region Event Helpers
/// <summary>
/// Helper method that raises the TypeGenerated event
/// </summary>
/// <param name="eventArgs">The event arguments passed to the subscriber</param>
internal void RaiseTypeGeneratedEvent ( TypeGeneratedEventArgs eventArgs )
{
if ( this . OnTypeGenerated ! = null )
{
this . OnTypeGenerated ( this , eventArgs ) ;
}
}
/// <summary>
/// Helper method that raises the PropertyGenerated event
/// </summary>
/// <param name="eventArgs">The event arguments passed to the subscriber</param>
internal void RaisePropertyGeneratedEvent ( PropertyGeneratedEventArgs eventArgs )
{
if ( this . OnPropertyGenerated ! = null )
{
this . OnPropertyGenerated ( this , eventArgs ) ;
}
}
#endregion
}
}