Reputation: 337
I Dare say that's the most complicated title I've ever come up with. I'm just not sure how to better explain it, short of this example:
I have a type. Under this type, I have assorted methods, some of which have one or more "OnAttribute" attributes attached to them, each defining an event name this method needs to fire on. I'm currently getting a Dictionary of Methods to their relevant lists of event names with this query:
var methods = this.GetType().GetMethods()
.Where(m => Attribute.IsDefined(m, typeof(OnAttribute)))
.ToDictionary(m => m,
m => m.GetCustomAttributes(typeof(OnAttribute), true)
.Select(a => ((OnAttribute)a).EventName)); //OnAttribute defines an EventName field
The resulting type is IEnumerable<IGrouping<System.Reflection.MethodInfo,IEnumerable<string>>>
. However, in order to sign the methods up for their requested events, I need to map each event name to any and all methods asking to execute on them. In other words, I need the previously mentioned type mapped to IEnumerable<IGrouping<string,IEnumerable<System.Reflection.MethodInfo>>>
.
I need this:
[On("Event_1")]
[On("Event_2")]
[On("Event_4")]
void Method_1()
{
...
}
[On("Event_1")]
void Method_2()
{
...
}
[On("Event_2")]
[On("Event_3")]
void Method_3()
{
...
}
Which, using my current code, would map to this:
Method_1 => ["Event_1","Event_2","Event_4"]
Method_2 => ["Event_1"]
Method_3 => ["Event_2","Event_3"]
Instead mapped to this:
"Event_1" => [Method_1,Method_2]
"Event_2" => [Method_1,Method_3]
"Event_3" => [Method_2]
"Event_4" => [Method_1]
I'm sure there's some better terminology out there somewhere to describe this; I just don't know what it is.
I attempted using a foreach loop to create a dictionary the ugly way, but as I said, that's really ugly. I also attempted to use SelectMany() with some anonymous types to flatten the Dictionary into a list, and then use GroupBy to re-group that list into my desired dictionary, but that seemed inefficient and messy. I'd like to know the (best) LINQ way of doing this.
I hope that made at least a little bit of sense.
Thanks for any help!
Upvotes: 0
Views: 97
Reputation: 129777
To get the reverse mapping from your current dictionary of methods, you can do this:
var reverseMapping = methods
.SelectMany(kvp => kvp.Value, (kvp, evt) => new { Event = evt, Method = kvp.Key })
.GroupBy(a => a.Event, a => a.Method);
Then, if you want to dump them out:
foreach (var grouping in reverseMapping.OrderBy(g => g.Key))
{
Console.WriteLine(string.Format("{0} => {1}",
grouping.Key, string.Join(",", grouping.Select(m => m.Name))));
}
Fiddle: https://dotnetfiddle.net/SdE7LD
Upvotes: 0
Reputation: 2361
You have to use a combination of SelectMany
and GroupBy
to get the required result.
Here is a solution that seems to be working:
var methods = this.GetType().GetMethods()
.Where(m => Attribute.IsDefined(m, typeof(OnAttribute)))
.SelectMany(m => m.GetCustomAttributes(typeof(OnAttribute), true)
.Select(a => new { EventName = ((OnAttribute)a).EventName, MethodInfo = m }))
.GroupBy(g => g.EventName, g => g.MethodInfo);
In this solution, the collection is first flattened to get an intermediate collection of anonymous types containing EventName
and the MethodInfo
. This intermediate collection of anonymous types if then Grouped by EventName
to get groups of Methods by EventName
.
Upvotes: 2