-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Throw informative exception when no name in services element #22
Conversation
First, thanks for the PR! I do appreciate the help. Here's my stumbling block, and maybe it's not a big deal, but is something to consider: while this fixes the very specific issue marked in #18 (the Here's the documentation showing some configuration snippets. Making a more complete example with more ordinal collections out of the snippets there, you'll get something like... {
"components": [{
"type": "Autofac.Example.Calculator.Division.Divide, Autofac.Example.Calculator.Division",
"services": [{
"type": "Autofac.Example.Calculator.Api.IOperation"
}],
"metadata": [{
"key": "answer",
"value": 42,
"type": "System.Int32, mscorlib"
}]
}],
"modules": [{
"type": "Autofac.Example.Calculator.OperationModule, Autofac.Example.Calculator"
}]
} Every place you see an array is a place in XML that will need a <?xml version="1.0" encoding="utf-8" ?>
<autofac>
<!-- name on component -->
<components name="0">
<type>Autofac.Example.Calculator.Division.Divide, Autofac.Example.Calculator.Division</type>
<!-- name on services -->
<services name="0" type="Autofac.Example.Calculator.Api.IOperation" />
<!-- name on metadata -->
<metadata name="0" key="answer" value="42" type="System.Int32, mscorlib" />
</components>
<!-- name on modules -->
<modules name="0">
<type>Autofac.Example.Calculator.OperationModule, Autofac.Example.Calculator</type>
</modules>
</autofac> I didn't bother putting in properties or parameters that have list values, but you'll see an example of that in the unit tests. I wonder if a solution to catch this would be to add some sort of integer parsing in the foreach loops where we iterate through ordinal collections, like: foreach (var serviceDefinition in component.GetSection("services").GetChildren())
{
// Make sure there's a name that can be parsed into an integer to ensure it's ordinal?
if(!Int32.TryParse(serviceDefinition.Key, out var name))
{
throw new InvalidOperationException("Name in ordinal collection has to be an integer.");
}
// "name" is a special reserved key in the XML configuration source
// that enables ordinal collections. To support both JSON and XML
// sources, we can't use "name" as the keyed service identifier;
// instead, it must be "key."
var serviceType = serviceDefinition.GetType("type", defaultAssembly);
string serviceKey = serviceDefinition["key"];
if (!string.IsNullOrEmpty(serviceKey))
{
yield return new KeyedService(serviceKey, serviceType);
}
else
{
yield return new TypedService(serviceType);
}
} I'm not glued to that logic or exception message, and didn't even try to compile it, but it'd have to happen on any place where we expect an ordinal collection (anywhere it'd be an array in JSON instead of a dictionary). I'm not entirely comfortable with putting only XML guidance in there, either, because, again, we don't know if it's XML, INI, JSON, environment variables, command line variables, or what. It's a benefit and a drawback. Can we make it more all-inclusive? Maybe with a link to the documentation at http://autofac.readthedocs.io/en/latest/configuration/xml.html and/or some docs on how to use Microsoft.Extensions.Configuration. Yeah, the URL there is to Maybe a message like... `The '{0}' collection should be ordinal (like an array) with items that have numeric names to indicate the index in the collection. An item under '{1}' didn't have a numeric name so couldn't be parsed. Check http://autofac.readthedocs.io/en/latest/configuration/xml.html for configuration examples.' Format args could be the name of the thing being parsed ( Of course, here's the real kicker if we step back from all this: Autofac really doesn't care about the ordinal nature of the collection. Look at the example in this Microsoft doc in the XML section. As long as the names are unique it doesn't matter what they are. We don't even use them, we just iterate through them and ignore the actual names. If we did use the names, yeah, they'd have to be numbers because that's how JSON and other parsing works to map arrays to a flat key/value set. But we don't care internally to our config consumer whether it's an array or what. If someone provided flat config like...
That's totally legit. Which is kind of cool, because then it leaves the extensibility of the configuration system pretty open. Maybe you want to use XML config so you can control that little piece there and do something with it. Or maybe you need it strongly named so in XML config that gets deployed with your app you can put one thing in and in environment you can easily override it at runtime without re-deploying. We don't even care if you jam extra attributes and things in there. Want to store a bunch of That's why using the abstraction is really neat... but it's new, so there's some ramp-up for folks and it looks like it's an Autofac problem when it's really malformed config. And that's why I didn't really solve this and was trying to think of ways to catch malformed config while still staying at the abstraction level, not diving into specifics of XML, JSON, or whatever. Am I off in the weeds? |
Thanks for the thoughtful response, @tillig. I'm making more work than I'm saving! Responses, not in the order the original points appeared.
Ah, yes. I was intentionally targeting the mentioned scope, for fear of breaking things I don't understand. Happy to do otherwise.
Ah, yes. At the time I thought the options were XML or JSON, and that JSON would not suffer from the problem, so considered it harmless.
Which was part of the reasoning behind my "just make it work" approach. If Autofac doesn't need them, why not work without them?
Yay! The restriction is relaxed. We can just check for presence.
Boo! It got harder again.
I believe that Either way, I'll start reworking and see if I can come up with something more palatable. |
Possible confusion:
When iterating through the children of the The XML parser specifically uses the "name" property to disambiguate elements of the same type (like two The whole point of any of the parsers is to flatten it all out to key/value pairs. There are some peculiarities around each one. The XML parser is particularly painful. They really want people to use JSON since it's really just easier. |
Yeah, sorry. I was wrong. Also off working on stuff and discovering this very thing. Honestly, I don't understand how the When you omit Ah well, I will soldier on. In (my) morning. It's not super late here, but I'm working on about 5 hours of sleep. I'm heading up the wooden stairs to Bedfordshire. Sorry for my continued ineptitude. 😁 |
Hey, don't sell yourself short. And don't let it sound like I don't appreciate your help. I do, very much. My knowledge is mostly through experience. Thinking I should write a blog article or something. I had a lot of discussions about this and other concerns in .NET Core with that team in early days both via work and through Autofac so I was in early on with this ordinal collection handling thing. (It was a late add before release.) Anyway, keep hitting breakpoints with the debugger and using Quick Watch. In this particular instance, it's one of the most educational things. ;) |
Oh, sorry. I didn't mean to sound like I thought you were ungrateful. You've not seemed so in any way.
Will do. For some reason up until yesterday, I was unable to even debug through. TestDriven.NET and ReSharper are still not my friends. Resorted to VS Test Runner, but at least it's functioning. Prior to that I was "debugging by printf". And by "printf", I mean "throwing weird exceptions with informative messages"! |
Repushed. I generally took your work and pasted it in. The exception message is a little different (and ever so slightly repetitive), mostly due to the lack of As always, I'm happy to make any changes. |
Works for me! Only suggestion I have is to move the string from the exception message into a resx file. We have one in |
Drat! I'd seen it and initially inlined the string for expediency and… forgot to go back. |
Whichever is easiest for you. Thanks! |
Thanks, @tillig. I decided that easiest was "both". |
Looks super. Thanks! Merging now! |
Thanks, @tillig. For the merge and your patience. I had a good time. |
Fixes #18.