701 lines
36 KiB
C#
701 lines
36 KiB
C#
//---------------------------------------------------------------------
|
|
// <copyright file="EntityCodeGenerator.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//
|
|
// @owner [....]
|
|
// @backupOwner [....]
|
|
//---------------------------------------------------------------------
|
|
//#define ENABLE_TEMPLATE_DEBUGGING
|
|
|
|
using System.CodeDom.Compiler;
|
|
using System.Collections.Generic;
|
|
using System.Data.Entity.Design.Common;
|
|
using System.Data.Entity.Design.SsdlGenerator;
|
|
using System.Data.Metadata.Edm;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.Versioning;
|
|
using System.Threading;
|
|
using System.Xml;
|
|
|
|
namespace System.Data.Entity.Design
|
|
{
|
|
public class EntityCodeGenerator
|
|
{
|
|
private LanguageOption _languageOption = LanguageOption.GenerateCSharpCode;
|
|
private EdmToObjectNamespaceMap _edmToObjectNamespaceMap = new EdmToObjectNamespaceMap();
|
|
|
|
public EntityCodeGenerator(LanguageOption languageOption)
|
|
{
|
|
_languageOption = EDesignUtil.CheckLanguageOptionArgument(languageOption, "languageOption");
|
|
}
|
|
|
|
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; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a source code file that contains object layer code generated from the specified conceptual schema definition
|
|
/// language (CSDL) file. The list of schema file paths is used to resolve any references contained in the CSDL file.
|
|
/// Note that the targetEntityFrameworkVersion parameter uses internal EntityFramework version numbers as described
|
|
/// in the <see cref="EntityFrameworkVersions"/> class.
|
|
/// </summary>
|
|
/// <param name="sourceEdmSchemaFilePath">The path of the CSDL file.</param>
|
|
/// <param name="targetPath">The path of the file that contains the generated object layer code.</param>
|
|
/// <param name="additionalEdmSchemaFilePaths">
|
|
/// A list of schema file paths that can be used to resolve any references in the source schema (the CSDL file).
|
|
/// If the source schema does not have any dependencies, pass in an empty list.
|
|
/// </param>
|
|
/// <param name="targetEntityFrameworkVersion">The internal Entity Framework version that is being targeted.</param>
|
|
/// <returns>A list of <see cref="EdmSchemaError"/> objects that contains any generated errors.</returns>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
|
|
[ResourceExposure(ResourceScope.Machine)] // for sourceEdmSchemaFilePath, targetPath and additionalEdmSchema which are machine resources
|
|
[ResourceConsumption(ResourceScope.Machine)] // for InternalGenerateCode method call. But the path is not created in this method.
|
|
public IList<EdmSchemaError> GenerateCode(string sourceEdmSchemaFilePath, string targetPath, IEnumerable<string> additionalEdmSchemaFilePaths, Version targetEntityFrameworkVersion)
|
|
{
|
|
EDesignUtil.CheckTargetEntityFrameworkVersionArgument(targetEntityFrameworkVersion, "targetEntityFrameworkVersion");
|
|
|
|
EntityUtil.CheckStringArgument(sourceEdmSchemaFilePath, "sourceEdmSchemaFilePath");
|
|
EDesignUtil.CheckArgumentNull(additionalEdmSchemaFilePaths, "additionalEdmSchemaFilePaths");
|
|
EntityUtil.CheckStringArgument(targetPath, "targetPath");
|
|
EDesignUtil.CheckTargetEntityFrameworkVersionArgument(targetEntityFrameworkVersion, "targetEntityFrameworkVersion");
|
|
|
|
return InternalGenerateCode(sourceEdmSchemaFilePath, new LazyTextWriterCreator(targetPath), additionalEdmSchemaFilePaths, targetEntityFrameworkVersion);
|
|
}
|
|
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
|
|
[ResourceExposure(ResourceScope.Machine)] // for sourceEdmSchemaFilePath, targetPath and additionalEdmSchema which are machine resources
|
|
[ResourceConsumption(ResourceScope.Machine)] // for InternalGenerateCode method call. But the path is not created in this method.
|
|
public IList<EdmSchemaError> GenerateCode(string sourceEdmSchemaFilePath, string targetPath, IEnumerable<string> additionalEdmSchemaFilePaths)
|
|
{
|
|
Version targetFrameworkVersion;
|
|
IList<EdmSchemaError> errors = GetMinimumTargetFrameworkVersion(sourceEdmSchemaFilePath, out targetFrameworkVersion);
|
|
|
|
if (errors.Where(e => e.Severity == EdmSchemaErrorSeverity.Error).Any())
|
|
{
|
|
return errors;
|
|
}
|
|
|
|
return errors.Concat(GenerateCode(sourceEdmSchemaFilePath, targetPath, additionalEdmSchemaFilePaths,
|
|
targetFrameworkVersion)).ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a source code file that contains the object layer code generated from the specified conceptual schema
|
|
/// definition language (CSDL) file. Note that the targetEntityFrameworkVersion parameter uses internal Entity
|
|
/// Framework version numbers as described in the <see cref="EntityFrameworkVersions"/> class.
|
|
/// </summary>
|
|
/// <param name="sourceEdmSchemaFilePath">The path of the CSDL file.</param>
|
|
/// <param name="targetPath">The path of the file that contains the generated object layer code.</param>
|
|
/// <param name="targetEntityFrameworkVersion">The internal Entity Framework version that is being targeted.</param>
|
|
/// <returns>A list of <see cref="EdmSchemaError"/> objects that contains any generated errors.</returns>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
|
|
[ResourceExposure(ResourceScope.Machine)] // for sourceEdmSchemaFilePath and targetPath which are Machine resources
|
|
[ResourceConsumption(ResourceScope.Machine)] // for InternalGenerateCode method call. But the path is not created in this method.
|
|
public IList<EdmSchemaError> GenerateCode(string sourceEdmSchemaFilePath, string targetPath, Version targetEntityFrameworkVersion)
|
|
{
|
|
EntityUtil.CheckStringArgument(sourceEdmSchemaFilePath, "sourceEdmSchemaFilePath");
|
|
EntityUtil.CheckStringArgument(targetPath, "targetPath");
|
|
EDesignUtil.CheckTargetEntityFrameworkVersionArgument(targetEntityFrameworkVersion, "targetEntityFrameworkVersion");
|
|
|
|
return InternalGenerateCode(sourceEdmSchemaFilePath, new LazyTextWriterCreator(targetPath), null, targetEntityFrameworkVersion);
|
|
}
|
|
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
|
|
[ResourceExposure(ResourceScope.Machine)] // for sourceEdmSchemaFilePath and targetPath which are Machine resources
|
|
[ResourceConsumption(ResourceScope.Machine)] // for InternalGenerateCode method call. But the path is not created in this method.
|
|
public IList<EdmSchemaError> GenerateCode(string sourceEdmSchemaFilePath, string targetPath)
|
|
{
|
|
Version targetFrameworkVersion;
|
|
IList<EdmSchemaError> errors = GetMinimumTargetFrameworkVersion(sourceEdmSchemaFilePath, out targetFrameworkVersion);
|
|
|
|
if (errors.Where(e => e.Severity == EdmSchemaErrorSeverity.Error).Any())
|
|
{
|
|
return errors;
|
|
}
|
|
|
|
return errors.Concat(GenerateCode(sourceEdmSchemaFilePath, targetPath, targetFrameworkVersion)).ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates object layer code using the conceptual schema definition language (CSDL) specified in the
|
|
/// XmlReader object, and outputs the generated code to a TextWriter.
|
|
/// Note that the targetEntityFrameworkVersion parameter uses internal EntityFramework version numbers as
|
|
/// described in the <see cref="EntityFrameworkVersions"/> class.
|
|
/// </summary>
|
|
/// <param name="sourceEdmSchema">An XmlReader that contains the CSDL.</param>
|
|
/// <param name="target">The TextWriter to which the object layer code is written.</param>
|
|
/// <param name="targetEntityFrameworkVersion">The internal Entity Framework version that is being targeted.</param>
|
|
/// <returns>A list of <see cref="EdmSchemaError"/> objects that contains any generated errors.</returns>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
|
|
[ResourceConsumption(ResourceScope.Machine)] // temp file creation, and passing to InternalGenerateCode
|
|
public IList<EdmSchemaError> GenerateCode(XmlReader sourceEdmSchema, TextWriter target, Version targetEntityFrameworkVersion)
|
|
{
|
|
EDesignUtil.CheckArgumentNull(sourceEdmSchema, "sourceEdmSchema");
|
|
EDesignUtil.CheckArgumentNull(target, "target");
|
|
EDesignUtil.CheckTargetEntityFrameworkVersionArgument(targetEntityFrameworkVersion, "targetEntityFrameworkVersion");
|
|
|
|
Version schemaVersion;
|
|
if (!IsValidSchema(sourceEdmSchema, out schemaVersion))
|
|
{
|
|
return new List<EdmSchemaError>() { CreateSourceEdmSchemaNotValidError() };
|
|
}
|
|
|
|
using (TempFileCollection collection = new TempFileCollection())
|
|
{
|
|
string tempSourceEdmSchemaPath = collection.AddExtension(XmlConstants.CSpaceSchemaExtension);
|
|
SaveXmlReaderToFile(sourceEdmSchema, tempSourceEdmSchemaPath);
|
|
|
|
return InternalGenerateCode(tempSourceEdmSchemaPath, schemaVersion, new LazyTextWriterCreator(target), null, targetEntityFrameworkVersion);
|
|
}
|
|
}
|
|
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
|
|
[ResourceConsumption(ResourceScope.Machine)] // temp file creation, and passing to InternalGenerateCode
|
|
public IList<EdmSchemaError> GenerateCode(XmlReader sourceEdmSchema, TextWriter target)
|
|
{
|
|
Version targetFrameworkVersion;
|
|
IList<EdmSchemaError> errors = GetMinimumTargetFrameworkVersion(sourceEdmSchema, out targetFrameworkVersion);
|
|
|
|
if (errors.Where(e => e.Severity == EdmSchemaErrorSeverity.Error).Any())
|
|
{
|
|
return errors;
|
|
}
|
|
|
|
return errors.Concat(GenerateCode(sourceEdmSchema, target, targetFrameworkVersion)).ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a source code file that contains the object layer code generated from the specified conceptual schema
|
|
/// definition language (CSDL) file. Note that the targetEntityFrameworkVersion parameter uses internal Entity
|
|
/// Framework version numbers as described in the <see cref="EntityFrameworkVersions"/> class.
|
|
/// </summary>
|
|
/// <param name="sourceEdmSchema">An XmlReader that contains the CSDL.</param>
|
|
/// <param name="target">The TextWriter to which the object layer code is written.</param>
|
|
/// <param name="additionalEdmSchemas">
|
|
/// A list of XmlReader objects that contain schemas that are referenced by the source schema (the CSDL).
|
|
/// If the source schema does not have any dependencies, pass in an empty IList object.
|
|
/// </param>
|
|
/// <param name="targetEntityFrameworkVersion">The internal Entity Framework version that is being targeted.</param>
|
|
/// <returns>A list of <see cref="EdmSchemaError"/> objects that contains any generated errors.</returns>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
|
|
[ResourceConsumption(ResourceScope.Machine)] //Temp file creation, and passing to InternalGenerateCode
|
|
public IList<EdmSchemaError> GenerateCode(XmlReader sourceEdmSchema, TextWriter target, IEnumerable<XmlReader> additionalEdmSchemas, Version targetEntityFrameworkVersion)
|
|
{
|
|
EDesignUtil.CheckArgumentNull(sourceEdmSchema, "sourceEdmSchema");
|
|
EDesignUtil.CheckArgumentNull(additionalEdmSchemas, "additionalEdmSchemas");
|
|
EDesignUtil.CheckArgumentNull(target, "target");
|
|
EDesignUtil.CheckTargetEntityFrameworkVersionArgument(targetEntityFrameworkVersion, "targetEntityFrameworkVersion");
|
|
|
|
Version schemaVersion;
|
|
if (!IsValidSchema(sourceEdmSchema, out schemaVersion))
|
|
{
|
|
return new List<EdmSchemaError>() { CreateSourceEdmSchemaNotValidError() };
|
|
}
|
|
|
|
using (TempFileCollection collection = new TempFileCollection())
|
|
{
|
|
|
|
string tempSourceEdmSchemaPath = collection.AddExtension(XmlConstants.CSpaceSchemaExtension);
|
|
SaveXmlReaderToFile(sourceEdmSchema, tempSourceEdmSchemaPath);
|
|
List<string> additionalTempPaths = new List<string>();
|
|
foreach (XmlReader reader in additionalEdmSchemas)
|
|
{
|
|
|
|
string temp = Path.GetTempFileName() + XmlConstants.CSpaceSchemaExtension;
|
|
SaveXmlReaderToFile(reader, temp);
|
|
additionalTempPaths.Add(temp);
|
|
collection.AddFile(temp, false);
|
|
}
|
|
return InternalGenerateCode(tempSourceEdmSchemaPath, schemaVersion, new LazyTextWriterCreator(target), additionalTempPaths, targetEntityFrameworkVersion);
|
|
}
|
|
}
|
|
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
|
|
[ResourceConsumption(ResourceScope.Machine)] //Temp file creation, and passing to InternalGenerateCode
|
|
public IList<EdmSchemaError> GenerateCode(XmlReader sourceEdmSchema, TextWriter target, IEnumerable<XmlReader> additionalEdmSchemas)
|
|
{
|
|
Version targetFrameworkVersion;
|
|
IList<EdmSchemaError> errors = GetMinimumTargetFrameworkVersion(sourceEdmSchema, out targetFrameworkVersion);
|
|
|
|
if (errors.Where(e => e.Severity == EdmSchemaErrorSeverity.Error).Any())
|
|
{
|
|
return errors;
|
|
}
|
|
|
|
return errors.Concat(GenerateCode(sourceEdmSchema, target, additionalEdmSchemas, targetFrameworkVersion)).ToList();
|
|
}
|
|
|
|
// this overload is for entry points that don't pass in readers, so we need to actually create a reader to get the schemaVersion out
|
|
private IList<EdmSchemaError> InternalGenerateCode(string sourceEdmSchemaFilePath, LazyTextWriterCreator textWriter, IEnumerable<string> additionalEdmSchemaFilePaths, Version targetFrameworkVersion)
|
|
{
|
|
// do this check to maintain backwards compatibility with the behavior shipped in
|
|
// framework v4
|
|
if (!File.Exists(sourceEdmSchemaFilePath))
|
|
{
|
|
return new List<EdmSchemaError>() { new EdmSchemaError(Strings.EdmSchemaFileNotFound(sourceEdmSchemaFilePath), (int)ModelBuilderErrorCode.FileNotFound, EdmSchemaErrorSeverity.Error, sourceEdmSchemaFilePath, 0, 0) };
|
|
}
|
|
|
|
using (XmlReader reader = XmlReader.Create(sourceEdmSchemaFilePath))
|
|
{
|
|
Version schemaVersion;
|
|
if (!IsValidSchema(reader, out schemaVersion))
|
|
{
|
|
return new List<EdmSchemaError>() { CreateSourceEdmSchemaNotValidError() };
|
|
}
|
|
|
|
return InternalGenerateCode(sourceEdmSchemaFilePath, schemaVersion, textWriter, additionalEdmSchemaFilePaths, targetFrameworkVersion);
|
|
}
|
|
}
|
|
|
|
private bool IsValidSchema(XmlReader reader, out Version entityFrameworkVersion)
|
|
{
|
|
double schemaVersion;
|
|
DataSpace dataSpace;
|
|
if (System.Data.EntityModel.SchemaObjectModel.SchemaManager.TryGetSchemaVersion(reader, out schemaVersion, out dataSpace) &&
|
|
dataSpace == DataSpace.CSpace)
|
|
{
|
|
entityFrameworkVersion = EntityFrameworkVersionsUtil.ConvertToVersion(schemaVersion);
|
|
return true;
|
|
}
|
|
else if (EntityFrameworkVersions.TryGetEdmxVersion(reader, out entityFrameworkVersion))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private IList<EdmSchemaError> GetMinimumTargetFrameworkVersion(string sourceEdmSchemaFilePath, out Version targetFrameworkVersion)
|
|
{
|
|
targetFrameworkVersion = null;
|
|
try
|
|
{
|
|
using (XmlReader reader = XmlReader.Create(sourceEdmSchemaFilePath))
|
|
{
|
|
return GetMinimumTargetFrameworkVersion(reader, out targetFrameworkVersion);
|
|
}
|
|
}
|
|
catch (System.IO.FileNotFoundException ex)
|
|
{
|
|
return new List<EdmSchemaError>() { EntityClassGenerator.CreateErrorForException(ModelBuilderErrorCode.FileNotFound, ex) };
|
|
}
|
|
}
|
|
|
|
private IList<EdmSchemaError> GetMinimumTargetFrameworkVersion(XmlReader sourceEdmSchemaXmlReader, out Version targetFrameworkVersion)
|
|
{
|
|
List<EdmSchemaError> errorList = new List<EdmSchemaError>();
|
|
if (!IsValidSchema(sourceEdmSchemaXmlReader, out targetFrameworkVersion))
|
|
{
|
|
errorList.Add(CreateSourceEdmSchemaNotValidError());
|
|
return errorList;
|
|
}
|
|
|
|
if (targetFrameworkVersion < EntityFrameworkVersions.Version2)
|
|
{
|
|
targetFrameworkVersion = EntityFrameworkVersions.Version2;
|
|
}
|
|
|
|
if (targetFrameworkVersion > EntityFrameworkVersions.Default)
|
|
{
|
|
errorList.Add(new EdmSchemaError(Strings.DefaultTargetVersionTooLow(EntityFrameworkVersions.Default, targetFrameworkVersion), (int)ModelBuilderErrorCode.SchemaVersionHigherThanTargetVersion, EdmSchemaErrorSeverity.Warning));
|
|
}
|
|
|
|
return errorList;
|
|
}
|
|
|
|
private IList<EdmSchemaError> InternalGenerateCode(string sourceEdmSchemaFilePath, Version schemaVersion, LazyTextWriterCreator textWriter, IEnumerable<string> additionalEdmSchemaFilePaths, Version targetFrameworkVersion)
|
|
{
|
|
List<EdmSchemaError> errors = new List<EdmSchemaError>();
|
|
try
|
|
{
|
|
if (targetFrameworkVersion == EntityFrameworkVersions.Version1)
|
|
{
|
|
errors.Add(new EdmSchemaError(Strings.EntityCodeGenTargetTooLow, (int)ModelBuilderErrorCode.TargetVersionNotSupported, EdmSchemaErrorSeverity.Error));
|
|
return errors;
|
|
}
|
|
|
|
if (!MetadataItemCollectionFactory.ValidateActualVersionAgainstTarget(targetFrameworkVersion, schemaVersion, errors))
|
|
{
|
|
return errors;
|
|
}
|
|
|
|
if (schemaVersion == EntityFrameworkVersions.EdmVersion1_1)
|
|
{
|
|
return GenerateCodeFor1_1Schema(sourceEdmSchemaFilePath, textWriter, additionalEdmSchemaFilePaths);
|
|
}
|
|
|
|
ReflectionAdapter codeGenerator = ReflectionAdapter.Create(_languageOption, targetFrameworkVersion);
|
|
codeGenerator.SourceCsdlPath = sourceEdmSchemaFilePath;
|
|
codeGenerator.ReferenceCsdlPaths = additionalEdmSchemaFilePaths;
|
|
codeGenerator.EdmToObjectNamespaceMap = _edmToObjectNamespaceMap.AsDictionary();
|
|
|
|
string code = codeGenerator.TransformText();
|
|
|
|
if (codeGenerator.Errors.Count != 0)
|
|
{
|
|
ModelBuilderErrorCode errorCode = ModelBuilderErrorCode.PreprocessTemplateTransformationError;
|
|
errors.AddRange(codeGenerator.Errors.OfType<CompilerError>().Select(c => ConvertToEdmSchemaError(c, errorCode)));
|
|
|
|
if (codeGenerator.Errors.HasErrors)
|
|
return errors;
|
|
}
|
|
|
|
using (TextWriter writer = textWriter.GetOrCreateTextWriter())
|
|
{
|
|
writer.Write(code);
|
|
}
|
|
}
|
|
catch (System.UnauthorizedAccessException ex)
|
|
{
|
|
errors.Add(EntityClassGenerator.CreateErrorForException(ModelBuilderErrorCode.SecurityError, ex));
|
|
}
|
|
catch (System.IO.FileNotFoundException ex)
|
|
{
|
|
errors.Add(EntityClassGenerator.CreateErrorForException(ModelBuilderErrorCode.FileNotFound, ex));
|
|
}
|
|
catch (System.Security.SecurityException ex)
|
|
{
|
|
errors.Add(EntityClassGenerator.CreateErrorForException(ModelBuilderErrorCode.SecurityError, ex));
|
|
}
|
|
catch (System.IO.DirectoryNotFoundException ex)
|
|
{
|
|
errors.Add(EntityClassGenerator.CreateErrorForException(ModelBuilderErrorCode.DirectoryNotFound, ex));
|
|
}
|
|
catch (System.IO.IOException ex)
|
|
{
|
|
errors.Add(EntityClassGenerator.CreateErrorForException(ModelBuilderErrorCode.IOException, ex));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (MetadataUtil.IsCatchableExceptionType(e))
|
|
{
|
|
errors.Add(EntityClassGenerator.CreateErrorForException(ModelBuilderErrorCode.PreprocessTemplateTransformationError, e, sourceEdmSchemaFilePath));
|
|
}
|
|
else
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
private static EdmSchemaError CreateSourceEdmSchemaNotValidError()
|
|
{
|
|
return new EdmSchemaError(Strings.EdmSchemaNotValid, (int)ModelBuilderErrorCode.SourceSchemaIsInvalid, EdmSchemaErrorSeverity.Error);
|
|
}
|
|
|
|
private IList<EdmSchemaError> GenerateCodeFor1_1Schema(string sourceEdmSchemaFilePath, LazyTextWriterCreator textWriter, IEnumerable<string> additionalEdmSchemaFilePaths)
|
|
{
|
|
EntityClassGenerator generator = new EntityClassGenerator(_languageOption);
|
|
string target = textWriter.TargetFilePath;
|
|
bool cleanUpTarget = false;
|
|
try
|
|
{
|
|
if (textWriter.IsUserSuppliedTextWriter)
|
|
{
|
|
cleanUpTarget = true;
|
|
target = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
|
|
|
|
}
|
|
|
|
IList<EdmSchemaError> errors = generator.GenerateCode(sourceEdmSchemaFilePath, target, additionalEdmSchemaFilePaths != null ? additionalEdmSchemaFilePaths : Enumerable.Empty<string>());
|
|
|
|
if (textWriter.IsUserSuppliedTextWriter && !errors.Any(e => e.Severity == EdmSchemaErrorSeverity.Error))
|
|
{
|
|
textWriter.GetOrCreateTextWriter().Write(File.ReadAllText(target));
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
finally
|
|
{
|
|
if (cleanUpTarget && target != null && File.Exists(target))
|
|
{
|
|
File.Delete(target);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private static void SaveXmlReaderToFile(XmlReader schema, string tempSchemaPath)
|
|
{
|
|
using (XmlWriter writer = XmlWriter.Create(tempSchemaPath))
|
|
{
|
|
writer.WriteNode(schema, false);
|
|
}
|
|
}
|
|
|
|
private static EdmSchemaError ConvertToEdmSchemaError(CompilerError error, ModelBuilderErrorCode defaultErrorCode)
|
|
{
|
|
int errorNumber;
|
|
string message = error.ErrorText;
|
|
bool usePositionInfo = true;
|
|
EdmSchemaErrorSeverity severity = error.IsWarning ? EdmSchemaErrorSeverity.Warning : EdmSchemaErrorSeverity.Error;
|
|
if (int.TryParse(error.ErrorNumber, out errorNumber))
|
|
{
|
|
if (error.Line == -1 && error.Column == -1)
|
|
{
|
|
usePositionInfo = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
message = String.Format(CultureInfo.InvariantCulture, "{0}({1})", error.ErrorText, error.ErrorNumber);
|
|
errorNumber = (int)defaultErrorCode;
|
|
}
|
|
|
|
if (usePositionInfo)
|
|
{
|
|
return new EdmSchemaError(message,
|
|
errorNumber,
|
|
severity,
|
|
error.FileName,
|
|
error.Line,
|
|
error.Column);
|
|
}
|
|
else
|
|
{
|
|
return new EdmSchemaError(message,
|
|
errorNumber,
|
|
severity);
|
|
}
|
|
}
|
|
|
|
|
|
static Type _vbCodeGeneratorTypeV2 = null;
|
|
static Type _vbCodeGeneratorTypeV3 = null;
|
|
static Type _csharpCodeGeneratorTypeV2 = null;
|
|
static Type _csharpCodeGeneratorTypeV3 = null;
|
|
|
|
private const string CSharpTemplateCodeGenTypeName = "TemplateCodeGenerators.CSharpCodeGenTemplate";
|
|
private const string VBTemplateCodeGenTypeName = "TemplateCodeGenerators.VBCodeGenTemplate";
|
|
private const string CSharpTemplateCodeGenV3TypeName = CSharpTemplateCodeGenTypeName + "V50";
|
|
private const string VBTemplateCodeGenV3TypeName = VBTemplateCodeGenTypeName + "V50";
|
|
private const string CSharpTemplateCodeGenResourceV2 = CSharpTemplateCodeGenTypeName + ".cs";
|
|
private const string CSharpTemplateCodeGenResourceV3 = CSharpTemplateCodeGenTypeName + "V5.0.cs";
|
|
private const string VBTemplateCodeGenResourceV2 = VBTemplateCodeGenTypeName + ".vb";
|
|
private const string VBTemplateCodeGenResourceV3 = VBTemplateCodeGenTypeName + "V5.0.vb";
|
|
|
|
private static object CreateCSharpCodeGeneratorV2()
|
|
{
|
|
if (_csharpCodeGeneratorTypeV2 == null)
|
|
{
|
|
Type type = CreateCodeGeneratorType(new Microsoft.CSharp.CSharpCodeProvider(), CSharpTemplateCodeGenResourceV2, CSharpTemplateCodeGenTypeName);
|
|
Interlocked.Exchange(ref _csharpCodeGeneratorTypeV2, type);
|
|
}
|
|
|
|
return Activator.CreateInstance(_csharpCodeGeneratorTypeV2);
|
|
}
|
|
|
|
private static object CreateCSharpCodeGeneratorV3()
|
|
{
|
|
if (_csharpCodeGeneratorTypeV3 == null)
|
|
{
|
|
Type type = CreateCodeGeneratorType(new Microsoft.CSharp.CSharpCodeProvider(), CSharpTemplateCodeGenResourceV3, CSharpTemplateCodeGenV3TypeName);
|
|
Interlocked.Exchange(ref _csharpCodeGeneratorTypeV3, type);
|
|
}
|
|
|
|
return Activator.CreateInstance(_csharpCodeGeneratorTypeV3);
|
|
}
|
|
|
|
private static object CreateVBCodeGeneratorV2()
|
|
{
|
|
if (_vbCodeGeneratorTypeV2 == null)
|
|
{
|
|
Type type = CreateCodeGeneratorType(new Microsoft.VisualBasic.VBCodeProvider(), VBTemplateCodeGenResourceV2, VBTemplateCodeGenTypeName);
|
|
Interlocked.Exchange(ref _vbCodeGeneratorTypeV2, type);
|
|
}
|
|
|
|
return Activator.CreateInstance(_vbCodeGeneratorTypeV2);
|
|
}
|
|
|
|
private static object CreateVBCodeGeneratorV3()
|
|
{
|
|
if (_vbCodeGeneratorTypeV3 == null)
|
|
{
|
|
Type type = CreateCodeGeneratorType(new Microsoft.VisualBasic.VBCodeProvider(), VBTemplateCodeGenResourceV3, VBTemplateCodeGenV3TypeName);
|
|
Interlocked.Exchange(ref _vbCodeGeneratorTypeV3, type);
|
|
}
|
|
|
|
return Activator.CreateInstance(_vbCodeGeneratorTypeV3);
|
|
}
|
|
|
|
private static Type CreateCodeGeneratorType(System.CodeDom.Compiler.CodeDomProvider compilerProvider, string resourceName, string typeName)
|
|
{
|
|
string sourceCode = null;
|
|
using (Stream sourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
|
|
using (StreamReader reader = new StreamReader(sourceStream))
|
|
{
|
|
sourceCode = reader.ReadToEnd();
|
|
}
|
|
|
|
CompilerParameters compilerParams = new CompilerParameters();
|
|
compilerParams.CompilerOptions = "/d:PREPROCESSED_TEMPLATE";
|
|
compilerParams.GenerateInMemory = true;
|
|
compilerParams.GenerateExecutable = false;
|
|
// grab the assemblies by location so that we don't compile against one that we didn't reference
|
|
compilerParams.ReferencedAssemblies.AddRange(new string[] {
|
|
typeof(System.CodeDom.Compiler.CodeDomProvider).Assembly.Location, // System.dll
|
|
typeof(System.Linq.Enumerable).Assembly.Location, // System.Core.dll
|
|
typeof(System.Data.Objects.ObjectContext).Assembly.Location, // System.Data.Entity.dll
|
|
typeof(System.Data.Entity.Design.EntityCodeGenerator).Assembly.Location, // System.Data.Entity.Design.dll
|
|
typeof(System.Data.DbType).Assembly.Location, // System.Data.dll
|
|
typeof(System.Xml.XmlAttribute).Assembly.Location, // System.Xml.dll
|
|
typeof(System.Xml.Linq.XElement).Assembly.Location, // System.Xml.Linq.dll
|
|
});
|
|
|
|
#if !ENABLE_TEMPLATE_DEBUGGING
|
|
CompilerResults results = compilerProvider.CompileAssemblyFromSource(compilerParams, sourceCode);
|
|
#else
|
|
// enables debugging
|
|
compilerParams.GenerateInMemory = false;
|
|
compilerParams.IncludeDebugInformation = true;
|
|
string baseName = Path.GetFileNameWithoutExtension(Path.GetTempFileName()) + ".";
|
|
compilerParams.OutputAssembly = Path.Combine(Path.GetTempPath(), baseName + typeName + ".Assembly.dll");
|
|
string sourceFileName = Path.Combine(Path.GetTempPath(), baseName + typeName + ".Source." + compilerProvider.FileExtension);
|
|
File.WriteAllText(sourceFileName, sourceCode);
|
|
CompilerResults results = compilerProvider.CompileAssemblyFromFile(compilerParams, sourceFileName);
|
|
#warning DO NOT CHECK IN LIKE THIS, Dynamic Assembly Debugging is enabled
|
|
#endif
|
|
|
|
|
|
if (results.Errors.HasErrors)
|
|
{
|
|
string message = results.Errors.OfType<CompilerError>().Aggregate(string.Empty, (accumulated, input) => accumulated == string.Empty ? input.ToString() : accumulated + Environment.NewLine + input.ToString());
|
|
throw EDesignUtil.InvalidOperation(message);
|
|
}
|
|
|
|
return results.CompiledAssembly.GetType(typeName);
|
|
}
|
|
|
|
|
|
|
|
private class ReflectionAdapter
|
|
{
|
|
private object _instance;
|
|
private MethodInfo _transformText;
|
|
private PropertyInfo _sourceCsdlPath;
|
|
private PropertyInfo _referenceCsdlPaths;
|
|
private PropertyInfo _errors;
|
|
private PropertyInfo _edmToObjectNamespaceMap;
|
|
|
|
internal ReflectionAdapter(object instance)
|
|
{
|
|
_instance = instance;
|
|
Type type = _instance.GetType();
|
|
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
|
|
_transformText = type.GetMethod("TransformText", flags, null, new Type[0], null);
|
|
Debug.Assert(_transformText != null, "Unable to find method, did the signature or name change?");
|
|
|
|
_sourceCsdlPath = type.GetProperty("SourceCsdlPath", flags, null, typeof(String), new Type[0], null);
|
|
Debug.Assert(_sourceCsdlPath != null, "Unable to find property, did the signature or name change?");
|
|
|
|
_referenceCsdlPaths = type.GetProperty("ReferenceCsdlPaths", flags, null, typeof(IEnumerable<String>), new Type[0], null);
|
|
Debug.Assert(_referenceCsdlPaths != null, "Unable to find property, did the signature or name change?");
|
|
|
|
_errors = type.GetProperty("Errors", flags, null, typeof(CompilerErrorCollection), new Type[0], null);
|
|
Debug.Assert(_errors != null, "Unable to find property, did the signature or name change?");
|
|
|
|
_edmToObjectNamespaceMap = type.GetProperty("EdmToObjectNamespaceMap", flags, null, typeof(Dictionary<string, string>), new Type[0], null);
|
|
Debug.Assert(_edmToObjectNamespaceMap != null, "Unable to find property, did the signature or name change?");
|
|
}
|
|
|
|
internal CompilerErrorCollection Errors
|
|
{
|
|
get { return (CompilerErrorCollection)_errors.GetValue(_instance, null); }
|
|
}
|
|
|
|
internal IEnumerable<String> ReferenceCsdlPaths
|
|
{
|
|
set
|
|
{
|
|
_referenceCsdlPaths.SetValue(_instance, value, null);
|
|
}
|
|
}
|
|
|
|
internal string SourceCsdlPath
|
|
{
|
|
get
|
|
{
|
|
return (String)_sourceCsdlPath.GetValue(_instance, null);
|
|
}
|
|
set
|
|
{
|
|
_sourceCsdlPath.SetValue(_instance, value, null);
|
|
}
|
|
}
|
|
|
|
internal Dictionary<string, string> EdmToObjectNamespaceMap
|
|
{
|
|
set { _edmToObjectNamespaceMap.SetValue(_instance, value, null); }
|
|
}
|
|
|
|
internal string TransformText()
|
|
{
|
|
try
|
|
{
|
|
return (string)_transformText.Invoke(_instance, new object[0]);
|
|
}
|
|
catch (TargetInvocationException e)
|
|
{
|
|
Exception actual = e.InnerException != null ? e.InnerException : e;
|
|
|
|
System.CodeDom.Compiler.CompilerError error = new System.CodeDom.Compiler.CompilerError();
|
|
error.ErrorText = actual.Message;
|
|
error.IsWarning = false;
|
|
error.FileName = SourceCsdlPath;
|
|
Errors.Add(error);
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
}
|
|
|
|
internal static ReflectionAdapter Create(LanguageOption language, Version targetEntityFrameworkVersion)
|
|
{
|
|
if (language == LanguageOption.GenerateCSharpCode)
|
|
{
|
|
if (targetEntityFrameworkVersion >= EntityFrameworkVersions.Latest)
|
|
{
|
|
return new ReflectionAdapter(CreateCSharpCodeGeneratorV3());
|
|
}
|
|
else
|
|
{
|
|
return new ReflectionAdapter(CreateCSharpCodeGeneratorV2());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.Assert(language == LanguageOption.GenerateVBCode, "Did you add a new option?");
|
|
if (targetEntityFrameworkVersion >= EntityFrameworkVersions.Latest)
|
|
{
|
|
return new ReflectionAdapter(CreateVBCodeGeneratorV3());
|
|
}
|
|
else
|
|
{
|
|
return new ReflectionAdapter(CreateVBCodeGeneratorV2());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|