diff --git a/.github/workflows/pull-request-build.yml b/.github/workflows/pull-request-build.yml index f5a0b6e..7d97515 100644 --- a/.github/workflows/pull-request-build.yml +++ b/.github/workflows/pull-request-build.yml @@ -13,8 +13,15 @@ jobs: - uses: actions/checkout@v3 - name: Build run: dotnet build --configuration Release EDMXTrimmer + - name: Test + run: dotnet test --logger:nunit --configuration Release EDMXTrimmer + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action/composite@v2 + with: + files: '**/TestResults/*.xml' + comment_mode: 'off' - name: Publish - run: dotnet publish --configuration Release EDMXTrimmer --output zip + run: dotnet publish --configuration Release EDMXTrimmer/EDMXTrimmer --output zip - name: Upload EDMXTrimmer.zip uses: actions/upload-artifact@v3 with: diff --git a/EDMXTrimmer/EDMXTrimmer.sln b/EDMXTrimmer/EDMXTrimmer.sln index 40d6ea7..a644a06 100644 --- a/EDMXTrimmer/EDMXTrimmer.sln +++ b/EDMXTrimmer/EDMXTrimmer.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.28803.202 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EDMXTrimmer", "EDMXTrimmer\EDMXTrimmer.csproj", "{CEE5566E-5FFF-46E4-BC78-166980691951}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EDMXTrimmerTest", "EDMXTrimmerTest\EDMXTrimmerTest.csproj", "{D555AE09-A8C1-4368-AA62-67AE517230B4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {CEE5566E-5FFF-46E4-BC78-166980691951}.Debug|Any CPU.Build.0 = Debug|Any CPU {CEE5566E-5FFF-46E4-BC78-166980691951}.Release|Any CPU.ActiveCfg = Release|Any CPU {CEE5566E-5FFF-46E4-BC78-166980691951}.Release|Any CPU.Build.0 = Release|Any CPU + {D555AE09-A8C1-4368-AA62-67AE517230B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D555AE09-A8C1-4368-AA62-67AE517230B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D555AE09-A8C1-4368-AA62-67AE517230B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D555AE09-A8C1-4368-AA62-67AE517230B4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/EDMXTrimmer/EDMXTrimmer/EdmxTrimmer.cs b/EDMXTrimmer/EDMXTrimmer/EdmxTrimmer.cs index 6e29b5d..326b99b 100644 --- a/EDMXTrimmer/EDMXTrimmer/EdmxTrimmer.cs +++ b/EDMXTrimmer/EDMXTrimmer/EdmxTrimmer.cs @@ -6,7 +6,7 @@ namespace EDMXTrimmer { - class EdmxTrimmer + public class EdmxTrimmer { public string EdmxFile { get; private set; } public bool Verbose { get; private set; } @@ -15,11 +15,16 @@ class EdmxTrimmer public bool EntitiesAreRegularExpressions { get; private set; } public bool RemovePrimaryAnnotationsFlag { get; private set; } public bool RemoveActionImportsFlag { get; private set; } + public bool RemoveFunctionImportsFlag { get; init; } + public bool RemoveComplexTypesFlag { get; init; } public string OutputFileName { get; set; } + public IReadOnlyCollection ActionsToInclude { get; set; } + 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 +36,17 @@ 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 TAG_FUNCTION_IMPORT = "FunctionImport"; + private const string TAG_COMPLEXTYPE = "ComplexType"; + 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 +91,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; @@ -110,6 +125,15 @@ public void AnalyzeFile() RemoveActionImports(); } + if (this.RemoveFunctionImportsFlag) + { + RemoveFunctionImports(); + } + if (this.RemoveComplexTypesFlag) + { + RemoveComplexTypes(); + } + this._xmlDocument.Save(OutputFileName); Console.WriteLine($"Trimmed EDMX saved to file: {OutputFileName}"); if (Verbose) @@ -125,11 +149,11 @@ 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) = + FilterByEntityIncluding(entitiesToKeep, entitySets, entityTypes); - RemoveEntitySets(entitySets, entitiesKeep); - RemoveEntityTypes(entityTypes, entitiesKeep); + RemoveEntitySets(entitySets, entitySetsToKeep); + RemoveEntityTypes(entityTypes, entityTypeNamesToKeep); } private void RemoveExcludedEntities( @@ -137,19 +161,92 @@ 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) = + FilterByEntityExcluding(entitiesToExclude, entitySets, entityTypes); - RemoveEntitySets(entitySets, entitiesKeep); - RemoveEntityTypes(entityTypes, entitiesKeep); + RemoveEntitySets(entitySets, entitySetsToKeep); + RemoveEntityTypes(entityTypes, entityTypeNamesToKeep); } - private string EntitySearchTermsToRegularExpression(List entitiesToKeep) + /// + /// Returns a list of EntitySets and EntityTypes that match the filteringEntities. + /// + /// The entities to filter by. + /// The entity sets to filter. + /// The entities to filter. + /// A tuple of filtered entity sets and filtered entity names. + internal (List EntitySetsToKeep, IReadOnlyCollection EntityTypeNamesToKeep) + FilterByEntityIncluding( + IEnumerable filteringEntities, + IEnumerable entitySets, + IEnumerable entityTypes) { - List listRegularExpression = entitiesToKeep.Select(s => EntitySearchTermToRegularExpression(s)).ToList(); - string regex = String.Join("|", listRegularExpression.ToArray()); + return FilterByEntity(filteringEntities, entitySets, entityTypes, true); + } - return regex; + /// + /// Returns a list of EntitySets and EntityTypes that do not match the filteringEntities. + /// + /// The entities that should not be part of the result. + /// The entity sets to filter. + /// The entities to filter. + /// A tuple of filtered entity sets and filtered entity names. + internal (List EntitySetsToKeep, IReadOnlyCollection EntityTypeNamesToKeep) + FilterByEntityExcluding( + IEnumerable filteringEntities, + IEnumerable entitySets, + IEnumerable entityTypes) + { + return FilterByEntity(filteringEntities, entitySets, entityTypes, false); + } + + private (List EntitySetsToKeep, IReadOnlyCollection EntityTypeNamesToKeep) + FilterByEntity( + IEnumerable filteringEntities, + IEnumerable entitySets, + IEnumerable entityTypes, + bool includeFiltered) + { + var nameRegex = EntitySearchTermsToRegularExpression(filteringEntities); + + // remove items from entityTypes that are referenced by entitySets + // those entityTypes will be filtered via the entitySets filtering + var entityTypeNamesReferencedByEntitySets = entitySets + .Select(n => GetEntityTypeWithoutNamespace(n, TAG_ENTITY_TYPE)) + .ToList(); + var entityTypesNotReferencedByEntitySets = entityTypes + .Where(node => !entityTypeNamesReferencedByEntitySets.Contains(GetEntityTypeWithoutNamespace(node, ATTRIBUTE_NAME))) + .ToList(); + + // filter entitySets + var entitySetsFiltered = entitySets + .Where(n => Regex.IsMatch(n.Attributes[ATTRIBUTE_NAME].Value, nameRegex) ? includeFiltered : !includeFiltered) + .ToList(); + + // filter entityTypes not referenced by entitySets + var entityTypesFiltered = entityTypesNotReferencedByEntitySets + .Where(node => Regex.IsMatch(node.Attributes[ATTRIBUTE_NAME].Value, nameRegex) ? includeFiltered : !includeFiltered) + .ToList(); + + // combine names of + // - entityTypes referenced by filtered entitySets + // - filtered entityTypes not referenced by entitySets + var entityTypeNames = entitySetsFiltered + .Select(n => GetEntityTypeWithoutNamespace(n, TAG_ENTITY_TYPE)) + .Concat(entityTypesFiltered + .Select(node => GetEntityTypeWithoutNamespace(node, ATTRIBUTE_NAME)) + ) + .Distinct() + .ToList(); + + return (entitySetsFiltered, entityTypeNames); + } + + private string EntitySearchTermsToRegularExpression(IEnumerable entitiesToKeep) + { + var parts = entitiesToKeep.Select(EntitySearchTermToRegularExpression); + + return String.Join("|", parts); } private string EntitySearchTermToRegularExpression(string searchTerm) @@ -164,7 +261,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 +276,52 @@ 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() - .ForEach(n => n.ParentNode.RemoveChild(n)); + RemoveNodes(_xmlDocument + .GetElementsByTagName(TAG_NAVIGATION_PROPERTY) + .Cast() + .Where(navProp => !entitiesNamesToKeep.Any(entityType => NodeReferencesEntity(navProp, entityType))) + ); // 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(); + RemoveNodes(entityTypes.Except(entityTypesToKeep)); + // Remove all Actions - this._xmlDocument.GetElementsByTagName(TAG_ACTION).Cast() - .Where(action => !entityTypesFound.Any(entityType => action.ChildNodes.Cast(). - Any(childNode => EntityExists(childNode, entityType)))).ToList() - .ForEach(n => n.ParentNode.RemoveChild(n)); + var allActions = _xmlDocument + .GetElementsByTagName(TAG_ACTION) + .Cast(); + + var actionsToRemove = allActions.Where(actionNode => { + if(ActionsToInclude != null && ActionsToInclude.Any()) { + return true != ActionIsWhitelisted(GetActionName(actionNode)); + } + return !ActionReferencesAnyEntity(actionNode); + }); + + RemoveNodes(actionsToRemove); // 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 +331,75 @@ private void RemoveEntityTypes(List entityTypes, List entities .ForEach(n => n.ParentNode.RemoveChild(n)); this._xmlDocument.Save(OutputFileName); + + return; + + IEnumerable 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; + } + + bool ActionIsWhitelisted(string? actionName) { + if(null == actionName || null == ActionsToInclude || !ActionsToInclude.Any()) { + return false; + } + var regex = GetRegexOrCreate("ACTIONS-TO-INCLUDE", () => EntitySearchTermsToRegularExpression(ActionsToInclude)); + return regex.IsMatch(actionName); + } + + bool ActionReferencesAnyEntity(XmlNode actionNode) => entitiesNamesToKeep.Any(entityType => AnyChildReferencesEntity(actionNode, entityType)); + + bool AnyChildReferencesEntity(XmlNode action, string entityType) => + action + .ChildNodes + .Cast() + .Any(childNode => NodeReferencesEntity(childNode, entityType)); + + bool NodeReferencesEntity(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 static string GetActionName(XmlNode actionNode) => actionNode.Attributes[ATTRIBUTE_NAME]?.Value; + + 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() @@ -281,10 +417,44 @@ private void RemoveActionImports() .ToList() .ForEach(n => n.ParentNode.RemoveChild(n)); } + private void RemoveFunctionImports() + { + this._xmlDocument.GetElementsByTagName(TAG_FUNCTION_IMPORT).Cast() + .ToList() + .ForEach(n => n.ParentNode.RemoveChild(n)); + } - private bool EntityExists(XmlNode xmlNode, string entityType) + private void RemoveComplexTypes() { - return xmlNode.Attributes[ATTRIBUTE_TYPE] == null ? false : Regex.IsMatch(xmlNode.Attributes[ATTRIBUTE_TYPE].Value, ENTITYNAMESPACE + entityType + "\\)?$"); + _xmlDocument.GetElementsByTagName(TAG_COMPLEXTYPE) + .Cast() + .ToList() + .ForEach(n => n.ParentNode.RemoveChild(n)); + } + + private bool IsEntityTypeMatches(string entityType, string @namespace, string source) { + var key = "ENTITY-" + @namespace + entityType; + var regex = GetRegexOrCreate(key, () => Regex.Escape(@namespace + entityType) + "\\)?$"); + + return regex.IsMatch(source!); + } + + private Regex GetRegexOrCreate(string key, Func patternFactory) { + if(entityTypeRegexps.TryGetValue(key!, out var cached)) { + return cached; + } + + var pattern = patternFactory(); + var target = new Regex(pattern!); + + entityTypeRegexps.Add(key, target); + return target; + } + + private static void RemoveNodes(IEnumerable nodesToRemove) { + foreach(var node in nodesToRemove!.ToList()) { + node.ParentNode.RemoveChild(node); + } } } -} +} \ No newline at end of file diff --git a/EDMXTrimmer/EDMXTrimmer/Program.cs b/EDMXTrimmer/EDMXTrimmer/Program.cs index c677489..41324ce 100644 --- a/EDMXTrimmer/EDMXTrimmer/Program.cs +++ b/EDMXTrimmer/EDMXTrimmer/Program.cs @@ -7,18 +7,23 @@ namespace EDMXTrimmer { public class Options { + private const string EntitiesToKeepName = "entitiestokeep"; + private const string EntitiesToExcludeName = "entitiestoexclude"; + [Option( Required = true, HelpText = "EDMX source file")] public string EdmxFile { get; set; } [Option( + longName: EntitiesToKeepName, Required = false, HelpText = "Enter the public name & collection name. All values to be separated with commas. Supports ? and * wildcards.", Separator = ',')] public IEnumerable EntitiesToKeep { get; set; } [Option( + longName: EntitiesToExcludeName, Required = false, HelpText = "Enter the public name & collection name. All values to be separated with commas. Supports ? and * wildcards.", Separator = ',')] @@ -54,6 +59,23 @@ public class Options Default = false)] public bool RemoveActionImports { get; set; } + [Option( + Required = false, + HelpText = "Function imports are removed from the EDMX file", + Default = false)] + public bool RemoveFunctionImports { get; set; } + + [Option( + Required = false, + HelpText = "ComplexType nodes are removed from the EDMX file", + Default = false)] + public bool RemoveComplexTypes { get; set; } + + [Option( + Required = false, + HelpText = $"Enter action names to keep, works with \"{EntitiesToKeepName}\" and \"{EntitiesToExcludeName}\" options. All values to be separated with commas. Supports ? and * wildcards.", + Separator = ',')] + public IReadOnlyCollection ActionsToKeep { get; set; } } class Program { @@ -72,7 +94,11 @@ static void Main(string[] args) entitiesToExclude:opt.EntitiesToExclude.ToList(), entitiesAreRegularExpressions:opt.EntitiesAreRegularExpressions, removePrimaryAnnotations:opt.RemovePrimaryAnnotations, - removeActionImports:opt.RemoveActionImports); + removeActionImports:opt.RemoveActionImports) { + RemoveComplexTypesFlag = opt.RemoveComplexTypes, + RemoveFunctionImportsFlag = opt.RemoveFunctionImports, + ActionsToInclude = opt.ActionsToKeep + }; trimmer.AnalyzeFile(); } diff --git a/EDMXTrimmer/EDMXTrimmerTest/EDMXTrimmerTest.csproj b/EDMXTrimmer/EDMXTrimmerTest/EDMXTrimmerTest.csproj new file mode 100644 index 0000000..7cd0bb3 --- /dev/null +++ b/EDMXTrimmer/EDMXTrimmerTest/EDMXTrimmerTest.csproj @@ -0,0 +1,31 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/EDMXTrimmer/EDMXTrimmerTest/EdmxTrimmerTest.cs b/EDMXTrimmer/EDMXTrimmerTest/EdmxTrimmerTest.cs new file mode 100644 index 0000000..2896613 --- /dev/null +++ b/EDMXTrimmer/EDMXTrimmerTest/EdmxTrimmerTest.cs @@ -0,0 +1,176 @@ +using NUnit.Framework; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using EDMXTrimmer; + +namespace EDMXTrimmer.Tests +{ + public class EdmxTrimmerTests + { + private const string TestEdmxFile = "TripPin.xml"; + private const string TestOutputFile = "TripPin-Trimmed.xml"; + + [SetUp] + public void Setup() + { + + } + + [TearDown] + public void TearDown() + { + // Delete the test EDMX file and output file + File.Delete(TestOutputFile); + } + + [Test] + public void TestEdmxTrimmer() + { + // Arrange + var entitiesToKeep = new List { "Photos", "People" }; + var entitiesToExclude = new List { "People" }; + var edmxTrimmer = new EdmxTrimmer(TestEdmxFile, TestOutputFile, true, entitiesToKeep, entitiesToExclude); + + // Act + edmxTrimmer.AnalyzeFile(); + + // Assert + Assert.That(File.Exists(TestOutputFile), Is.True); + + var trimmedEdmx = File.ReadAllText(TestOutputFile); + Assert.Multiple(() => + { + Assert.That(trimmedEdmx, Does.Contain(" { @"\b\w+s\b" }; // Keep entities that end with "s" + var entitiesToExclude = new List { @"P\w+" }; // Exclude entities that start with "P" + var edmxTrimmer = new EdmxTrimmer( + TestEdmxFile, + TestOutputFile, + entitiesAreRegularExpressions: true, + entitiesToKeep: entitiesToKeep, + entitiesToExclude: entitiesToExclude); + + // Act + edmxTrimmer.AnalyzeFile(); + + // Assert + Assert.That(File.Exists(TestOutputFile), Is.True); + + var trimmedEdmx = File.ReadAllText(TestOutputFile); + Assert.Multiple(() => + { + Assert.That(trimmedEdmx, Does.Not.Contain(" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Org.OData.Core.V1.Permission/Read + + + + + + image/jpeg + + + + + + + + + + Org.OData.Core.V1.Permission/Read + + + + + + + + + + + + + + + + + + + + + Org.OData.Core.V1.Permission/Read + + + + + + + + + + + Org.OData.Core.V1.Permission/Read + + + + + + + + + + + + + + + Org.OData.Core.V1.Permission/Read + + + + + + + + + + + + + + + + + + + + + + + + + + + Org.OData.Core.V1.Permission/Read + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Org.OData.Capabilities.V1.SearchExpressions/none + + + + + + + + + + + + + + + + + + + + + + Concurrency + + + + + + + Org.OData.Capabilities.V1.NavigationType/None + + + + + + + + Org.OData.Capabilities.V1.NavigationType/Recursive + + + + + + + + + + + Org.OData.Capabilities.V1.SearchExpressions/none + + + + + + + + + Trips + Friends + + + + + + + + + + + + Org.OData.Capabilities.V1.SearchExpressions/none + + + + + + + + + + + + + + + + + + + Org.OData.Capabilities.V1.SearchExpressions/none + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Org.OData.Capabilities.V1.ConformanceLevelType/Advanced + + + + + application/json;odata.metadata=full;IEEE754Compatible=false;odata.streaming=true + + application/json;odata.metadata=minimal;IEEE754Compatible=false;odata.streaming=true + + application/json;odata.metadata=none;IEEE754Compatible=false;odata.streaming=true + + + + + + + contains + endswith + startswith + length + indexof + substring + tolower + toupper + trim + concat + year + month + day + hour + minute + second + round + floor + ceiling + cast + isof + + + + + + \ No newline at end of file