-
Notifications
You must be signed in to change notification settings - Fork 458
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
Deprecating Resolve(Type, IDictionary) in favor of IReadOnlyDictionary<string, object> #346
Comments
What about types that don't implement A We can't use two overloads because |
Right, we can't overload. I was figuring that |
Something like C5 collections still doesn't look like it implements I'll assign it to v5. |
I guess the real driver for this is that I had to write an Another question though is net40 support and lower, so maybe this will never be possible. |
Windsor dropped support for versions before .NET 4.5 in Windsor 4, only Castle Core supports versions before .NET 4.5. |
@jnm2 - I have added this to the v5 discussion and placed a bullet point in the description. I have a question though, how deep would we like this to go? I am assuming we are talking about the resolve method in the kernel here. The problem I can see right now, is this eventually get's passed down to a resolve all method, ultimately leading to the newing up of a CreationContext. It then becomes part of the publicly accessible property All good so far but ... if this is immutable however ... you might end up with a blood bath. My quick test here, was to remove the indexer setter on Arguments in favour of throwing an exception to see how bad this really would be and to re-run the tests. I ended up with: Would just like to know thoughts around scoping the problem. Happy to provide more info to clarify. |
I think the CreationContext should be immutable period. The fact that it is not presents a big problem with resolution determinism in the container as it stands today. |
One thing you could do to scope the problem is copy on first write so that you aren't allocating a new hashtable unnecessarily but you also aren't modifying the dictionary someone passed into a Resolve method. |
Exactly! We want immutability. This problem tends to manifest itself heavily within the CreationContext where that dictionary is mutable throughout the resolve process(in fact the CreationContext get's recreated sometimes, god!). I was just pondering how far we would like to take this. You are suggesting an "outside-in" safe guard via the public API. This is fairly easy to do and it does not stop anyone from rewriting this dictionary with a single linq statement in your code base. I guess what I am asking you to consider is that this problem endures all the way into the bowls of the MicroKernel. What are your thoughts on trying to fix that? I think you raised a big problem in the first instance to do with resolution determinism here. In some cases(esp with open generic implementations) this is not always possible but what about fixing the stuff we can fix? |
That's something you'll have to decide as maintainers as you go along, but an iterative approach might be better than a boil-the-ocean approach. For the moment I'm only concerned with whether I can pass an instance known statically as only |
Fair enough, I do believe the ocean is already boiling. We are just hacking below the steam.
I guess what I am trying to tell you here, is it will get mutated anyway. Windsor expects this. |
@Fir3pho3nixx I mean I don't care if a copy is mutated by Windsor, but it shouldn't be mutating the dictionary instance I pass to Resolve because it may collide with my logic between calls. |
@Fir3pho3nixx thanks for looking into this more, I had just thought we'd do as @jnm2 suggested and implement it at the API surface level but this is something we'll definitely need to look at more. If Windsor does mutate the user's dictionary then we could probably implement copy-on-write using the cheshire cat pattern or something similar so the Windsor internals are none the wiser that they were even passed a readonly dictionary. However, we've already got copy-on-write with the overload that takes an anonymous type which Windsor obviously can't modify, Windsor uses the
|
@jnm2 - Totally got you on that, don't worry I really did not take offense at all. I am just looking at this from an 'internals' point of view. No harm done at all. 👍 @jonorossi - Loving that name "Cheshire the cat" pattern! I did come across the method you mentioned but I think my brain was in immutability mode so I did not pay anymore attention. I have left this in the V5 deprecation issue and wont be taking it out. We are still good to go. |
I also wanted to add this for reference: https://github.com/dotnet/corefx/issues/17917#issuecomment-298706121 |
@Fir3pho3nixx We're good, I see no sign of offense either direction. =) |
Good to know they are looking at making |
I know we have implemented this, but stuff like this spooks the crap out of me. Happy to let it go for now. I have a stinking feeling we are not done here! |
Re-opening this while we discuss this further on #420. |
With 8 In #420 it sounds like you want to do the following (overloads omitted for brevity):
If you remove the anonymous type one that you wanted to catch Looking at Autofac they've got a |
What we are saying is we should deprecate support for data structures with mutable API’s until we can address the underlying mutation problem. This means not supporting hashtable and dictionary on Resolve at the WindsorContainer level and obsolescence for the doppelgänger API’s in the MicroKernel. For now of course. We need a new issue to track why the mutations are happening in the first place. Your idea of introducing a separate type is a great one. I think you proposed using Arguments for this purpose and then doing implicit conversions. The problem I have with this is that although we have reduced the surface level API bloat here, Arguments might end up becoming this weird esoteric thing that does too much type conversion. Happy to clarify anything I have said as I typed this out on my mobile while commuting. |
If this is the problem you want to solve the proposed solution does way more than this. You are already proposing to clone the map of whatever is passed to it irrespective of the proposed public API change, cloning would solve this. The proposed API change itself without the cloning does not solve this problem because there is no semantic difference between I understand that @jnm2 wants to pass in |
Correct. However we are on the eve of releasing V5. Worth discussing right? Let's not forget, the PR descended into chaos because I tried to introduce a Resolve(IReadOnlyDictionary) overload and removed the Resolve(IDictionary) overload. This played up hell with the Resolve(object) API. Everything that was IDictionary fell back onto Resolve(object) for all the tests and caused an absolute bloodbath when running them. I decided then that having this
I can happily revert to the If we are all happy with this, I can amend my PR fairly soon. |
Definitely, it was just that the justification (mutation) didn't apply to breaking change (public API change).
Yep 😢
Agreed, see my comments: #420 (comment)
Are we talking about
This issue is titled in relation to adding IReadOnlyDictionary, I'd love a new issue about the dictionary mutation problem with some sample code illustrating the problem. I think the time is probably now to raise that issue about Resolve overloads accepting |
Would love to see the use case for why |
Deleted. Please see https://github.com/castleproject/Windsor/pull/420/files#diff-16ccc2c284d39fff9d45a051973cd1c8L354 |
The extension Arguments.FromReadOnlyDictionary now calls a shallow copy constructor here. I think this coupled with making Arguments readonly for any side effects when it comes to calling Add, Clear or Remove by setting isReadOnly here gets you what you need. It is also a tidy way of getting rid of bad methods as it turns out :) |
As it turns out we are talking about methods that apparently enforce mutation semantics privately. You already had a sense of this when you mentioned Either way, was a great win. Thanks for helping out. |
…f `WindsorContainer.Resolve(Castle.MicroKernel.Arguments)` along with new Arguments class factory methods including support for IReadOnlyDictionary (@Fir3pho3nixx, #346) Additional breaking changes: - Changed CreationContext.AdditionalArguments to use Arguments instead of IDictionary (@Fir3pho3nixx, #346) - Removed ComponentDependencyRegistrationExtensions(Insert, InsertAnonymous, InsertTyped, InsertTypedCollection) and created Insert, InsertNamed, InsertProperties and InsertTyped Arguments instance methods (@Fir3pho3nixx, #346) - ComponentRegistration.DependsOn and ComponentRegistration.DynamicParameters changed to use Arguments via DynamicParametersDelegate (@Fir3pho3nixx, #346) - Removed IArgumentsComparer[] from Arguments (@Fir3pho3nixx, #346)
Right now it's a bit of a pain to use the Resolve method because what I'll have in hand is an
IReadOnlyDictionary<string, object>
, usually from a method parameter.Please consider adding this method and removing the nongeneric overload.
The nongeneric one would be a problem. So long as it's there and you pass it a type like
Dictionary<string, object>
which implements both interfaces, which all of the BCL dictionaries do, you'll have to disambiguate the overload by explicitly casting to one or the other interface type.This would be a breaking change for people still using non-generic dictionaries like
Hashtable
, and there may be a few, so what if you removed it in v5?The text was updated successfully, but these errors were encountered: