LINQ to dictionary with multiple keys for a single value - c#

Consider the following code:
public KeyAttribute : Attribute{
public string Value;
public KeyAttribute(string value){
Value = value;
}
}
[Key("A")]
[Key("AB")]
public class A : IClass
{
public string FieldA {get;set;}
}
[Key("B")]
public class B : IClass
{
public string FieldB {get;set;}
}
So I have to through all the implementations of IClass interface, and I've got to construct a dictionary Dictionary<string, ConstructorInfo> where the keys are the Value properties of KeyAttribute, and the values are the corresponding ConstructorInfo.
Notice that there might be several KeyAttribute on a single class, so for that case, there should be the corresponding amount of entries in the dictionary.
For the current example, the desired outcome is:
key | value
--------+-------------------
"A" | ConstructorInfo A
"AB" | ConstructorInfo A
"B" | ConstructorInfo B
At first I wrote this:
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IClass).IsAssignableFrom(t))
.ToDictionary(t =>
{
var key = (KeyAttribute) t.GetCustomAttributes(typeof(KeyAttribute), true).First();
return key.Value;
}, t => t.GetConstructors(BindingFlags.Public).First());
But as you can see, the code above does not handle the situation with several attributes.
So I did the following, but it's obviously wrong and I'm not sure how to correct it.
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IClass).IsAssignableFrom(t))
.ToDictionary(t =>
{
return t.GetCustomAttributes(typeof(KeyAttribute), true).Select(a => ((KeyAttribute)a).Value);
}, t => t.GetConstructors(BindingFlags.Public).First());
I know I can do that without LINQ like that:
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IClass).IsAssignableFrom(t));
var dict = new Dictionary<string, ConstructorInfo>();
foreach (var type in types)
{
var keys = type.GetCustomAttributes(typeof(KeyAttribute), true).Cast<KeyAttribute>().Select(a => a.Value);
var ctorInfo = type.GetConstructors(BindingFlags.Public).First();
foreach (var key in keys)
{
dict.Add(key, ctorInfo);
}
}
return dict;
But I'd rather stick to LINQ, if it is possible.
Sorry about all these somewhat misleading details about attributes and all that, while it is a question about LINQ, but I couldn't think of any other vivid example.

This should work:
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IClass).IsAssignableFrom(t))
.SelectMany(t => t.GetCustomAttributes(typeof(KeyAttribute), true)
.Select(a => new
{
constInfo = t.GetConstructors(BindingFlags.Public).First(),
attrVal = ((KeyAttribute)a).Value
}))
.ToDictionary(entry => entry.attrVal, entry => entry.constInfo);

Use SelectMany for key attributes
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IClass).IsAssignableFrom(t))
.SelectMany(t =>
{
return t.GetCustomAttributes<KeyAttribute>()
.Select(ka => new
{
Key = ka,
Ctor = t.GetConstructors(BindingFlags.Public).First()
});
})
.ToDictionary(t => t.Key, t.Ctor);

Related

How to get property value from IEnumerable<object>

I have a method which returns IEnumerable
public static IEnumerable<object> GetProps<T>(T obj)
{
var result = obj.GetType().GetProperties()
.Select(x => new { property = x.Name, value = x.GetValue(obj) })
.Where(x => x.value == null)
.ToList();
return result;
}
Above code will return result as [{"property":"YearOfBirth","value":null}]
I;m now trying to get property valueYearOfBirth from the returned result.
Can someone please suggest/help ?
The type of:
new { property = x.Name, value = x.GetValue(obj) }
is an anonymous type and you can't access fields or properties of that anonymous type outside of the function where it was defined, without using reflection. Here's how you would access its properties using reflection:
foreach (object prop in GetProps(obj))
{
string propertyName = prop.GetType().GetProperty("property").GetValue(prop);
object propertyValue = prop.GetType().GetProperty("value").GetValue(prop);
}
But that's not really the best solution. You don't care about the property value, since you're just returning ones where it's null. So a better solution is IEnumerable<string>:
public static IEnumerable<string> GetProps<T>(T obj)
{
var result = obj.GetType().GetProperties()
.Select(x => new { property = x.Name, value = x.GetValue(obj) })
.Where(x => x.value == null)
.Select(x => x.property)
.ToList();
return result;
}
If you really want to return the property name with its value, I suggest using ValueTuple syntax instead of anonymous types, which will let you access the property and value fields of the ValueTuple (requires C# 7):
public static IEnumerable<(string property, object value)> GetProps<T>(T obj)
{
var result = obj.GetType().GetProperties()
.Select(x => ( x.Name, x.GetValue(obj) ) })
.Where(x => x.Item2 == null)
.ToList();
return result;
}
var yearOfBirth = GetProps(someObject)
.FirstOrDefault(x => x.property == "YearOfBirth")?.value;
Something like that.
You could alternatively just do:
dynamic someObjectDynamic = someObject;
var yearOfBirth = someObjectDynamic.YearOfBirth;

