diff --git a/EDMXTrimmer/EDMXTrimmer/EdmxTrimmer.cs b/EDMXTrimmer/EDMXTrimmer/EdmxTrimmer.cs index 6e29b5d..c49ea34 100644 --- a/EDMXTrimmer/EDMXTrimmer/EdmxTrimmer.cs +++ b/EDMXTrimmer/EDMXTrimmer/EdmxTrimmer.cs @@ -20,6 +20,7 @@ class EdmxTrimmer private XmlDocument _xmlDocument; private XmlNode _firstSchemaNode; private string ENTITYNAMESPACE; + private string ENTITYNAMESPACE_ALIAS; private const string TAG_SCHEMA = "Schema"; private const string TAG_ENTITY_TYPE = "EntityType"; private const string TAG_ENTITY_SET = "EntitySet"; @@ -31,12 +32,15 @@ class EdmxTrimmer private const string TAG_PARAMETER = "Parameter"; private const string TAG_ENUM_TYPE = "EnumType"; private const string TAG_ACTION_IMPORT = "ActionImport"; + private const string ATTRIBUTE_ALIAS = "Alias"; private const string ATTRIBUTE_NAMESPACE = "Namespace"; private const string ATTRIBUTE_NAME = "Name"; private const string ATTRIBUTE_TYPE = "Type"; private const string ATTRIBUTE_TARGET = "Target"; private const string ATTRIBUTE_AXType = "AXType"; + private readonly IDictionary entityTypeRegexps = new Dictionary(); + public EdmxTrimmer( string edmxFile, string outputFileName, @@ -81,6 +85,11 @@ public void AnalyzeFile() this._firstSchemaNode = this._xmlDocument.GetElementsByTagName(TAG_SCHEMA)[0]; this.ENTITYNAMESPACE = this._firstSchemaNode.Attributes[ATTRIBUTE_NAMESPACE].Value + "."; + var aliasAttrValue = this._firstSchemaNode.Attributes[ATTRIBUTE_ALIAS]?.Value.Trim(); + if(!string.IsNullOrEmpty(aliasAttrValue)) { + this.ENTITYNAMESPACE_ALIAS = aliasAttrValue + "."; + } + var entitySets = this._xmlDocument.GetElementsByTagName(TAG_ENTITY_SET).Cast().ToList(); var entityTypes = this._xmlDocument.GetElementsByTagName(TAG_ENTITY_TYPE).Cast().ToList(); var originalEntityCount = entitySets.Count; @@ -125,11 +134,10 @@ private void RemoveAllEntitiesExcept( List entitySets, List entityTypes) { - string regex = EntitySearchTermsToRegularExpression(entitiesToKeep); - var entitiesKeep = entitySets.Where(n => Regex.IsMatch(n.Attributes[ATTRIBUTE_NAME].Value, regex)).ToList(); + var (entitySetsToKeep, entityTypeNamesToKeep) = FilterByEntity(entitiesToKeep, entitySets, entityTypes, true); - RemoveEntitySets(entitySets, entitiesKeep); - RemoveEntityTypes(entityTypes, entitiesKeep); + RemoveEntitySets(entitySets, entitySetsToKeep); + RemoveEntityTypes(entityTypes, entityTypeNamesToKeep); } private void RemoveExcludedEntities( @@ -137,19 +145,37 @@ private void RemoveExcludedEntities( List entitySets, List entityTypes) { - string regex = EntitySearchTermsToRegularExpression(entitiesToExclude); - var entitiesKeep = entitySets.Where(n => !Regex.IsMatch(n.Attributes[ATTRIBUTE_NAME].Value, regex)).ToList(); + var (entitySetsToKeep, entityTypeNamesToKeep) = FilterByEntity(entitiesToExclude, entitySets, entityTypes, false); - RemoveEntitySets(entitySets, entitiesKeep); - RemoveEntityTypes(entityTypes, entitiesKeep); + RemoveEntitySets(entitySets, entitySetsToKeep); + RemoveEntityTypes(entityTypes, entityTypeNamesToKeep); } - private string EntitySearchTermsToRegularExpression(List entitiesToKeep) + private (List EntitySetsToKeep, IReadOnlyCollection EntityTypeNamesToKeep) FilterByEntity(IEnumerable filteringEntities, IEnumerable entitySets, IEnumerable entityTypes, bool includeFiltered) { - List listRegularExpression = entitiesToKeep.Select(s => EntitySearchTermToRegularExpression(s)).ToList(); - string regex = String.Join("|", listRegularExpression.ToArray()); + var nameRegex = EntitySearchTermsToRegularExpression(filteringEntities); - return regex; + var entitySetsNodes = entitySets + .Where(n => Regex.IsMatch(n.Attributes[ATTRIBUTE_NAME].Value, nameRegex) ? includeFiltered : !includeFiltered) + .ToList(); + + var entityTypeNames = entitySetsNodes + .Select(n => GetEntityTypeWithoutNamespace(n, TAG_ENTITY_TYPE)) + .Concat(entityTypes + .Where(node => Regex.IsMatch(node.Attributes[ATTRIBUTE_NAME].Value, nameRegex) ? includeFiltered : !includeFiltered) + .Select(node => GetEntityTypeWithoutNamespace(node, ATTRIBUTE_NAME)) + ) + .Distinct() + .ToList(); + + return (entitySetsNodes, entityTypeNames); + } + + private string EntitySearchTermsToRegularExpression(IEnumerable entitiesToKeep) + { + var parts = entitiesToKeep.Select(EntitySearchTermToRegularExpression); + + return String.Join("|", parts); } private string EntitySearchTermToRegularExpression(string searchTerm) @@ -164,7 +190,7 @@ private string EntitySearchTermToRegularExpression(string searchTerm) return regex; } - private void RemoveEntitySets(List entitySets, List entitiesKeep) + private void RemoveEntitySets(IEnumerable entitySets, List entitiesKeep) { // Remove entities not required (EntitySet) entitySets.Except(entitiesKeep).ToList().ForEach(n => n.ParentNode.RemoveChild(n)); @@ -179,81 +205,42 @@ private void RemoveEntitySets(List entitySets, List entitiesKe }); } - private void RemoveEntityTypes(List entityTypes, List entitiesKeep) + private void RemoveEntityTypes(IReadOnlyCollection entityTypes, IReadOnlyCollection entitiesNamesToKeep) { - List entityTypesFound = new List(); - entitiesKeep.ForEach(n => - { - string entityType = n.Attributes[TAG_ENTITY_TYPE].Value; - entityType = entityType.Replace(ENTITYNAMESPACE, ""); - entityTypesFound.Add(entityType); - }); - // Remove all navigation properties this._xmlDocument.GetElementsByTagName(TAG_NAVIGATION_PROPERTY).Cast() - .Where(navProp => !entityTypesFound.Any(entityType => EntityExists(navProp, entityType))).ToList() + .Where(navProp => !entitiesNamesToKeep.Any(entityType => EntityExists(navProp, entityType))).ToList() .ForEach(n => n.ParentNode.RemoveChild(n)); // Remove entity not required (EntityType) - var entityTypesKeep = entityTypes.Where(n => entityTypesFound.Contains(n.Attributes[ATTRIBUTE_NAME].Value)).ToList(); - entityTypes.Except(entityTypesKeep).ToList().ForEach(n => n.ParentNode.RemoveChild(n)); - + var entityTypesToKeep = entityTypes.Where(n => entitiesNamesToKeep.Contains(n.Attributes[ATTRIBUTE_NAME].Value)).ToList(); + entityTypes.Except(entityTypesToKeep).ToList().ForEach(n => n.ParentNode.RemoveChild(n)); + // Remove all Actions this._xmlDocument.GetElementsByTagName(TAG_ACTION).Cast() - .Where(action => !entityTypesFound.Any(entityType => action.ChildNodes.Cast(). + .Where(action => !entitiesNamesToKeep.Any(entityType => action.ChildNodes.Cast(). Any(childNode => EntityExists(childNode, entityType)))).ToList() .ForEach(n => n.ParentNode.RemoveChild(n)); // Determine enums to keep List enumTypesFound = new List(); // Enums from entity type properties - entityTypesKeep.ForEach(n => - { - var properties = n.ChildNodes.Cast().Where(prop => prop.Name.Equals(TAG_PROPERTY)).ToList(); - properties.ForEach(prop => - { - if (prop.Attributes[ATTRIBUTE_TYPE] != null) - { - var enumType = prop.Attributes[ATTRIBUTE_TYPE].Value; - if (enumType.StartsWith(ENTITYNAMESPACE)) - { - enumType = enumType.Replace(ENTITYNAMESPACE, ""); - enumTypesFound.Add(enumType); - } - } - }); - }); + + var propertiesTypes = entityTypesToKeep.SelectMany(typeNode => GetEntityTypesFromNodeChildren(typeNode, TAG_PROPERTY)); + enumTypesFound.AddRange(propertiesTypes); + // Enums from actions var entityActions = this._xmlDocument.GetElementsByTagName(TAG_ACTION).Cast().ToList(); - entityActions.ForEach(action => + entityActions.ForEach(actionNode => { // Enums from parameters - var parameters = action.ChildNodes.Cast().Where(param => param.Name.Equals(TAG_PARAMETER)).ToList(); - parameters.ForEach(param => - { - if (param.Attributes[ATTRIBUTE_TYPE] != null) - { - var enumType = param.Attributes[ATTRIBUTE_TYPE].Value; - if (enumType.StartsWith(ENTITYNAMESPACE)) - { - enumType = enumType.Replace(ENTITYNAMESPACE, ""); - enumTypesFound.Add(enumType); - } - } - }); + var parametersTypes = GetEntityTypesFromNodeChildren(actionNode, TAG_PARAMETER)!; + enumTypesFound.AddRange(parametersTypes); + // Enum from return type // get the first child node with name "ReturnType" if it exists - var returnType = action.ChildNodes.Cast().FirstOrDefault(node => node.Name.Equals(TAG_RETURN_TYPE)); - if (returnType != null && returnType.Attributes[ATTRIBUTE_TYPE] != null) - { - var enumType = returnType.Attributes[ATTRIBUTE_TYPE].Value; - if (enumType.StartsWith(ENTITYNAMESPACE)) - { - enumType = enumType.Replace(ENTITYNAMESPACE, ""); - enumTypesFound.Add(enumType); - } - } - + var returnType = GetEntityTypesFromNodeChildren(actionNode, TAG_RETURN_TYPE)!; + enumTypesFound.Add(returnType.FirstOrDefault()); }); // Remove unused Enums except AXType this._xmlDocument.GetElementsByTagName(TAG_ENUM_TYPE).Cast() @@ -263,7 +250,45 @@ private void RemoveEntityTypes(List entityTypes, List entities .ForEach(n => n.ParentNode.RemoveChild(n)); this._xmlDocument.Save(OutputFileName); + + return; + + IReadOnlyCollection GetEntityTypesFromNodeChildren(XmlNode typeNode, string nodeName) => + typeNode + .ChildNodes + .Cast() + .Where(prop => prop.Name.Equals(nodeName)) + .Select(RemoveNamespace) + .Where(name => name != null) + .ToList(); + + string RemoveNamespace(XmlNode xmlNode) { + var enumType = xmlNode.Attributes[ATTRIBUTE_TYPE]?.Value; + + if(enumType == null) { + return null; + } + if(ENTITYNAMESPACE_ALIAS != null && enumType.StartsWith(ENTITYNAMESPACE_ALIAS)) { + return enumType.Replace(ENTITYNAMESPACE_ALIAS, ""); + } + if(enumType.StartsWith(ENTITYNAMESPACE)) { + return enumType.Replace(ENTITYNAMESPACE, ""); + } + + return null; + } + } + + private string GetEntityTypeWithoutNamespace(XmlNode n, string attributeName) { + var entityType = n.Attributes[attributeName]?.Value; + if(ENTITYNAMESPACE_ALIAS != null) { + var replaced = entityType.Replace(ENTITYNAMESPACE_ALIAS, ""); + if(replaced != entityType) { + return replaced; + } + } + return entityType.Replace(ENTITYNAMESPACE, ""); } private void RemovePrimaryAnnotations() @@ -282,9 +307,28 @@ private void RemoveActionImports() .ForEach(n => n.ParentNode.RemoveChild(n)); } - private bool EntityExists(XmlNode xmlNode, string entityType) - { - return xmlNode.Attributes[ATTRIBUTE_TYPE] == null ? false : Regex.IsMatch(xmlNode.Attributes[ATTRIBUTE_TYPE].Value, ENTITYNAMESPACE + entityType + "\\)?$"); + private bool EntityExists(XmlNode xmlNode, string entityType) { + var typeValue = xmlNode.Attributes[ATTRIBUTE_TYPE]?.Value; + + if(null == typeValue) { + return false; + } + if(ENTITYNAMESPACE_ALIAS != null && IsEntityTypeMatches(entityType, ENTITYNAMESPACE_ALIAS, typeValue)) { + return true; + } + return IsEntityTypeMatches(entityType, ENTITYNAMESPACE, typeValue); + } + + private bool IsEntityTypeMatches(string entityType, string @namespace, string source) { + var key = @namespace + entityType; + + if(!entityTypeRegexps.TryGetValue(key, out var regex)) { + var pattern = Regex.Escape(@namespace + entityType) + "\\)?$"; + regex = new Regex(pattern); + entityTypeRegexps.Add(key, regex); + } + + return regex.IsMatch(source!); } } -} +} \ No newline at end of file