<#@ assembly name="System.Core" #> <#@ assembly name="System.Data" #> <#@ assembly name="System.Data.Entity" #> <#@ assembly name="System.Data.Entity.Design" #> <#@ assembly name="System.Xml" #> <#@ assembly name="System.Xml.Linq"#> <#@ assembly name="EnvDTE"#> <#@ import namespace="System" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.Data.Objects" #> <#@ import namespace="System.Data.Objects.DataClasses" #> <#@ import namespace="System.Xml" #> <#@ import namespace="System.Xml.Linq" #> <#@ import namespace="System.Globalization" #> <#@ import namespace="System.Reflection" #> <#@ import namespace="System.Data.Metadata.Edm" #> <#@ import namespace="System.Data.Mapping" #> <#@ import namespace="System.Data.Entity.Design" #> <#@ import namespace="System.CodeDom" #> <#@ import namespace="System.CodeDom.Compiler" #> <#@ import namespace="Microsoft.CSharp"#> <#@ import namespace="System.Text"#> <#+ // Copyright (c) Microsoft Corporation. All rights reserved. /// /// Responsible for helping to create source code that is /// correctly formated and functional /// public class CodeGenerationTools { private readonly DynamicTextTransformation _textTransformation; private readonly CSharpCodeProvider _code; private readonly MetadataTools _ef; /// /// Initializes a new CodeGenerationTools object with the TextTransformation (T4 generated class) /// that is currently running /// public CodeGenerationTools(object textTransformation) { if (textTransformation == null) { throw new ArgumentNullException("textTransformation"); } _textTransformation = DynamicTextTransformation.Create(textTransformation); _code = new CSharpCodeProvider(); _ef = new MetadataTools(_textTransformation); FullyQualifySystemTypes = false; CamelCaseFields = true; } /// /// When true, all types that are not being generated /// are fully qualified to keep them from conflicting with /// types that are being generated. Useful when you have /// something like a type being generated named System. /// /// Default is false. /// public bool FullyQualifySystemTypes { get; set; } /// /// When true, the field names are Camel Cased, /// otherwise they will preserve the case they /// start with. /// /// Default is true. /// public bool CamelCaseFields { get; set; } /// /// Returns the NamespaceName suggested by VS if running inside VS. Otherwise, returns /// null. /// public string VsNamespaceSuggestion() { string suggestion = _textTransformation.Host.ResolveParameterValue("directiveId", "namespaceDirectiveProcessor", "namespaceHint"); if (String.IsNullOrEmpty(suggestion)) { return null; } return suggestion; } /// /// Returns a string that is safe for use as an identifier in C#. /// Keywords are escaped. /// public string Escape(string name) { if (name == null) { return null; } return _code.CreateEscapedIdentifier(name); } /// /// Returns the name of the TypeUsage's EdmType that is safe for /// use as an identifier. /// public string Escape(TypeUsage typeUsage) { if (typeUsage == null) { return null; } if (typeUsage.EdmType is ComplexType || typeUsage.EdmType is EntityType) { return Escape(typeUsage.EdmType.Name); } else if (typeUsage.EdmType is PrimitiveType) { Type clrType = _ef.ClrType(typeUsage); string typeName = Escape(clrType); if (clrType.IsValueType && _ef.IsNullable(typeUsage)) { return String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName); } return typeName; } else if (typeUsage.EdmType is CollectionType) { return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", Escape(((CollectionType)typeUsage.EdmType).TypeUsage)); } throw new ArgumentException("typeUsage"); } /// /// Returns the name of the EdmMember that is safe for /// use as an identifier. /// public string Escape(EdmMember member) { if (member == null) { return null; } return Escape(member.Name); } /// /// Returns the name of the EdmType that is safe for /// use as an identifier. /// public string Escape(EdmType type) { if (type == null) { return null; } return Escape(type.Name); } /// /// Returns the name of the EdmFunction that is safe for /// use as an identifier. /// public string Escape(EdmFunction function) { if (function == null) { return null; } return Escape(function.Name); } /// /// Returns the name of the EntityContainer that is safe for /// use as an identifier. /// public string Escape(EntityContainer container) { if (container == null) { return null; } return Escape(container.Name); } /// /// Returns the name of the EntitySet that is safe for /// use as an identifier. /// public string Escape(EntitySet set) { if (set == null) { return null; } return Escape(set.Name); } /// /// Returns the name of the StructuralType that is safe for /// use as an identifier. /// public string Escape(StructuralType type) { if (type == null) { return null; } return Escape(type.Name); } /// /// Returns the NamespaceName with each segment safe to /// use as an identifier. /// public string EscapeNamespace(string namespaceName) { if (String.IsNullOrEmpty(namespaceName)) { return namespaceName; } string[] parts = namespaceName.Split('.'); namespaceName = String.Empty; foreach (string part in parts) { if (namespaceName != String.Empty) { namespaceName += "."; } namespaceName += Escape(part); } return namespaceName; } /// /// Returns the name of the EdmMember formatted for /// use as a field identifier. /// /// This method changes behavior based on the CamelCaseFields /// setting. /// public string FieldName(EdmMember member) { if (member == null) { return null; } return FieldName(member.Name); } /// /// Returns the name of the EntitySet formatted for /// use as a field identifier. /// /// This method changes behavior based on the CamelCaseFields /// setting. /// public string FieldName(EntitySet set) { if (set == null) { return null; } return FieldName(set.Name); } private string FieldName(string name) { if (CamelCaseFields) { return "_" + CamelCase(name); } else { return "_" + name; } } /// /// Returns the name of the Type object formatted for /// use in source code. /// /// This method changes behavior based on the FullyQualifySystemTypes /// setting. /// public string Escape(Type clrType) { if(clrType == null) { return null; } string typeName; if (FullyQualifySystemTypes) { typeName = "global::" + clrType.FullName; } else { typeName = _code.GetTypeOutput(new CodeTypeReference(clrType)); } return typeName; } /// /// Returns the abstract option if the entity is Abstract, otherwise returns String.Empty /// public string AbstractOption(EntityType entity) { if (entity.Abstract) { return "abstract"; } return String.Empty; } /// /// Returns the passed in identifier with the first letter changed to lowercase /// public string CamelCase(string identifier) { if (String.IsNullOrEmpty(identifier)) { return identifier; } if (identifier.Length == 1) { return identifier[0].ToString(CultureInfo.InvariantCulture).ToLowerInvariant(); } return identifier[0].ToString(CultureInfo.InvariantCulture).ToLowerInvariant() + identifier.Substring(1); } /// /// If the value parameter is null or empty an empty string is returned, /// otherwise it retuns value with a single space concatenated on the end. /// public string SpaceAfter(string value) { return StringAfter(value, " "); } /// /// If the value parameter is null or empty an empty string is returned, /// otherwise it retuns value with a single space concatenated on the end. /// public string SpaceBefore(string value) { return StringBefore(" ", value); } /// /// If the value parameter is null or empty an empty string is returned, /// otherwise it retuns value with append concatenated on the end. /// public string StringAfter(string value, string append) { if (String.IsNullOrEmpty(value)) { return String.Empty; } return value + append; } /// /// If the value parameter is null or empty an empty string is returned, /// otherwise it retuns value with prepend concatenated on the front. /// public string StringBefore(string prepend, string value) { if (String.IsNullOrEmpty(value)) { return String.Empty; } return prepend + value; } /// /// Retuns as full of a name as possible, if a namespace is provided /// the namespace and name are combined with a period, otherwise just /// the name is returned. /// public string CreateFullName(string namespaceName, string name) { if (String.IsNullOrEmpty(namespaceName)) { return name; } return namespaceName + "." + name; } public string CreateLiteral(object value) { if (value == null) { return string.Empty; } Type type = value.GetType(); if (type.IsEnum) { return type.FullName + "." + value.ToString(); } if (type == typeof(Guid)) { return string.Format(CultureInfo.InvariantCulture, "new Guid(\"{0}\")", ((Guid)value).ToString("D", CultureInfo.InvariantCulture)); } else if (type == typeof(DateTime)) { return string.Format(CultureInfo.InvariantCulture, "new DateTime({0}, DateTimeKind.Unspecified)", ((DateTime)value).Ticks); } else if (type == typeof(byte[])) { var arrayInit = string.Join(", ", ((byte[])value).Select(b => b.ToString(CultureInfo.InvariantCulture)).ToArray()); return string.Format(CultureInfo.InvariantCulture, "new Byte[] {{{0}}}", arrayInit); } else if (type == typeof(DateTimeOffset)) { var dto = (DateTimeOffset)value; return string.Format(CultureInfo.InvariantCulture, "new DateTimeOffset({0}, new TimeSpan({1}))", dto.Ticks, dto.Offset.Ticks); } var expression = new CodePrimitiveExpression(value); var writer = new StringWriter(); CSharpCodeProvider code = new CSharpCodeProvider(); code.GenerateCodeFromExpression(expression, writer, new CodeGeneratorOptions()); return writer.ToString(); } } /// /// Responsible for making the Entity Framework Metadata more /// accessible for code generation. /// public class MetadataTools { private readonly DynamicTextTransformation _textTransformation; /// /// Initializes an MetadataTools Instance with the /// TextTransformation (T4 generated class) that is currently running /// public MetadataTools(object textTransformation) { if (textTransformation == null) { throw new ArgumentNullException("textTransformation"); } _textTransformation = DynamicTextTransformation.Create(textTransformation); } /// /// If the passed in TypeUsage has an EdmType that is a PrimitiveType, this method returns /// the corosponding Type object, otherwise it returns the Type object for Object. /// public Type ClrType(TypeUsage typeUsage) { if (typeUsage.EdmType is PrimitiveType) { return ((PrimitiveType)typeUsage.EdmType).ClrEquivalentType; } return typeof(object); } /// /// True if the EdmProperty is a key of its DeclaringType, False otherwise. /// public bool IsKey(EdmProperty property) { if (property != null && property.DeclaringType.BuiltInTypeKind == BuiltInTypeKind.EntityType) { return ((EntityType)property.DeclaringType).KeyMembers.Contains(property); } return false; } /// /// True if the EdmProperty TypeUsage is Nullable, False otherwise. /// public bool IsNullable(EdmProperty property) { return property != null && IsNullable(property.TypeUsage); } /// /// True if the TypeUsage is Nullable, False otherwise. /// public bool IsNullable(TypeUsage typeUsage) { Facet nullableFacet = null; if (typeUsage != null && typeUsage.Facets.TryGetValue("Nullable", true, out nullableFacet)) { return (bool)nullableFacet.Value; } return false; } /// /// If the passed in TypeUsage represents a collection this method returns final element /// type of the collection, otherwise it returns the value passed in. /// public TypeUsage GetElementType(TypeUsage typeUsage) { if (typeUsage == null) { return null; } if (typeUsage.EdmType is CollectionType) { return GetElementType(((CollectionType)typeUsage.EdmType).TypeUsage); } else { return typeUsage; } } /// /// Returns the NavigationProperty that is the other end of the same association set if it is /// available, otherwise it returns null. /// public NavigationProperty Inverse(NavigationProperty navProperty) { if(navProperty == null) { return null; } EntityType toEntity = navProperty.ToEndMember.GetEntityType(); return toEntity.NavigationProperties .SingleOrDefault(n => Object.ReferenceEquals(n.RelationshipType, navProperty.RelationshipType) && !Object.ReferenceEquals(n, navProperty)); } /// /// Given a property on the dependent end of a referential constraint, returns the corresponding property on the principal end. /// Requires: The association has a referential constraint, and the specified dependentProperty is one of the properties on the dependent end. /// public EdmProperty GetCorrespondingPrincipalProperty(NavigationProperty navProperty, EdmProperty dependentProperty) { if (navProperty == null) { throw new ArgumentNullException("navProperty"); } if (dependentProperty == null) { throw new ArgumentNullException("dependentProperty"); } ReadOnlyMetadataCollection fromProperties = GetPrincipalProperties(navProperty); ReadOnlyMetadataCollection toProperties = GetDependentProperties(navProperty); return fromProperties[toProperties.IndexOf(dependentProperty)]; } /// /// Given a property on the principal end of a referential constraint, returns the corresponding property on the dependent end. /// Requires: The association has a referential constraint, and the specified principalProperty is one of the properties on the principal end. /// public EdmProperty GetCorrespondingDependentProperty(NavigationProperty navProperty, EdmProperty principalProperty) { if (navProperty == null) { throw new ArgumentNullException("navProperty"); } if (principalProperty == null) { throw new ArgumentNullException("principalProperty"); } ReadOnlyMetadataCollection fromProperties = GetPrincipalProperties(navProperty); ReadOnlyMetadataCollection toProperties = GetDependentProperties(navProperty); return toProperties[fromProperties.IndexOf(principalProperty)]; } /// /// Gets the collection of properties that are on the principal end of a referential constraint for the specified navigation property. /// Requires: The association has a referential constraint. /// public ReadOnlyMetadataCollection GetPrincipalProperties(NavigationProperty navProperty) { if (navProperty == null) { throw new ArgumentNullException("navProperty"); } return ((AssociationType)navProperty.RelationshipType).ReferentialConstraints[0].FromProperties; } /// /// Gets the collection of properties that are on the dependent end of a referential constraint for the specified navigation property. /// Requires: The association has a referential constraint. /// public ReadOnlyMetadataCollection GetDependentProperties(NavigationProperty navProperty) { if (navProperty == null) { throw new ArgumentNullException("navProperty"); } return ((AssociationType)navProperty.RelationshipType).ReferentialConstraints[0].ToProperties; } /// /// True if this entity type requires the HandleCascadeDelete method defined and the method has /// not been defined on any base type /// public bool NeedsHandleCascadeDeleteMethod(ItemCollection itemCollection, EntityType entity) { bool needsMethod = ContainsCascadeDeleteAssociation(itemCollection, entity); // Check to make sure no base types have already declared this method EntityType baseType = entity.BaseType as EntityType; while(needsMethod && baseType != null) { needsMethod = !ContainsCascadeDeleteAssociation(itemCollection, baseType); baseType = baseType.BaseType as EntityType; } return needsMethod; } /// /// True if this entity type participates in any relationships where the other end has an OnDelete /// cascade delete defined, or if it is the dependent in any identifying relationships /// private bool ContainsCascadeDeleteAssociation(ItemCollection itemCollection, EntityType entity) { return itemCollection.GetItems().Where(a => ((RefType)a.AssociationEndMembers[0].TypeUsage.EdmType).ElementType == entity && IsCascadeDeletePrincipal(a.AssociationEndMembers[1]) || ((RefType)a.AssociationEndMembers[1].TypeUsage.EdmType).ElementType == entity && IsCascadeDeletePrincipal(a.AssociationEndMembers[0])).Any(); } /// /// True if the source end of the specified navigation property is the principal in an identifying relationship. /// or if the source end has cascade delete defined. /// public bool IsCascadeDeletePrincipal(NavigationProperty navProperty) { if (navProperty == null) { throw new ArgumentNullException("navProperty"); } return IsCascadeDeletePrincipal((AssociationEndMember)navProperty.FromEndMember); } /// /// True if the specified association end is the principal in an identifying relationship. /// or if the association end has cascade delete defined. /// public bool IsCascadeDeletePrincipal(AssociationEndMember associationEnd) { if (associationEnd == null) { throw new ArgumentNullException("associationEnd"); } return associationEnd.DeleteBehavior == OperationAction.Cascade || IsPrincipalEndOfIdentifyingRelationship(associationEnd); } /// /// True if the specified association end is the principal end in an identifying relationship. /// In order to be an identifying relationship, the association must have a referential constraint where all of the dependent properties are part of the dependent type's primary key. /// public bool IsPrincipalEndOfIdentifyingRelationship(AssociationEndMember associationEnd) { if (associationEnd == null) { throw new ArgumentNullException("associationEnd"); } ReferentialConstraint refConstraint = ((AssociationType)associationEnd.DeclaringType).ReferentialConstraints.Where(rc => rc.FromRole == associationEnd).SingleOrDefault(); if (refConstraint != null) { EntityType entity = refConstraint.ToRole.GetEntityType(); return !refConstraint.ToProperties.Where(tp => !entity.KeyMembers.Contains(tp)).Any(); } return false; } /// /// True if the specified association type is an identifying relationship. /// In order to be an identifying relationship, the association must have a referential constraint where all of the dependent properties are part of the dependent type's primary key. /// public bool IsIdentifyingRelationship(AssociationType association) { if (association == null) { throw new ArgumentNullException("association"); } return IsPrincipalEndOfIdentifyingRelationship(association.AssociationEndMembers[0]) || IsPrincipalEndOfIdentifyingRelationship(association.AssociationEndMembers[1]); } /// /// requires: firstType is not null /// effects: if secondType is among the base types of the firstType, return true, /// otherwise returns false. /// when firstType is same as the secondType, return false. /// public bool IsSubtypeOf(EdmType firstType, EdmType secondType) { if (secondType == null) { return false; } // walk up firstType hierarchy list for (EdmType t = firstType.BaseType; t != null; t = t.BaseType) { if (t == secondType) return true; } return false; } /// /// Returns the subtype of the EntityType in the current itemCollection /// public IEnumerable GetSubtypesOf(EntityType type, ItemCollection itemCollection, bool includeAbstractTypes) { if (type != null) { IEnumerable typesInCollection = itemCollection.GetItems(); foreach (EntityType typeInCollection in typesInCollection) { if (type.Equals(typeInCollection) == false && this.IsSubtypeOf(typeInCollection, type)) { if ( includeAbstractTypes || !typeInCollection.Abstract) { yield return typeInCollection; } } } } } public static bool TryGetStringMetadataPropertySetting(MetadataItem item, string propertyName, out string value) { value = null; MetadataProperty property = item.MetadataProperties.FirstOrDefault(p => p.Name == propertyName); if (property != null) { value = (string)property.Value; } return value != null; } } /// /// Responsible for loading an EdmItemCollection from a .edmx file or .csdl files /// public class MetadataLoader { private readonly DynamicTextTransformation _textTransformation; /// /// Initializes an MetadataLoader Instance with the /// TextTransformation (T4 generated class) that is currently running /// public MetadataLoader(object textTransformation) { if (textTransformation == null) { throw new ArgumentNullException("textTransformation"); } _textTransformation = DynamicTextTransformation.Create(textTransformation); } /// /// Load the metadata for Edm, Store, and Mapping collections and register them /// with a new MetadataWorkspace, returns false if any of the parts can't be /// created, some of the ItemCollections may be registered and usable even if false is /// returned /// public bool TryLoadAllMetadata(string inputFile, out MetadataWorkspace metadataWorkspace) { metadataWorkspace = new MetadataWorkspace(); EdmItemCollection edmItemCollection = CreateEdmItemCollection(inputFile); metadataWorkspace.RegisterItemCollection(edmItemCollection); StoreItemCollection storeItemCollection = null; if (TryCreateStoreItemCollection(inputFile, out storeItemCollection)) { StorageMappingItemCollection storageMappingItemCollection = null; if (TryCreateStorageMappingItemCollection(inputFile, edmItemCollection, storeItemCollection, out storageMappingItemCollection)) { metadataWorkspace.RegisterItemCollection(storeItemCollection); metadataWorkspace.RegisterItemCollection(storageMappingItemCollection); return true; } } return false; } /// /// Create an EdmItemCollection loaded with the metadata provided /// public EdmItemCollection CreateEdmItemCollection(string sourcePath, params string[] referenceSchemas) { EdmItemCollection edmItemCollection; if(TryCreateEdmItemCollection(sourcePath, referenceSchemas, out edmItemCollection)) { return edmItemCollection; } return new EdmItemCollection(); } /// /// Attempts to create a EdmItemCollection from the specified metadata file /// public bool TryCreateEdmItemCollection(string sourcePath, out EdmItemCollection edmItemCollection) { return TryCreateEdmItemCollection(sourcePath, null, out edmItemCollection); } /// /// Attempts to create a EdmItemCollection from the specified metadata file /// public bool TryCreateEdmItemCollection(string sourcePath, string[] referenceSchemas, out EdmItemCollection edmItemCollection) { edmItemCollection = null; if (String.IsNullOrEmpty(sourcePath)) { throw new ArgumentException("sourcePath"); } if (referenceSchemas == null) { referenceSchemas = new string[0]; } ItemCollection itemCollection = null; sourcePath = _textTransformation.Host.ResolvePath(sourcePath); EdmItemCollectionBuilder collectionBuilder = new EdmItemCollectionBuilder(_textTransformation, referenceSchemas.Select(s => _textTransformation.Host.ResolvePath(s)).Where(s => s != sourcePath)); if (collectionBuilder.TryCreateItemCollection(sourcePath, out itemCollection)) { edmItemCollection = (EdmItemCollection)itemCollection; } return edmItemCollection != null; } /// /// Attempts to create a StoreItemCollection from the specified metadata file /// public bool TryCreateStoreItemCollection(string sourcePath, out StoreItemCollection storeItemCollection) { storeItemCollection = null; if (String.IsNullOrEmpty(sourcePath)) { throw new ArgumentException("sourcePath"); } ItemCollection itemCollection = null; StoreItemCollectionBuilder collectionBuilder = new StoreItemCollectionBuilder(_textTransformation); if (collectionBuilder.TryCreateItemCollection(_textTransformation.Host.ResolvePath(sourcePath), out itemCollection)) { storeItemCollection = (StoreItemCollection)itemCollection; } return storeItemCollection != null; } /// /// Attempts to create a StorageMappingItemCollection from the specified metadata file, EdmItemCollection, and StoreItemCollection /// public bool TryCreateStorageMappingItemCollection(string sourcePath, EdmItemCollection edmItemCollection, StoreItemCollection storeItemCollection, out StorageMappingItemCollection storageMappingItemCollection) { storageMappingItemCollection = null; if (String.IsNullOrEmpty(sourcePath)) { throw new ArgumentException("sourcePath"); } if (edmItemCollection == null) { throw new ArgumentNullException("edmItemCollection"); } if (storeItemCollection == null) { throw new ArgumentNullException("storeItemCollection"); } ItemCollection itemCollection = null; StorageMappingItemCollectionBuilder collectionBuilder = new StorageMappingItemCollectionBuilder(_textTransformation, edmItemCollection, storeItemCollection); if (collectionBuilder.TryCreateItemCollection(_textTransformation.Host.ResolvePath(sourcePath), out itemCollection)) { storageMappingItemCollection = (StorageMappingItemCollection)itemCollection; } return storageMappingItemCollection != null; } /// /// Gets the Model Namespace from the provided schema file. /// public string GetModelNamespace(string sourcePath) { if (String.IsNullOrEmpty(sourcePath)) { throw new ArgumentException("sourcePath"); } if(sourcePath == "$edmxInputFile$") { _textTransformation.Errors.Add(new CompilerError(_textTransformation.Host.TemplateFile ?? "Currently Running Template", 0, 0, "", "Please overwrite the replacement token '$edmxInputFile$' with the actual name of the .edmx file you would like to generate from.")); return String.Empty; } EdmItemCollectionBuilder builder = new EdmItemCollectionBuilder(_textTransformation); XElement model; if(builder.TryLoadRootElement(_textTransformation.Host.ResolvePath(sourcePath), out model)) { XAttribute attribute = model.Attribute("Namespace"); if (attribute != null) { return attribute.Value; } } return String.Empty; } /// /// base class for ItemCollectionBuilder classes that /// load the specific types of metadata /// private abstract class ItemCollectionBuilder { private readonly DynamicTextTransformation _textTransformation; private readonly string _fileExtension; private readonly string _namespaceV1; private readonly string _namespaceV2; private readonly string _edmxSectionName; private readonly string _rootElementName; /// /// FileExtension for individual (non-edmx) metadata file for this /// specific ItemCollection type /// public string FileExtension { get { return _fileExtension; } } /// /// EF Version 1 XmlNamespace name /// public string NamespaceV1 { get { return _namespaceV1; } } /// /// EF Version 2 XmlNamespace name /// public string NamespaceV2 { get { return _namespaceV2; } } /// /// The name of the XmlElement in the .edmx element /// to find this ItemCollection's metadata /// public string EdmxSectionName { get { return _edmxSectionName; } } /// /// The name of the root element of this ItemCollection's metadata /// public string RootElementName { get { return _rootElementName; } } /// /// Method to build the appropriate ItemCollection /// protected abstract ItemCollection CreateItemCollection(IEnumerable readers, out IList errors); /// /// Ctor to setup the ItemCollectionBuilder members /// protected ItemCollectionBuilder(DynamicTextTransformation textTransformation, string fileExtension, string namespaceV1, string namespaceV2, string edmxSectionName, string rootElementName) { _textTransformation = textTransformation; _fileExtension = fileExtension; _namespaceV1 = namespaceV1; _namespaceV2 = namespaceV2; _edmxSectionName = edmxSectionName; _rootElementName = rootElementName; } /// /// Try to create an ItemCollection loaded with the metadata provided /// public bool TryCreateItemCollection(string sourcePath, out ItemCollection itemCollection) { itemCollection = null; if (String.IsNullOrEmpty(sourcePath)) { throw new ArgumentException("sourcePath"); } if(sourcePath == "$edmxInputFile$" ) { _textTransformation.Errors.Add(new CompilerError(_textTransformation.Host.TemplateFile ?? "Currently Running Template", 0, 0, "", "Please overwrite the replacement token '$edmxInputFile$' with the actual name of the .edmx file you would like to generate from.")); return false; } XElement schemaElement = null; if (TryLoadRootElement(sourcePath, out schemaElement)) { List readers = new List(); try { readers.Add(schemaElement.CreateReader()); IList errors = null; ItemCollection tempItemCollection = CreateItemCollection(readers, out errors); if (ProcessErrors(errors, sourcePath)) { return false; } itemCollection = tempItemCollection; return true; } finally { foreach (XmlReader reader in readers) { ((IDisposable)reader).Dispose(); } } } return false; } /// /// Tries to load the root element from the metadata file provided /// public bool TryLoadRootElement(string sourcePath, out XElement schemaElement) { schemaElement = null; string extension = Path.GetExtension(sourcePath); if (extension.Equals(".edmx", StringComparison.InvariantCultureIgnoreCase)) { return TryLoadRootElementFromEdmx(sourcePath, out schemaElement); } else if(extension.Equals(FileExtension, StringComparison.InvariantCultureIgnoreCase)) { // load from single metadata file (.csdl, .ssdl, or .msl) schemaElement = XElement.Load(sourcePath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo); return true; } return false; } /// /// Trys to load the root element from the edmxDocument provided /// private static bool TryLoadRootElementFromEdmx(XElement edmxDocument, string edmxNamespace, string sectionNamespace, string sectionName, string rootElementName, out XElement rootElement) { rootElement = null; XNamespace edmxNs = edmxNamespace; XNamespace sectionNs = sectionNamespace; XElement runtime = edmxDocument.Element(edmxNs + "Runtime"); if (runtime == null) return false; XElement section = runtime.Element(edmxNs + sectionName); if (section == null) return false; rootElement = section.Element(sectionNs + rootElementName); return rootElement != null; } /// /// Trys to load the root element from the .edmx metadata file provided /// private bool TryLoadRootElementFromEdmx(string edmxPath, out XElement rootElement) { rootElement = null; XElement element = XElement.Load(edmxPath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo); return TryLoadRootElementFromEdmx(element, MetadataConstants.EDMX_NAMESPACE_V2, NamespaceV2, EdmxSectionName, RootElementName, out rootElement) || TryLoadRootElementFromEdmx(element, MetadataConstants.EDMX_NAMESPACE_V1, NamespaceV1, EdmxSectionName, RootElementName, out rootElement); } /// /// Takes an Enumerable of EdmSchemaErrors, and adds them /// to the errors collection of the template class /// private bool ProcessErrors(IEnumerable errors, string sourceFilePath) { bool foundErrors = false; foreach (EdmSchemaError error in errors) { CompilerError newError = new CompilerError(error.SchemaLocation, error.Line, error.Column, error.ErrorCode.ToString(CultureInfo.InvariantCulture), error.Message); newError.IsWarning = error.Severity == EdmSchemaErrorSeverity.Warning; foundErrors |= error.Severity == EdmSchemaErrorSeverity.Error; if (error.SchemaLocation == null) { newError.FileName = sourceFilePath; } _textTransformation.Errors.Add(newError); } return foundErrors; } } /// /// Builder class for creating a StorageMappingItemCollection /// private class StorageMappingItemCollectionBuilder : ItemCollectionBuilder { private readonly EdmItemCollection _edmItemCollection; private readonly StoreItemCollection _storeItemCollection; public StorageMappingItemCollectionBuilder(DynamicTextTransformation textTransformation, EdmItemCollection edmItemCollection, StoreItemCollection storeItemCollection) : base(textTransformation, MetadataConstants.MSL_EXTENSION, MetadataConstants.MSL_NAMESPACE_V1, MetadataConstants.MSL_NAMESPACE_V2, MetadataConstants.MSL_EDMX_SECTION_NAME, MetadataConstants.MSL_ROOT_ELEMENT_NAME) { _edmItemCollection = edmItemCollection; _storeItemCollection = storeItemCollection; } protected override ItemCollection CreateItemCollection(IEnumerable readers, out IList errors) { return MetadataItemCollectionFactory.CreateStorageMappingItemCollection(_edmItemCollection, _storeItemCollection, readers, out errors); } } /// /// Builder class for creating a StoreItemCollection /// private class StoreItemCollectionBuilder : ItemCollectionBuilder { public StoreItemCollectionBuilder(DynamicTextTransformation textTransformation) : base(textTransformation, MetadataConstants.SSDL_EXTENSION, MetadataConstants.SSDL_NAMESPACE_V1, MetadataConstants.SSDL_NAMESPACE_V2, MetadataConstants.SSDL_EDMX_SECTION_NAME, MetadataConstants.SSDL_ROOT_ELEMENT_NAME) { } protected override ItemCollection CreateItemCollection(IEnumerable readers, out IList errors) { return MetadataItemCollectionFactory.CreateStoreItemCollection(readers, out errors); } } /// /// Builder class for creating a EdmItemCollection /// private class EdmItemCollectionBuilder : ItemCollectionBuilder { private List _referenceSchemas = new List(); public EdmItemCollectionBuilder(DynamicTextTransformation textTransformation) : base(textTransformation, MetadataConstants.CSDL_EXTENSION, MetadataConstants.CSDL_NAMESPACE_V1, MetadataConstants.CSDL_NAMESPACE_V2, MetadataConstants.CSDL_EDMX_SECTION_NAME, MetadataConstants.CSDL_ROOT_ELEMENT_NAME) { } public EdmItemCollectionBuilder(DynamicTextTransformation textTransformation, IEnumerable referenceSchemas) : this(textTransformation) { _referenceSchemas.AddRange(referenceSchemas); } protected override ItemCollection CreateItemCollection(IEnumerable readers, out IList errors) { List ownedReaders = new List(); List allReaders = new List(); try { allReaders.AddRange(readers); foreach (string path in _referenceSchemas.Distinct()) { XElement reference; if(TryLoadRootElement(path, out reference)) { XmlReader reader = reference.CreateReader(); allReaders.Add(reader); ownedReaders.Add(reader); } } return MetadataItemCollectionFactory.CreateEdmItemCollection(allReaders, out errors); } finally { foreach (XmlReader reader in ownedReaders) { ((IDisposable)reader).Dispose(); } } } } } /// /// Responsible for encapsulating the retrieval and translation of the CodeGeneration /// annotations in the EntityFramework Metadata to a form that is useful in code generation. /// public static class Accessibility { private const string GETTER_ACCESS = "http://schemas.microsoft.com/ado/2006/04/codegeneration:GetterAccess"; private const string SETTER_ACCESS = "http://schemas.microsoft.com/ado/2006/04/codegeneration:SetterAccess"; private const string TYPE_ACCESS = "http://schemas.microsoft.com/ado/2006/04/codegeneration:TypeAccess"; private const string METHOD_ACCESS = "http://schemas.microsoft.com/ado/2006/04/codegeneration:MethodAccess"; private const string ACCESS_PROTECTED = "Protected"; private const string ACCESS_INTERNAL = "Internal"; private const string ACCESS_PRIVATE = "Private"; private static readonly Dictionary AccessibilityRankIdLookup = new Dictionary { { "private", 1}, { "internal", 2}, { "protected", 3}, { "public", 4}, }; /// /// Gets the accessibility that should be applied to a type being generated from the provided GlobalItem. /// /// defaults to public if no annotation is found. /// public static string ForType(GlobalItem item) { if (item == null) { return null; } return GetAccessibility(item, TYPE_ACCESS); } /// /// Gets the accessibility that should be applied at the property level for a property being /// generated from the provided EdmMember. /// /// defaults to public if no annotation is found. /// public static string ForProperty(EdmMember member) { if (member == null) { return null; } string getterAccess, setterAccess, propertyAccess; CalculatePropertyAccessibility(member, out propertyAccess, out getterAccess, out setterAccess); return propertyAccess; } /// /// Gets the accessibility that should be applied at the property level for a Read-Only property being /// generated from the provided EdmMember. /// /// defaults to public if no annotation is found. /// public static string ForReadOnlyProperty(EdmMember member) { if (member == null) { return null; } return GetAccessibility(member, GETTER_ACCESS); } /// /// Gets the accessibility that should be applied at the property level for a property being /// generated from the provided EntitySet. /// /// defaults to public if no annotation is found. /// public static string ForReadOnlyProperty(EntitySet set) { if (set == null) { return null; } return GetAccessibility(set, GETTER_ACCESS); } /// /// Gets the accessibility that should be applied at the property level for a Write-Only property being /// generated from the provided EdmMember. /// /// defaults to public if no annotation is found. /// public static string ForWriteOnlyProperty(EdmMember member) { if (member == null) { return null; } return GetAccessibility(member, SETTER_ACCESS); } /// /// Gets the accessibility that should be applied at the get level for a property being /// generated from the provided EdmMember. /// /// defaults to empty if no annotation is found or the accessibility is the same as the property level. /// public static string ForGetter(EdmMember member) { if (member == null) { return null; } string getterAccess, setterAccess, propertyAccess; CalculatePropertyAccessibility(member, out propertyAccess, out getterAccess, out setterAccess); return getterAccess; } /// /// Gets the accessibility that should be applied at the set level for a property being /// generated from the provided EdmMember. /// /// defaults to empty if no annotation is found or the accessibility is the same as the property level. /// public static string ForSetter(EdmMember member) { if (member == null) { return null; } string getterAccess, setterAccess, propertyAccess; CalculatePropertyAccessibility(member, out propertyAccess, out getterAccess, out setterAccess); return setterAccess; } /// /// Gets the accessibility that should be applied to a method being generated from the provided EdmFunction. /// /// defaults to public if no annotation is found. /// public static string ForMethod(EdmFunction function) { if (function == null) { return null; } return GetAccessibility(function, METHOD_ACCESS); } private static void CalculatePropertyAccessibility(MetadataItem item, out string propertyAccessibility, out string getterAccessibility, out string setterAccessibility) { getterAccessibility = GetAccessibility(item, GETTER_ACCESS); int getterRank = AccessibilityRankIdLookup[getterAccessibility]; setterAccessibility = GetAccessibility(item, SETTER_ACCESS); int setterRank = AccessibilityRankIdLookup[setterAccessibility]; int propertyRank = Math.Max(getterRank, setterRank); if (setterRank == propertyRank) { setterAccessibility = String.Empty; } if (getterRank == propertyRank) { getterAccessibility = String.Empty; } propertyAccessibility = AccessibilityRankIdLookup.Where(v => v.Value == propertyRank).Select(v => v.Key).Single(); } private static string GetAccessibility(MetadataItem item, string name) { string accessibility; if (MetadataTools.TryGetStringMetadataPropertySetting(item, name, out accessibility)) { return TranslateUserAccessibilityToCSharpAccessibility(accessibility); } return "public"; } private static string TranslateUserAccessibilityToCSharpAccessibility(string userAccessibility) { if (userAccessibility == ACCESS_PROTECTED) { return "protected"; } else if (userAccessibility == ACCESS_INTERNAL) { return "internal"; } else if (userAccessibility == ACCESS_PRIVATE) { return "private"; } else { // default to public return "public"; } } } /// /// Responsible for creating source code regions in code when the loop inside /// actually produces something. /// public class CodeRegion { private const int STANDARD_INDENT_LENGTH = 4; private readonly DynamicTextTransformation _textTransformation; private int _beforeRegionLength; private int _emptyRegionLength; private int _regionIndentLevel = -1; /// /// Initializes an CodeRegion instance with the /// TextTransformation (T4 generated class) that is currently running /// public CodeRegion(object textTransformation) { if (textTransformation == null) { throw new ArgumentNullException("textTransformation"); } _textTransformation = DynamicTextTransformation.Create(textTransformation); } /// /// Initializes an CodeRegion instance with the /// TextTransformation (T4 generated class) that is currently running, /// and the indent level to start the first region at. /// public CodeRegion(object textTransformation, int firstIndentLevel) : this(textTransformation) { if (firstIndentLevel < 0) { throw new ArgumentException("firstIndentLevel"); } _regionIndentLevel = firstIndentLevel - 1; } /// /// Starts the begining of a region /// public void Begin(string regionName) { if (regionName == null) { throw new ArgumentNullException("regionName"); } Begin(regionName, 1); } /// /// Start the begining of a region, indented /// the numbers of levels specified /// public void Begin(string regionName, int levelsToIncreaseIndent) { if (regionName == null) { throw new ArgumentNullException("regionName"); } _beforeRegionLength = _textTransformation.GenerationEnvironment.Length; _regionIndentLevel += levelsToIncreaseIndent; _textTransformation.Write(GetIndent(_regionIndentLevel)); _textTransformation.WriteLine("#region " + regionName); _emptyRegionLength = _textTransformation.GenerationEnvironment.Length; } /// /// Ends a region, or totaly removes it if nothing /// was generted since the begining of the region. /// public void End() { End(1); } /// /// Ends a region, or totaly removes it if nothing /// was generted since the begining of the region, also outdents /// the number of levels specified. /// public void End(int levelsToDecrease) { int indentLevel = _regionIndentLevel; _regionIndentLevel -= levelsToDecrease; if (_emptyRegionLength == _textTransformation.GenerationEnvironment.Length) _textTransformation.GenerationEnvironment.Length = _beforeRegionLength; else { _textTransformation.WriteLine(String.Empty); _textTransformation.Write(GetIndent(indentLevel)); _textTransformation.WriteLine("#endregion"); } } /// /// Gets the current indent level that the next end region statement will be written /// at /// public int CurrentIndentLevel { get { return _regionIndentLevel; } } /// /// Get a string of spaces equivelent to the number of indents /// desired. /// public static string GetIndent(int indentLevel) { if (indentLevel < 0) { throw new ArgumentException("indentLevel"); } return String.Empty.PadLeft(indentLevel * STANDARD_INDENT_LENGTH); } } /// /// Responsible for collecting together the actual method parameters /// and the parameters that need to be sent to the Execute method. /// public class FunctionImportParameter { public FunctionParameter Source { get; set; } public string RawFunctionParameterName { get; set; } public string FunctionParameterName { get; set; } public string FunctionParameterType { get; set; } public string LocalVariableName { get; set; } public string RawClrTypeName { get; set; } public string ExecuteParameterName { get; set; } public string EsqlParameterName { get; set; } public bool NeedsLocalVariable { get; set; } public bool IsNullableOfT { get; set; } /// /// Creates a set of FunctionImportParameter objects from the parameters passed in. /// public static IEnumerable Create(IEnumerable parameters, CodeGenerationTools code, MetadataTools ef) { if (parameters == null) { throw new ArgumentNullException("parameters"); } if (code == null) { throw new ArgumentNullException("code"); } if (ef == null) { throw new ArgumentNullException("ef"); } UniqueIdentifierService unique = new UniqueIdentifierService(); List importParameters = new List(); foreach (FunctionParameter parameter in parameters) { FunctionImportParameter importParameter = new FunctionImportParameter(); importParameter.Source = parameter; importParameter.RawFunctionParameterName = unique.AdjustIdentifier(code.CamelCase(parameter.Name)); importParameter.FunctionParameterName = code.Escape(importParameter.RawFunctionParameterName); if (parameter.Mode == ParameterMode.In) { importParameter.NeedsLocalVariable = true; importParameter.FunctionParameterType = code.Escape(parameter.TypeUsage); importParameter.EsqlParameterName = parameter.Name; Type clrType = ef.ClrType(parameter.TypeUsage); importParameter.RawClrTypeName = code.Escape(clrType); importParameter.IsNullableOfT = clrType.IsValueType; } else { importParameter.NeedsLocalVariable = false; importParameter.FunctionParameterType = "ObjectParameter"; importParameter.ExecuteParameterName = importParameter.FunctionParameterName; } importParameters.Add(importParameter); } // we save the local parameter uniquification for a second pass to make the visible parameters // as pretty and sensible as possible for (int i = 0; i < importParameters.Count; i++) { FunctionImportParameter importParameter = importParameters[i]; if (importParameter.NeedsLocalVariable) { importParameter.LocalVariableName = unique.AdjustIdentifier(importParameter.RawFunctionParameterName + "Parameter"); importParameter.ExecuteParameterName = importParameter.LocalVariableName; } } return importParameters; } // // Class to create unique variables within the same scope // private sealed class UniqueIdentifierService { private readonly HashSet _knownIdentifiers; public UniqueIdentifierService() { _knownIdentifiers = new HashSet(StringComparer.Ordinal); } /// /// Given an identifier, makes it unique within the scope by adding /// a suffix (1, 2, 3, ...), and returns the adjusted identifier. /// public string AdjustIdentifier(string identifier) { // find a unique name by adding suffix as necessary int numberOfConflicts = 0; string adjustedIdentifier = identifier; while (!_knownIdentifiers.Add(adjustedIdentifier)) { ++numberOfConflicts; adjustedIdentifier = identifier + numberOfConflicts.ToString(CultureInfo.InvariantCulture); } return adjustedIdentifier; } } private string FunctionImportTypeName(FunctionParameter parameter) { return parameter.Mode == ParameterMode.In ? parameter.TypeUsage.EdmType.Name : "ObjectParameter"; } } /// /// Responsible for marking the various sections of the generation, /// so they can be split up into separate files /// public class EntityFrameworkTemplateFileManager { /// /// Creates the VsEntityFrameworkTemplateFileManager if VS is detected, otherwise /// creates the file system version. /// public static EntityFrameworkTemplateFileManager Create(object textTransformation) { DynamicTextTransformation transformation = DynamicTextTransformation.Create(textTransformation); IDynamicHost host = transformation.Host; #if !PREPROCESSED_TEMPLATE if (host.AsIServiceProvider() != null) { return new VsEntityFrameworkTemplateFileManager(transformation); } #endif return new EntityFrameworkTemplateFileManager(transformation); } private sealed class Block { public String Name; public int Start, Length; } private readonly List files = new List(); private readonly Block footer = new Block(); private readonly Block header = new Block(); private readonly DynamicTextTransformation _textTransformation; // reference to the GenerationEnvironment StringBuilder on the // TextTransformation object private readonly StringBuilder _generationEnvironment; private Block currentBlock; /// /// Initializes an EntityFrameworkTemplateFileManager Instance with the /// TextTransformation (T4 generated class) that is currently running /// private EntityFrameworkTemplateFileManager(object textTransformation) { if (textTransformation == null) { throw new ArgumentNullException("textTransformation"); } _textTransformation = DynamicTextTransformation.Create(textTransformation); _generationEnvironment = _textTransformation.GenerationEnvironment; } /// /// Marks the end of the last file if there was one, and starts a new /// and marks this point in generation as a new file. /// public void StartNewFile(string name) { if (name == null) { throw new ArgumentNullException("name"); } CurrentBlock = new Block { Name = name }; } public void StartFooter() { CurrentBlock = footer; } public void StartHeader() { CurrentBlock = header; } public void EndBlock() { if (CurrentBlock == null) { return; } CurrentBlock.Length = _generationEnvironment.Length - CurrentBlock.Start; if (CurrentBlock != header && CurrentBlock != footer) { files.Add(CurrentBlock); } currentBlock = null; } /// /// Produce the template output files. /// public virtual IEnumerable Process(bool split = true) { var generatedFileNames = new List(); if (split) { EndBlock(); var headerText = _generationEnvironment.ToString(header.Start, header.Length); var footerText = _generationEnvironment.ToString(footer.Start, footer.Length); var outputPath = Path.GetDirectoryName(_textTransformation.Host.TemplateFile); files.Reverse(); foreach (var block in files) { var fileName = Path.Combine(outputPath, block.Name); var content = headerText + _generationEnvironment.ToString(block.Start, block.Length) + footerText; generatedFileNames.Add(fileName); CreateFile(fileName, content); _generationEnvironment.Remove(block.Start, block.Length); } } return generatedFileNames; } protected virtual void CreateFile(string fileName, string content) { if (IsFileContentDifferent(fileName, content)) { File.WriteAllText(fileName, content); } } protected bool IsFileContentDifferent(String fileName, string newContent) { return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent); } private Block CurrentBlock { get { return currentBlock; } set { if (CurrentBlock != null) { EndBlock(); } if (value != null) { value.Start = _generationEnvironment.Length; } currentBlock = value; } } #if !PREPROCESSED_TEMPLATE private sealed class VsEntityFrameworkTemplateFileManager : EntityFrameworkTemplateFileManager { private EnvDTE.ProjectItem templateProjectItem; private EnvDTE.DTE dte; private Action checkOutAction; private Action> projectSyncAction; /// /// Creates an instance of the VsEntityFrameworkTemplateFileManager class with the IDynamicHost instance /// public VsEntityFrameworkTemplateFileManager(object textTemplating) : base(textTemplating) { var hostServiceProvider = _textTransformation.Host.AsIServiceProvider(); if (hostServiceProvider == null) { throw new ArgumentNullException("Could not obtain hostServiceProvider"); } dte = (EnvDTE.DTE) hostServiceProvider.GetService(typeof(EnvDTE.DTE)); if (dte == null) { throw new ArgumentNullException("Could not obtain DTE from host"); } templateProjectItem = dte.Solution.FindProjectItem(_textTransformation.Host.TemplateFile); checkOutAction = fileName => dte.SourceControl.CheckOutItem(fileName); projectSyncAction = keepFileNames => ProjectSync(templateProjectItem, keepFileNames); } public override IEnumerable Process(bool split) { if (templateProjectItem.ProjectItems == null) { return new List(); } var generatedFileNames = base.Process(split); projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null)); return generatedFileNames; } protected override void CreateFile(string fileName, string content) { if (IsFileContentDifferent(fileName, content)) { CheckoutFileIfRequired(fileName); File.WriteAllText(fileName, content); } } private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, IEnumerable keepFileNames) { var keepFileNameSet = new HashSet(keepFileNames); var projectFiles = new Dictionary(); var originalOutput = Path.GetFileNameWithoutExtension(templateProjectItem.FileNames[0]); foreach (EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems) { projectFiles.Add(projectItem.FileNames[0], projectItem); } // Remove unused items from the project foreach (var pair in projectFiles) { if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalOutput + ".")) { pair.Value.Delete(); } } // Add missing files to the project foreach (string fileName in keepFileNameSet) { if (!projectFiles.ContainsKey(fileName)) { templateProjectItem.ProjectItems.AddFromFile(fileName); } } } private void CheckoutFileIfRequired(string fileName) { if (dte.SourceControl == null || !dte.SourceControl.IsItemUnderSCC(fileName) || dte.SourceControl.IsItemCheckedOut(fileName)) { return; } // run on worker thread to prevent T4 calling back into VS checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null)); } } #endif } /// /// Responsible creating an instance that can be passed /// to helper classes that need to access the TextTransformation /// members. It accesses member by name and signature rather than /// by type. This is necessary when the /// template is being used in Preprocessed mode /// and there is no common known type that can be /// passed instead /// public class DynamicTextTransformation { private object _instance; IDynamicHost _dynamicHost; private readonly MethodInfo _write; private readonly MethodInfo _writeLine; private readonly PropertyInfo _generationEnvironment; private readonly PropertyInfo _errors; private readonly PropertyInfo _host; /// /// Creates an instance of the DynamicTextTransformation class around the passed in /// TextTransformation shapped instance passed in, or if the passed in instance /// already is a DynamicTextTransformation, it casts it and sends it back. /// public static DynamicTextTransformation Create(object instance) { if (instance == null) { throw new ArgumentNullException("instance"); } DynamicTextTransformation textTransformation = instance as DynamicTextTransformation; if (textTransformation != null) { return textTransformation; } return new DynamicTextTransformation(instance); } private DynamicTextTransformation(object instance) { _instance = instance; Type type = _instance.GetType(); _write = type.GetMethod("Write", new Type[] { typeof(string) }); _writeLine = type.GetMethod("WriteLine", new Type[] { typeof(string) }); _generationEnvironment = type.GetProperty("GenerationEnvironment", BindingFlags.Instance | BindingFlags.NonPublic); _host = type.GetProperty("Host"); _errors = type.GetProperty("Errors"); } /// /// Gets the value of the wrapped TextTranformation instance's GenerationEnvironment property /// public StringBuilder GenerationEnvironment { get { return (StringBuilder)_generationEnvironment.GetValue(_instance, null); } } /// /// Gets the value of the wrapped TextTranformation instance's Errors property /// public System.CodeDom.Compiler.CompilerErrorCollection Errors { get { return (System.CodeDom.Compiler.CompilerErrorCollection)_errors.GetValue(_instance, null); } } /// /// Calls the wrapped TextTranformation instance's Write method. /// public void Write(string text) { _write.Invoke(_instance, new object[] { text }); } /// /// Calls the wrapped TextTranformation instance's WriteLine method. /// public void WriteLine(string text) { _writeLine.Invoke(_instance, new object[] { text }); } /// /// Gets the value of the wrapped TextTranformation instance's Host property /// if available (shows up when hostspecific is set to true in the template directive) and returns /// the appropriate implementation of IDynamicHost /// public IDynamicHost Host { get { if (_dynamicHost == null) { if(_host == null) { _dynamicHost = new NullHost(); } else { _dynamicHost = new DynamicHost(_host.GetValue(_instance, null)); } } return _dynamicHost; } } } /// /// Reponsible for abstracting the use of Host between times /// when it is available and not /// public interface IDynamicHost { /// /// An abstracted call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolveParameterValue /// string ResolveParameterValue(string id, string name, string otherName); /// /// An abstracted call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolvePath /// string ResolvePath(string path); /// /// An abstracted call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost TemplateFile /// string TemplateFile { get; } /// /// Returns the Host instance cast as an IServiceProvider /// IServiceProvider AsIServiceProvider(); } /// /// Reponsible for implementing the IDynamicHost as a dynamic /// shape wrapper over the Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost interface /// rather than type dependent wrapper. We don't use the /// interface type so that the code can be run in preprocessed mode /// on a .net framework only installed machine. /// public class DynamicHost : IDynamicHost { private readonly object _instance; private readonly MethodInfo _resolveParameterValue; private readonly MethodInfo _resolvePath; private readonly PropertyInfo _templateFile; /// /// Creates an instance of the DynamicHost class around the passed in /// Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost shapped instance passed in. /// public DynamicHost(object instance) { _instance = instance; Type type = _instance.GetType(); _resolveParameterValue = type.GetMethod("ResolveParameterValue", new Type[] { typeof(string), typeof(string), typeof(string) }); _resolvePath = type.GetMethod("ResolvePath", new Type[] { typeof(string) }); _templateFile = type.GetProperty("TemplateFile"); } /// /// A call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolveParameterValue /// public string ResolveParameterValue(string id, string name, string otherName) { return (string)_resolveParameterValue.Invoke(_instance, new object[] { id, name, otherName }); } /// /// A call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolvePath /// public string ResolvePath(string path) { return (string)_resolvePath.Invoke(_instance, new object[] { path }); } /// /// A call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost TemplateFile /// public string TemplateFile { get { return (string)_templateFile.GetValue(_instance, null); } } /// /// Returns the Host instance cast as an IServiceProvider /// public IServiceProvider AsIServiceProvider() { return _instance as IServiceProvider; } } /// /// Reponsible for implementing the IDynamicHost when the /// Host property is not available on the TextTemplating type. The Host /// property only exists when the hostspecific attribute of the template /// directive is set to true. /// public class NullHost : IDynamicHost { /// /// An abstraction of the call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolveParameterValue /// that simply retuns null. /// public string ResolveParameterValue(string id, string name, string otherName) { return null; } /// /// An abstraction of the call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost ResolvePath /// that simply retuns the path passed in. /// public string ResolvePath(string path) { return path; } /// /// An abstraction of the call to Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost TemplateFile /// that returns null. /// public string TemplateFile { get { return null; } } /// /// Returns null. /// public IServiceProvider AsIServiceProvider() { return null; } } /// /// Responsible for encapsulating the constants defined in Metadata /// public static class MetadataConstants { public const string EDMX_NAMESPACE_V1 = "http://schemas.microsoft.com/ado/2007/06/edmx"; public const string EDMX_NAMESPACE_V2 = "http://schemas.microsoft.com/ado/2008/10/edmx"; public const string CSDL_EXTENSION = ".csdl"; public const string CSDL_NAMESPACE_V1 = "http://schemas.microsoft.com/ado/2006/04/edm"; public const string CSDL_NAMESPACE_V2 = "http://schemas.microsoft.com/ado/2008/09/edm"; public const string CSDL_EDMX_SECTION_NAME = "ConceptualModels"; public const string CSDL_ROOT_ELEMENT_NAME = "Schema"; public const string EDM_ANNOTATION_09_02 = "http://schemas.microsoft.com/ado/2009/02/edm/annotation"; public const string SSDL_EXTENSION = ".ssdl"; public const string SSDL_NAMESPACE_V1 = "http://schemas.microsoft.com/ado/2006/04/edm/ssdl"; public const string SSDL_NAMESPACE_V2 = "http://schemas.microsoft.com/ado/2009/02/edm/ssdl"; public const string SSDL_EDMX_SECTION_NAME = "StorageModels"; public const string SSDL_ROOT_ELEMENT_NAME = "Schema"; public const string MSL_EXTENSION = ".msl"; public const string MSL_NAMESPACE_V1 = "urn:schemas-microsoft-com:windows:storage:mapping:CS"; public const string MSL_NAMESPACE_V2 = "http://schemas.microsoft.com/ado/2008/09/mapping/cs"; public const string MSL_EDMX_SECTION_NAME = "Mappings"; public const string MSL_ROOT_ELEMENT_NAME = "Mapping"; } #>