Autofac - get all instances of classes whose interface implements specific interface [duplicate]

Consider the following
builder.Register(c => new A());
builder.Register(c => new B());
builder.Register(c => new C());
B and C are both ISomeInterface.
I would now like to get an IEnumerable of all registered objects that implement ISomeInterface.
How can I accomplish this in Autofac?
Just tried this, works and does not depend on lifetime context:
Enumerate types using Activator instead
var types = con.ComponentRegistry.Registrations
.Where(r => typeof(ISomeInterface).IsAssignableFrom(r.Activator.LimitType))
.Select(r => r.Activator.LimitType);
Then to resolve:
IEnumerable<ISomeInterface> lst = types.Select(t => con.Resolve(t) as ISomeInterface);
If you have
container.Register(c => new A()).As<ISomeInterface>();
container.Register(c => new B()).As<ISomeInterface>();
Then when you do
var classes = container.Resolve<IEnumerable<ISomeInterface>>();
You will get a variable that is a list of ISomeInterface, containing A and B
Here is how I did it.
var l = Container.ComponentRegistry.Registrations
.SelectMany(x => x.Services)
.OfType<IServiceWithType>()
.Where(x =>
x.ServiceType.GetInterface(typeof(ISomeInterface).Name) != null)
.Select(c => (ISomeInterface) c.ServiceType);
I was looking for a similar solution for registrations done as follows:
builder.RegisterType<Bamboo>().As<IExoticTree>();
// and/or
builder.RegisterType<SolidOak>().Keyed<IMountainTree>(key);
where IExoticTree and IMountainTree inherit from a common ITree interface.
With those, the service type (e.g. registered interface) is different from the LimitType and hence, the proposed solution is not applicable.
I got inspired by the accepted solution to manage these as well with the following code:
IEnumerable<ITree> instances = scope.ComponentRegistry.Registrations
.Where(r => typeof(ITree).IsAssignableFrom(r.Activator.LimitType))
.Select(r => r.Services.First())
.Select(s => scope.ResolveService(s) as ITree)
.Distinct();
Hope it helps someone ^^
I needed to resolve based on some context. So slight variation...
builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(ISomeStrategy)))
.Where(t => typeof(ISomeStrategy).IsAssignableFrom(t))
.AsSelf();
builder.Register<Func<SomeEnum, ISomeStrategy>>(c =>
{
var types = c.ComponentRegistry.Registrations
.Where(r => typeof(ISomeStrategy).IsAssignableFrom(r.Activator.LimitType))
.Select(r => r.Activator.LimitType);
ISomeStrategy[] lst = types.Select(t => c.Resolve(t) as ISomeStrategy).ToArray();
return (someEnum) =>
{
return lst.FirstOrDefault(x => x.CanProcess(someEnum));
};
});
Then for your class using the strategy
public SomeProvider(Func<SomeType, ISomeStrategy> someFactory)
{
_someFactory = someFactory;
}
public void DoSomething(SomeType someType)
{
var strategy = _someFactory(someType);
strategy.DoIt();
}
Clarification for any one searching this with regards to Inherited Class
Ex :
public static IEnumerable<Type> GetTypesInherit<T>(this Assembly assembly)
{
var types = assembly.GetTypes()
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(T)))
.ToList();
return types;
}
Installer :
public static IContainer Install()
{
var builder = new ContainerBuilder();
var executingAssembly = Assembly.GetExecutingAssembly();
var testScreens = executingAssembly.GetTypesInherit<TestScreen>();
foreach (var testScreen in testScreens)
{
builder.RegisterType(testScreen).As<TestScreen>();
}
var container = builder.Build();
return container;
}
Usage (Implicit) :
public class MainViewModel(IEnumerable<TestScreen> testScreens)
{}

