I recently needed to workaround a bug caused by failing to load an embedded resource due to casing. Below is a simplified example of the original problem:
```c#
static void Foo(string name)
{
// name here is "Foo.xml".
// The assembly contains the embedded file "foo.xml".
var assembly = Assembly.GetExecutingAssembly();
using (var stream = assembly.GetManifestResourceStream(name))
{
// stream is null because the name is case-sensitive.
var doc = XDocument.Load(stream);
// ...
}
}
As indicated by the comments, the embedded resource was "foo.xml", but the code was trying to load "Foo.xml". As properly documented, Assembly.GetManifestResourceStream is case-sensitive. In the actual program, the argument "Foo.xml" was coming from an external source so could not be changed easily. Also, the embedded resource was being used by other code as "foo.xml", so it was not desirable to rename either.
As a workaround, I retrieved all of the embedded resource names and looped through each to compare without case-sensitivity. Here is the helper method:
```c#
static string ToCaseSensitiveResourceName(string caseInsensitiveName)
{
// caseInsensitiveName is "Foo.xml".
// The assembly contains the embedded file 'foo.xml'.
var assembly = Assembly.GetExecutingAssembly();
var names = assembly.GetManifestResourceNames();
foreach (var name in names)
{
if (String.Equals(name, caseInsensitiveName, StringComparison.InvariantCultureIgnoreCase))
{
return name;
}
}
return null;
}
Combining these two together provided a workaround for the issue.
c#
static void Bar(string name)
{
// name here is "Foo.xml".
// The assembly contains the embedded file "foo.xml".
var assembly = Assembly.GetExecutingAssembly();
var realName = ToCaseSensitiveResourceName(name);
using (var stream = assembly.GetManifestResourceStream(realName))
{
// The embedded resource is opened for streaming.
var doc = XDocument.Load(stream);
// ...
}
}
However, it would have been much simpler if the API provided Assembly.GetManifestResourceStream(string, StringComparison). Hence, this proposed API.
[EDIT] Add C# syntax highlight by @karelz
@danmosemsft Could you please label this as api-suggestion? Thanks.
@karelz Could you please help categorize this? Thanks.
This seems to be a rare case and behaves as documented. The problem is primarily in the application -
one of its components didn't play by the rules and used wrong casing of a resource.
I don't think we should take such API, unless there is strong evidence this is more common and/or the reason is better than workaround for bugs in application code.
@bendono we continuously label issues - we were just OOF on Thanksgiving week ;)
I agree with @karelz here. this can cause a problem too for the app which embeds resources with the same name and different casing.
<ItemGroup>
<EmbeddedResource Include="debug.txt" >
<LogicalName>FILE1</LogicalName>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="commits.txt" >
<LogicalName>file1</LogicalName>
</EmbeddedResource>
</ItemGroup>
I think case sensitivity is required to enable this scenario which is supporting embedding resources with the same name and different casing.
@karelz While it is true that this proposal originated as a workaround for a faulty application, upon talking with the original developer, the assumption and desired behavior was for case-insensitive lookup. He apparently did not check the documentation, but the desired behavior is certainly something that can more easily be supported.
@tarekgh I also agree with you in that case sensitivity is required to enable the scenario that you created. And the existing API supports that just fine. Though it is also telling that you needed to resort to defining the LogicalName to force names differing only in case sensitivity. On a Windows box, such physical file names will be rejected, though I think it is permitted on Linux. Regardless, the existing API supports this scenario just fine.
However, in our case, we desire case-insensitive resource names. Adding an overload with a StringComparison argument can allow the developer to choose as appropriate for their project. The default will of course not change: case-sensitive lookup.
On a Windows box, such physical file names will be rejected,
This can be permitted on Windows too if the files on different folders. nothing prevents you to embed the resources from different places.
However, in our case, we desire case-insensitive resource names. Adding an overload with a StringComparison argument can allow the developer to choose as appropriate for their project. The default will of course not change: case-sensitive lookup.
I understand this. my point is if there is code asking for the resources with case insensitive and then there are 2 resources with the same name and different casing, we may be returning the undesired resources at that time. I think your workaround is acceptable in your case but we don't have to generalize it as it is not a popular scenario and there is already a workaround for it.
Agreed with @tarekgh, unless there is evidence this is more common scenario (so far we saw 1 hit), we should not add new API. Living with a workaround which is easy to discover is ok for a few occurrences in the world.
Adding new API would cause confusion to developers - they would have to choose which overload to use and would be curious why the other is there when the resources are supposed to be case-sensitive.
Closing ...
I agree also. @bendono I hope this doesn't deter you from opening other issues/suggestions. As @karelz mentioned, the bar for new API is high because the cost goes long beyond the implementation/testing cost.
Understood. Thank you all for reviewing it.