在变量中使用 C# Select

Use C# Select in variable

我有一个 linq select 部分,我经常使用它,所以我想知道是否有办法“重用”它,即将它放在某种变量中。

我的 linq 语句看起来有点像这样:

var today = DateTime.Now;
var tmp = _context.Student
.Where(b => b.Semester > 10)
.Select(b => new {
    BName = b.Name,
    SuperList = b.Seminars.SelectMany(sem => sem.DateFrom <= today && ......) // this is a superfancy and long subquery that I wanna reuse
});

因为我有一些其他的 linq 查询将使用完全相同的子查询,所以我尝试创建一个函数来为我提供这个子查询部分以供重用。但是,唉,它不起作用,查询没有抛出错误,但看起来好像子查询被完全忽略了:

var today = DateTime.Now;
var f1 = GetFancySubquery(today);

var tmp = _context.Student.Where(b => b.Semester > 10)
   .Select(b => new {
        BName = b.Name,
        SuperList = f1.Invoke(b)
    });       

private Func<Student, List<SemesterSomethingDTO>> GetFancySubquery(DateTime tag)
{
    Func<Student, List<SemesterSomethingDTO>> condition = s => s.Seminars.SelectMany(sem => ....).ToList();
    return condition;
}

我觉得我很接近(或者可能不是)- 我做错了什么?我正在尝试做的事情是否可行?

我对你的实体一无所知。不过你可以试试属于code

var today = DateTime.Now;
var tmp = _context.Student.Include(i=>i.SuperList).Where(b => b.Semester > 10)
.Select(b => new {
   BName = b.Name,
   SuperList = b.Seminars.Where(sem => sem.DateFrom <= today && ......) 
});

您似乎缺少用于确保加载研讨会数据的 .Include 语句。

如果你尝试它是否有效:

_context.Student.Include(s => s.Seminars)... // continue your query here

您可以阅读更多关于 .Include here.

此外,您可以将 Func 委托声明为变量,而不是从函数返回它。考虑来自 Microsoft's documentation 的这个例子:

// Declare a Func variable and assign a lambda expression to the
// variable. The method takes a string and converts it to uppercase.
Func<string, string> selector = str => str.ToUpper();

// Create an array of strings.
string[] words = { "orange", "apple", "Article", "elephant" };
// Query the array and select strings according to the selector method.
IEnumerable<String> aWords = words.Select(selector);

I feel like I'm close (or maybe not) - what am I doing wrong? Is what I'm trying to do even possible?

你确实很接近,是的,这是可能的。

我认为对你来说最好的方法是为 IEnumerable(你要查询的列表)创建一个 extension method,因为你将在几个 re-using 查询部分,这就是为什么扩展方法可以帮助您保留内容 D.R.Y.

会是这样的:

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; }
}

备注:

  1. 扩展 classes 和其中的方法必须是 public 静态的才能工作。据我所知。
  2. LinqExtension 的 FancyQuery 方法上的这个 IEnumerable 参数,指的是扩展所针对的类型。例如,您将无法将它与 IEnumerable.
  3. 一起使用
  4. 代码与您的不完全相同,但已尝试重现类似情况。
  5. LinqExtension 不应与我的代码示例位于同一 class 中。例如应该在 Extension 或 Helper 命名空间(文件夹)中。要在你的代码中的任何地方使用它,你应该插入一个带有它的命名空间的 using 语句并且它可以用于扩展你正在扩展的类型

如果有什么不清楚的地方,请尽管问:)

希望有用。

编辑 1:现在扩展方法没有整个查询(Where 和 Select 子句)。

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();

    }
}

编辑 2:仅适用于 SuperList 的扩展方法(IEnumerable 与我的 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

您可以扩展任何您想要的类型,只需更改 return 和扩展方法的签名。现在它只在 SuperList 上使用,但可以与任何 IEnumerable 类型的对象一起使用(在本例中)

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();

    }
}

此类辅助函数需要扩展到生成的表达式树中。香草 EF 任何版本都不支持它。我建议使用 LINQKit 来完成这样的任务:

public static class DTOExtensions
{
    [Expandable(nameof(GetFancySubqueryImpl))]
    public static List<SemesterSomethingDTO> GetFancySubquery(this Student student, DateTime tag)
    {
        throw new InvalidOperationException();
    }

    private static Expression<Student, DateTime, List<SemesterSomethingDTO>> GetFancySubqueryImpl()
    {
        return (student, tag) => student.Seminars.SelectMany(sem => ....).ToList();
    }
}

和用法:

var today = DateTime.Now;
var tmp = _context.Student
    .Where(b => b.Semester > 10)
    .AsExpandable() // activating LINQKit
    .Select(b => new {
        BName = b.Name,
        SuperList = b.GetFancySubquery(today)
    });