How do I Import types defined in a C# dictionary?

I'm struggling to figure out how to generate my imports line when the property on an object is a dictionary and its value being a custom type.
My DTO/C# class is defined below. The Imports function in my .tst file is not picking up that it needs to import the Status type, similar to how it imported ProjectDTO.
Is there a way to get to the type of the dictionary's values within the .tst file?
C# file
[TypeScript]
public class ActiveProjectsViewModel
{
public List<ProjectDTO> Projects { get; set; }
public Dictionary<int,Status> Statuses { get; set; }
public bool EditModeAvailable { get; set; }
}
.tst file
${
string Imports(Class c)
{
var props = c.Properties.Where(p=>!p.Attributes.Any(a => String.Equals(a.name, "TypeScriptIgnore", StringComparison.OrdinalIgnoreCase)));
IEnumerable<Type> types = props
.Select(p => p.Type)
.Where(t => !t.IsPrimitive || t.IsEnum)
.Select(t => t.IsGeneric ? t.TypeArguments.First() : t)
.Where(t => !t.Attributes.Any(a => String.Equals(a.name, "TypeScriptIgnore", StringComparison.OrdinalIgnoreCase)))
.Distinct();
return string.Join(Environment.NewLine, types.Select(t => $"import {{ {t.Name} }} from './{t.Name}';").Distinct());
}
}
$Classes()[
$Imports
export class $Name {
$Properties($PropertyIsNotIgnored)[
public $name: $Type = $Type[$Default];]
$BaseClass[$Properties($PropertyIsNotIgnored)[
public $name: $Type = $Type[$Default];]]
}]
$Enums(*)[
export enum $Name {
$Values[
$Name = $Value][,]
}]
Produces this file:
import { ProjectDTO } from './ProjectDTO';
export class ActiveProjectsViewModel {
public projects: ProjectDTO[] = [];
public statuses: { [key: number]: Status; } = {};
public editModeAvailable: boolean = false;
}
From the creator of Typewriter posted here: https://github.com/frhagn/Typewriter/issues/282
it looks like the type defined in the dictionary can be derived using the approach below.
The SelectMany line was the key one added.
string Imports(Class c)
{
var props = c.Properties.Where(p => !p.Attributes.Any(a => String.Equals(a.name, "TypeScriptIgnore", StringComparison.OrdinalIgnoreCase)));
IEnumerable<Type> types = props
.Select(p => p.Type)
.SelectMany(t => t.IsGeneric ? t.TypeArguments : new[] { t } as IEnumerable<Type>)
.Where(t => !t.IsPrimitive || t.IsEnum)
.Where(t => !t.Attributes.Any(a => String.Equals(a.name, "TypeScriptIgnore", StringComparison.OrdinalIgnoreCase)))
.Distinct();
return string.Join(Environment.NewLine, types.Select(t => $"import {{ {t.Name} }} from './{t.Name}';").Distinct());
}

Getting All Controllers and Actions names in C#

