One of my fetishes is including as much resources as possible within the executable. This is fairly easy to do with Visual Studio. Just select desired file and change Build Action
property to Embedded Resource
.
Biggest benefit is that you can count on file always being there. There is no need to add file to installer nor to do error handling. And, even when you need to have user-modifiable files, you can always tuck away safe defaults far out of reach of an inexperienced user.
When you have XML file stored as this, loading is as easy as it gets:
var doc = new XmlDocument();
var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("project.example.xml");
doc.Load(stream);
However, when you have that same file linked to it’s DTD (Document Type Definition) things get ugly. Only thing that we get with our function is FileNotFoundException
with Could not find file 'C:\somepath\example.dtd'
. Things do not change even when DTD is embedded next to XML. As you can see from exception, our XmlDocument
is stubbornly reading DTD from disk instead from resources.
Easiest solution is to delete referring DOCTYPE
from XML document. However, that is double-edged sword since that also means that you lose quite a good helping hand in detecting early errors. I will not even go into how (properly created) DTD can be huge help with intellisense during XML editing.
Better solution would be to use XmlDocument
’s existing XmlResolver
property. XmlResolver
is what actually handles from where each stream is read. If we inherit that class we can make critical change to read DTD from resource stream instead from file system. And it all fits in single overridden function:
public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) {
var fileName = absoluteUri.Segments[absoluteUri.Segments.Length - 1];
var resourceName = "project." + fileName;
var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
return resourceStream;
}
Code will just check which file name is required and return matching embedded resource stream. Loading XML stays almost same:
var doc = new XmlDocument();
doc.XmlResolver = MyResolver();
var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("project.example.xml");
doc.Load(stream);
P.S. Do notice that above are excerpts from little bit bigger code. Full sample is available for download.