Reputation: 4610
I have a controller that has a few actions on it, some of them may have a custom attribute. I want to use linq to select some data into an anonymous type for each action on the controller e.g.
Controller1
Action1
[MyAttribute("Con1Action2)"]
Action2
[MyAttribute("Con1Action3")]
Action3
Controller2
Action1
[MyAttribute("Con2Action2)"]
Action2
[MyAttribute("Con2Action3")]
Action3
I want the following returned:
NameSpace = "someText", Controller = "Controller1", ActionName = "Con1Action2",
NameSpace = "someText", Controller = "Controller1", ActionName = "Con1Action3",
NameSpace = "someText", Controller = "Controller2", ActionName = "Con2Action2",
NameSpace = "someText", Controller = "Controller2", ActionName = "Con2Action3"
I'm struggling with a SelectMany for each action:
var controllers= myControllerList
.Where(type =>type.Namespace.StartsWith("X.") &&
type.GetMethods().Any(m => m.GetCustomAttributes(typeof(MyAttribute)).Any()))
.SelectMany(type =>
{
var actionNames = type.GetMethods().Where(m => m.GetCustomAttributes(typeof(MyAttribute)).Any()).ToList();
var actionName = (MyAttribute)actionNames[0].GetCustomAttribute(typeof(MyAttribute));
return new
{
Namespace = GetPath(type.Namespace),
ActionName= actionName.Name,
Controller = type.Name.Remove(2);
};
}).ToList();
I'm getting an error on the SelectMany- the type arguments for the method...cannot be inferred from the usage.
Upvotes: 4
Views: 4153
Reputation: 4886
"SelectMany Projects each element of a sequence to an IEnumerable and flattens the resulting sequences into one sequence." Source: https://msdn.microsoft.com/en-us/library/system.linq.enumerable.selectmany(v=vs.100).aspx
You're returning one element:
return new
{
Namespace = GetPath(type.Namespace),
ActionName= actionName.Name,
Controller = type.Name.Remove(2);
};
You can just use .Select
You'd use SelectMany if you wanted to get a flattened list from a property that is a colection, for example:
projects.SelectMany(p => p.Technologies).ToList()
Assuming a project has a property that is a collection named Technologies, that query would return all the technologies from all projects.
In your case because you want the list of actions per controller, you have to return a list of action information per controller:
var controllers= myControllerList
.Where(type =>type.Namespace.StartsWith("X.") &&
type.GetMethods().Any(m => m.GetCustomAttributes(typeof(MyAttribute)).Any()))
.SelectMany(type =>
{
var actionNames = type.GetMethods().Where(m => m.GetCustomAttributes(typeof(MyAttribute)).Any()).ToList();
return actionNames.Select(action => {
var actionName = (MyAttribute)action.GetCustomAttribute(typeof(MyAttribute));
return new
{
Namespace = GetPath(type.Namespace),
ActionName= actionName.Name,
Controller = type.Name.Remove(2);
});
});
}).ToList();
Upvotes: 3
Reputation: 12544
As mentioned, the problem is that a single object is returned new {}
where selectmany expects a collection.
In your current setup , you could do that by returning a select on the actions inside your selectmany (e.g. return actionNames.Select(a=> new {...
), so that an enumerable is returned containing each separate attribute, but in this setup the attributes would be queried multiple times. (once for the check, once for the implementation)
Just a suggestion (And air coded, so might have syntax errors), but if you query all methods (selectmany), buffer the attribute per methodinfo, then check where filled, you only search for the attribute once:
var controllers= myControllerList
.Where(type =>type.Namespace.StartsWith("X."))
.SelectMany(type => type.GetMethods())
.Select(m => new {
Type = m.DeclaringType,
Att = m.GetCustomAttribute(typeof(MyAttribute)) as MyAttribute
})
.Where(t=>t.Att!=null)
.Select(t=> new
{
Namespace = GetPath(t.Type.Namespace),
ActionName= t.Att.Name,
Controller = t.Type.Name.Remove(2);
}
).ToList();
Upvotes: 0
Reputation: 157048
I presume the problem is SelectMany
. It expects the implementation on IEnumerable<T>
on the type you are calling, in this case Type
. But Type
does not implement IEnumerable<T>
. Hence it will error out.
Replace SelectMany
by Select
and you will be fine.
Upvotes: 1