Is it possible to list the names of all controllers and their actions programmatically?
I want to implement database driven security for each controller and action. As a developer, I know all controllers and actions and can add them to a database table, but is there any way to add them automatically?
The following will extract controllers, actions, attributes and return types:
Assembly asm = Assembly.GetAssembly(typeof(MyWebDll.MvcApplication));
var controlleractionlist = asm.GetTypes()
.Where(type=> typeof(System.Web.Mvc.Controller).IsAssignableFrom(type))
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public))
.Where(m => !m.GetCustomAttributes(typeof( System.Runtime.CompilerServices.CompilerGeneratedAttribute), true).Any())
.Select(x => new {Controller = x.DeclaringType.Name, Action = x.Name, ReturnType = x.ReturnType.Name, Attributes = String.Join(",", x.GetCustomAttributes().Select(a => a.GetType().Name.Replace("Attribute",""))) })
.OrderBy(x=>x.Controller).ThenBy(x => x.Action).ToList();
If you run this code in linqpad for instance and call
controlleractionlist.Dump();
you get the following output:
You can use reflection to find all Controllers in the current assembly, and then find their public methods that are not decorated with the NonAction attribute.
Assembly asm = Assembly.GetExecutingAssembly();
asm.GetTypes()
.Where(type=> typeof(Controller).IsAssignableFrom(type)) //filter controllers
.SelectMany(type => type.GetMethods())
.Where(method => method.IsPublic && ! method.IsDefined(typeof(NonActionAttribute)));
I was looking for a way to get Area, Controller and Action and for this I manage to change a little the methods you post here, so if anyone is looking for a way to get the AREA here is my ugly method (which I save to an xml):
public static void GetMenuXml()
{
var projectName = Assembly.GetExecutingAssembly().FullName.Split(',')[0];
Assembly asm = Assembly.GetAssembly(typeof(MvcApplication));
var model = asm.GetTypes().
SelectMany(t => t.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public))
.Where(d => d.ReturnType.Name == "ActionResult").Select(n => new MyMenuModel()
{
Controller = n.DeclaringType?.Name.Replace("Controller", ""),
Action = n.Name,
ReturnType = n.ReturnType.Name,
Attributes = string.Join(",", n.GetCustomAttributes().Select(a => a.GetType().Name.Replace("Attribute", ""))),
Area = n.DeclaringType.Namespace.ToString().Replace(projectName + ".", "").Replace("Areas.", "").Replace(".Controllers", "").Replace("Controllers", "")
});
SaveData(model.ToList());
}
Edit:
//assuming that the namespace is ProjectName.Areas.Admin.Controllers
Area=n.DeclaringType.Namespace.Split('.').Reverse().Skip(1).First()
All these answers rely upon reflection, and although they work, they try to mimic what the middleware does.
Additionally, you may add controllers in different ways, and it is not rare to have the controllers shipped in multiple assemblies. In such cases, relying on reflection requires too much knowledge: for example, you have to know which assemblies are to be included, and when controllers are registered manually, you might choose a specific controller implementation, thus leaving out some legit controllers that would be picked up via reflection.
The proper way in ASP.NET Core to get the registered controllers (wherever they are) is to require this service IActionDescriptorCollectionProvider.
The ActionDescriptors property contains the list of all the actions available. Each ControllerActionDescriptor provides details
including names, types, routes, arguments and so on.
var adcp = app.Services.GetRequiredService<IActionDescriptorCollectionProvider>();
var descriptors = adcp.ActionDescriptors
.Items
.OfType<ControllerActionDescriptor>();
For further information, please see the MSDN documentation.
Edited You may find more information on this SO question.
var result = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(ApiController).IsAssignableFrom(type))
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public))
.Where(m => !m.GetCustomAttributes(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), true).Any())
.GroupBy(x => x.DeclaringType.Name)
.Select(x => new { Controller = x.Key, Actions = x.Select(s => s.Name).ToList() })
.ToList();
Assembly assembly = Assembly.LoadFrom(sAssemblyFileName)
IEnumerable<Type> types = assembly.GetTypes().Where(type => typeof(Controller).IsAssignableFrom(type)).OrderBy(x => x.Name);
foreach (Type cls in types)
{
list.Add(cls.Name.Replace("Controller", ""));
IEnumerable<MemberInfo> memberInfo = cls.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public).Where(m => !m.GetCustomAttributes(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), true).Any()).OrderBy(x => x.Name);
foreach (MemberInfo method in memberInfo)
{
if (method.ReflectedType.IsPublic && !method.IsDefined(typeof(NonActionAttribute)))
{
list.Add("\t" + method.Name.ToString());
}
}
}
If it may helps anyone, I improved #AVH's answer to get more informations using recursivity.
My goal was to create an autogenerated API help page :
Assembly.GetAssembly(typeof(MyBaseApiController)).GetTypes()
.Where(type => type.IsSubclassOf(typeof(MyBaseApiController)))
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public))
.Where(m => !m.GetCustomAttributes(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), true).Any())
.Select(x => new ApiHelpEndpointViewModel
{
Endpoint = x.DeclaringType.Name.Replace("Controller", String.Empty),
Controller = x.DeclaringType.Name,
Action = x.Name,
DisplayableName = x.GetCustomAttributes<DisplayAttribute>().FirstOrDefault()?.Name ?? x.Name,
Description = x.GetCustomAttributes<DescriptionAttribute>().FirstOrDefault()?.Description ?? String.Empty,
Properties = x.ReturnType.GenericTypeArguments.FirstOrDefault()?.GetProperties(),
PropertyDescription = x.ReturnType.GenericTypeArguments.FirstOrDefault()?.GetProperties()
.Select(q => q.CustomAttributes.SingleOrDefault(a => a.AttributeType.Name == "DescriptionAttribute")?.ConstructorArguments ?? new List<CustomAttributeTypedArgument>() )
.ToList()
})
.OrderBy(x => x.Controller)
.ThenBy(x => x.Action)
.ToList()
.ForEach(x => apiHelpViewModel.Endpoints.Add(x)); //See comment below
(Just change the last ForEach() clause as my model was encapsulated inside another model).
The corresponding ApiHelpViewModel is :
public class ApiHelpEndpointViewModel
{
public string Endpoint { get; set; }
public string Controller { get; set; }
public string Action { get; set; }
public string DisplayableName { get; set; }
public string Description { get; set; }
public string EndpointRoute => $"/api/{Endpoint}";
public PropertyInfo[] Properties { get; set; }
public List<IList<CustomAttributeTypedArgument>> PropertyDescription { get; set; }
}
As my endpoints return IQueryable<CustomType>, the last property (PropertyDescription) contains a lot of metadatas related to CustomType's properties. So you can get the name, type, description (added with a [Description] annotation) etc... of every CustomType's properties.
It goes further that the original question, but if it can help someone...
UPDATE
To go even further, if you want to add some [DataAnnotation] on fields you can't modify (because they've been generated by a Template for example), you can create a MetadataAttributes class :
[MetadataType(typeof(MetadataAttributesMyClass))]
public partial class MyClass
{
}
public class MetadataAttributesMyClass
{
[Description("My custom description")]
public int Id {get; set;}
//all your generated fields with [Description] or other data annotation
}
BE CAREFUL : MyClass MUST be :
A partial class,
In the same namespace as the generated MyClass
Then, update the code which retrieves the metadatas :
Assembly.GetAssembly(typeof(MyBaseController)).GetTypes()
.Where(type => type.IsSubclassOf(typeof(MyBaseController)))
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public))
.Where(m => !m.GetCustomAttributes(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), true).Any())
.Select(x =>
{
var type = x.ReturnType.GenericTypeArguments.FirstOrDefault();
var metadataType = type.GetCustomAttributes(typeof(MetadataTypeAttribute), true)
.OfType<MetadataTypeAttribute>().FirstOrDefault();
var metaData = (metadataType != null)
? ModelMetadataProviders.Current.GetMetadataForType(null, metadataType.MetadataClassType)
: ModelMetadataProviders.Current.GetMetadataForType(null, type);
return new ApiHelpEndpoint
{
Endpoint = x.DeclaringType.Name.Replace("Controller", String.Empty),
Controller = x.DeclaringType.Name,
Action = x.Name,
DisplayableName = x.GetCustomAttributes<DisplayAttribute>().FirstOrDefault()?.Name ?? x.Name,
Description = x.GetCustomAttributes<DescriptionAttribute>().FirstOrDefault()?.Description ?? String.Empty,
Properties = x.ReturnType.GenericTypeArguments.FirstOrDefault()?.GetProperties(),
PropertyDescription = metaData.Properties.Select(e =>
{
var m = metaData.ModelType.GetProperty(e.PropertyName)
.GetCustomAttributes(typeof(DescriptionAttribute), true)
.FirstOrDefault();
return m != null ? ((DescriptionAttribute)m).Description : string.Empty;
}).ToList()
};
})
.OrderBy(x => x.Controller)
.ThenBy(x => x.Action)
.ToList()
.ForEach(x => api2HelpViewModel.Endpoints.Add(x));
(Credit to this answer)
and update PropertyDescription as public List<string> PropertyDescription { get; set; }
Use Reflection, enumerate all types inside the assembly and filter classes inherited from System.Web.MVC.Controller, than list public methods of this types as actions
Or, to whittle away at #dcastro 's idea and just get the controllers:
Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(Controller).IsAssignableFrom(type))
#decastro answer is good. I add this filter to return only public actions those have been declared by the developer.
var asm = Assembly.GetExecutingAssembly();
var methods = asm.GetTypes()
.Where(type => typeof(Controller)
.IsAssignableFrom(type))
.SelectMany(type => type.GetMethods())
.Where(method => method.IsPublic
&& !method.IsDefined(typeof(NonActionAttribute))
&& (
method.ReturnType==typeof(ActionResult) ||
method.ReturnType == typeof(Task<ActionResult>) ||
method.ReturnType == typeof(String) ||
//method.ReturnType == typeof(IHttpResult) ||
)
)
.Select(m=>m.Name);
Update:
For .NET 6 minimal hosting model see this answer on how to replace Startup in the code below
https://stackoverflow.com/a/71026903/3850405
Original:
In .NET Core 3 and .NET 5 you can do it like this:
Example:
public class Example
{
public void ApiAndMVCControllers()
{
var controllers = GetChildTypes<ControllerBase>();
foreach (var controller in controllers)
{
var actions = controller.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public);
}
}
private static IEnumerable<Type> GetChildTypes<T>()
{
var types = typeof(Startup).Assembly.GetTypes();
return types.Where(t => t.IsSubclassOf(typeof(T)) && !t.IsAbstract);
}
}

