C#中缓存含Include/Import的XSD进行XML验证的实现问题
I’ve run into this exact issue before—getting XSD includes/imports to play nice with a cached XmlSchemaSet can be tricky, but once you nail it, the performance boost is huge. Here’s how to resolve the problem and get your cached schemas working reliably:
Why Your Current Setup Isn’t Working
When you add just the main XSD to XmlSchemaSet, it won’t automatically resolve included/imported schemas unless you configure a resolver or explicitly add dependencies. The compiler throws errors because it can’t locate those dependent files during compilation, leaving your schema set incomplete and unusable for repeated validation.
Step-by-Step Solution to Cache a Complete XmlSchemaSet
1. Precompile the Schema Set with All Dependencies
You have two reliable approaches to ensure all schemas (including nested ones) are included in the cached set:
Approach A: Explicitly Add All Schemas
If you know the paths to every included/imported XSD, add them directly to the set before compiling. This guarantees all schemas are loaded and validated together.
// Cache this instance (use a static variable, singleton, or app-level cache) private static XmlSchemaSet _cachedSchemaSet; public static XmlSchemaSet GetCachedSchemaSet() { if (_cachedSchemaSet == null) { _cachedSchemaSet = new XmlSchemaSet(); // Add dependent schemas first (order may matter for cross-references) _cachedSchemaSet.Add(null, @"path/to/dependency1.xsd"); _cachedSchemaSet.Add(null, @"path/to/dependency2.xsd"); // Add the main schema last _cachedSchemaSet.Add(null, @"path/to/main.xsd"); // Compile to validate all schemas and lock the set for reuse _cachedSchemaSet.Compile(); } return _cachedSchemaSet; }
Approach B: Use a Resolver to Auto-Resolve Includes/Imports
If your main XSD uses relative paths for includes/imports, configure an XmlReader with a resolver to let XmlSchemaSet find those files automatically.
private static XmlSchemaSet _cachedSchemaSet; public static XmlSchemaSet GetCachedSchemaSet() { if (_cachedSchemaSet == null) { _cachedSchemaSet = new XmlSchemaSet(); // Set a resolver to handle relative paths (default is XmlUrlResolver) _cachedSchemaSet.XmlResolver = new XmlUrlResolver(); // Load the main schema with a reader that knows the base path using (var reader = XmlReader.Create(@"path/to/main.xsd", new XmlReaderSettings { XmlResolver = _cachedSchemaSet.XmlResolver })) { _cachedSchemaSet.Add(null, reader); } // Compile to resolve and load all nested schemas _cachedSchemaSet.Compile(); } return _cachedSchemaSet; }
2. Reuse the Cached Schema Set for Validation
Once your schema set is compiled and cached, use it for every XML validation to avoid reloading schemas:
public bool ValidateXml(string xmlContent) { var schemaSet = GetCachedSchemaSet(); var settings = new XmlReaderSettings { Schemas = schemaSet, ValidationType = ValidationType.Schema, ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings }; // Handle validation errors (customize this to your needs) settings.ValidationEventHandler += (sender, e) => { if (e.Severity == XmlSeverityType.Error) throw new XmlSchemaValidationException($"Validation failed: {e.Message}"); }; // Read through the XML to trigger validation using (var reader = XmlReader.Create(new StringReader(xmlContent), settings)) { while (reader.Read()) { } } return true; }
Key Notes to Avoid Issues
- Compile Once: Always call
Compile()after adding all schemas—this ensures dependencies are resolved and the set is locked for safe reuse. - Cache Correctly: Store the compiled
XmlSchemaSetin a static variable, singleton, or application-level cache (likeMemoryCache) to avoid recreating it on every validation. - Permission Checks: Ensure your app has read access to all XSD files (including dependencies) when compiling the set.
- Relative Paths: If using relative paths, verify the resolver’s base directory is correct—you can set the
BaseUriproperty on theXmlReaderor use a custom resolver for embedded resources.
Troubleshooting Tips
- If you still get "cannot resolve schema" errors, enable verbose logging to identify missing schemas:
settings.ValidationEventHandler += (sender, e) => { Console.WriteLine($"Validation {e.Severity}: {e.Message}"); }; - Double-check that
targetNamespacevalues in included/imported schemas match references in the main schema. - For embedded schemas, create a custom
XmlResolverto load them directly from your assembly.
内容的提问来源于stack exchange,提问作者aeb-dev

