I feel like I'm close (or maybe not) - what am I doing wrong? Is what
I'm trying to do even possible?
You were close indeed, and yes it's possible.
I would think the best approach for you case it to create an extension method for IEnumerable<Student> (the list you want to query), since you'll be re-using query in several parts, and that's why extension methods can help you keep things D.R.Y.
It would be something like this:
class Program
{
static void Main(string[] args)
{
var today = DateTime.Now;
List<Student> students = new List<Student>();
// Not quite the same code as yours since I don't have all the classes
// but it's the same idea
var tmp = students
.Where(b => b.Name == "Student 1")
.Select(b => new
{
BName = b.Name,
SuperList = b.Courses.Select(course => course.Semester == 2).ToList()
});
// This is exactly the same query using and extension method
var tmp2 = students.FancyQuery("Student 1", 2);
}
}
public static class LinqExtension
{
/// <summary>
/// Extension method to query an IEnumerable<Student> by name and course semester
/// </summary>
/// <param name="query">Type that the method extends</param>
/// <param name="name">Student name</param>
/// <param name="semester">Course semester</param>
/// <returns>IEnumerable<QuerySelectResult> after filtering by name and semester</student></returns>
public static List<QuerySelectResult> FancyQuery(this IEnumerable<Student> query, string name, int semester)
{
return query.Where(b => b.Name == name)
.Select(b => new QuerySelectResult
{
BName = b.Name,
SuperList = b.Courses.Where(course => course.Semester == 2).ToList()
}).ToList();
}
}
public class Student
{
public string Name { get; set; }
public List<Course> Courses { get; set; }
}
public class Course
{
public string Name { get; set; }
public int Semester { get; set; }
}
public class QuerySelectResult
{
public string BName { get; set; }
public List<Course> SuperList { get; set; }
}
Notes:
- Extension classes and methods within must be public static to work. AFAIK.
- this IEnumerable<Student> argument on LinqExtension's FancyQuery method, refers to the type the extension is for. For example, you won't be able to use it with an IEnumerable<int>.
- Code isn't the exactly the same as yours but tried to recreate a similar situation.
- LinqExtension shouldn't be in the same class as my code sample. Should be for example in Extension or Helper namespace (folder). To use it anywhere on you code, you should insert an using statement with it's namespace and it becomes available to extend the type you are extending
If something isn't clear, please do ask :)
Hope it's useful.
EDIT 1: Now the extension method doesn't have the whole query (Where and Select clauses).
Thank you very much! The problem here is: fancyQuery contains now the entire query. While I want only the subquery as reusable, the rest can look very different. so unfortunately this doesnt help me
how? it seems like I'm missing an important puzzlepiece here - how do I do it ?
class Program
{
static void Main(string[] args)
{
var today = DateTime.Now;
List<Student> students = new List<Student>();
// Not quite the same code as yours since I don't have all the classes
// but it's the same idea
var tmp = students
.Where(b => b.Name == "Student 1")
.Select(b => new
{
BName = b.Name,
SuperList = b.Courses.Select(course => course.Semester == 2).ToList()
});
// As you can see "students.Where(b => b.Name == "Student 1")" it's still and IEnumerable after the Where()
// clause is used, so the extension method can be used after it
var tmp2 = students.Where(b => b.Name == "Student 1").FancyQuery(2);
}
}
public static class LinqExtension
{
/// <summary>
/// Extension method to query an IEnumerable<Student> by name and course semester
/// </summary>
/// <param name="query">Type that the method extends</param>
/// <param name="name">Student name</param>
/// <param name="semester">Course semester</param>
/// <returns>IEnumerable<QuerySelectResult> after filtering by name and semester</student></returns>
public static List<QuerySelectResult> FancyQuery(this IEnumerable<Student> query, int semester)
{
// Took out the Where() clause from here, and left only the Select() one
return query.Select(b => new QuerySelectResult
{
BName = b.Name,
SuperList = b.Courses.Where(course => course.Semester == semester).ToList()
}).ToList();
}
}
EDIT 2: Extension method only for SuperList (IEnumerable<Course> with my classes)
but fancyQuery adds the entire select - which I do not want. maybe next time I do not need BName but wanna query something else - the only thing I need is SuperList
You can extend whatever type you want, just changed the the return and signature of the extension method. Now it's only used on SuperList, but can be used with any object of type IEnumerable<Course> (in this case)
static void Main(string[] args)
{
var today = DateTime.Now;
List<Student> students = new List<Student>();
// Not quite the same code as yours since I don't have all the classes
// but it's the same idea
var tmp = students
.Where(b => b.Name == "Student 1")
.Select(b => new
{
BName = b.Name,
SuperList = b.Courses.Select(course => course.Semester == 2).ToList()
});
// Now the extension method is used only on SuperList
var tmp2 = students
.Where(b => b.Name == "Student 1")
.Select(b => new
{
BName = b.Name,
SuperList = b.Courses.FancyQuery(2)
});
}
}
public static class LinqExtension
{
/// <summary>
/// Extension method to query an IEnumerable<Student> by name and course semester
/// </summary>
/// <param name="query">Type that the method extends</param>
/// <param name="name">Student name</param>
/// <param name="semester">Course semester</param>
/// <returns>IEnumerable<QuerySelectResult> after filtering by name and semester</student></returns>
public static List<Course> FancyQuery(this IEnumerable<Course> query, int semester)
{
// Took out the Where() clause from here, and left only the Select() one
return query.Where(course => course.Semester == semester).ToList();
}
}
GetFancySubQueryshould return anIQueryable<>, then you can append further modifications (such as .Where or .OrderBy), before your final projection. Adding .ToList will execute the command, so remove that bit.Invoke()will not work as expected. E.g. conditions must be formulated asExpression<Func<T, bool>>. But what you call "condition" is in fact a projection.SelectMany- i.e. do any of them referenceb? 3. Your lambda toSelectManyappears to return a boolean which is invalid?