Linq OrderedQueryAble Error

First Thing's First I have a class to manipulate some data through a linq variable:
public class Result
{
public bool LongerThan10Seconds { get; set; }
public int Id { get; set; }
public DateTime CompletionTime { get; set; }
}
Then in a Separate class I'm using this to grab info from a linq var
using (var data = new ProjectEntities())
{
Result lastResult = null;
List<Result> dataResults = new List<Result>();
foreach(var subResult in data.Status.Select(x => x.ID).Distinct().Select(Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time)))
{
if (lastResult != null)
{
if (subResult.CompletionTime.Subtract(lastResult.CompletionTime).Seconds > 10)
dataResults.Add(subResult);
}
lastResult = subResult;
}
however I get the error:
Linq.IOrderedQueryAble does not contain a definition for 'CompletionTime' and no Extension method 'CompletionTime' accepting a first argument of type 'System.Linq.IOrderedQueryable.
Is anyone able to provide a solution to get around this would be much appreciated been trying to figure it out for a while but seems a bit difficult in terms of a DateTime Variable.
Your problem is that subResult holds an IOrderedQueryable (presumably an IOrderedQueryable<Result>) rather than a Result.
You have this in the foreach: var subResult in data.Status.Select(x => x.ID).Distinct().Select(Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time)). Notice what's inside the Select: Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time). That will return an IOrderedQueryable<T>, not a T (where T is whatever type is in the data.Status collection).
You need to either get a single value out of that IOrderedQueryable, using First() or some similar method, like this:
var subResult in data.Status.Select(x => x.ID).Distinct().Select(Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time).First())
... or flatten your IEnumerable<IQueryable<T>> to an IEnumerable<T>:
var subResult in data.Status.Select(x => x.ID).Distinct().SelectMany(Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time))
Edit: You may also be having an issue where C# is uncertain what type the var subResult is. If they're all Result type objects, try replacing var subResult with Result subResult.
Looks like you have to use SelectMany instead your second Select method call
data.Status.Select(x => x.ID).Distinct()
.SelectMany(Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time))

Categories

Resources