【问题标题】:Simplify the following code to a one-liner using Linq queries使用 Linq 查询将以下代码简化为单行代码
【发布时间】:2018-07-31 11:53:24
【问题描述】:

有两个表,用户和费用,具有一对多的关系。因此,UserClass 包含一个名为 Expenses 的 ICollection 类型的导航属性。我想要的是获取所有名为 John 的用户,并且仅包括费用描述为午餐的费用。以下是我想出的。但我觉得有更好的方法。我想转换这个:

List<User> users = db.Users.Where(user => user.Name == "John").ToList();
            foreach (User user in users)
            {
                List<Expense> expenses = db.Expenses.Where(expense => expense.Description == "Lunch" && expense.AddedBy == user.Id).ToList();
                user.Expenses = expenses;
            }

到这样的事情:

List<User> users = db.Users.Where(user => user.Name == "John").Include(user => user.Expenses.Where(expence => expence.Description == "Lunch")).ToList();

但它会引发以下异常

抛出异常:EntityFramework.dll 中的“System.ArgumentException” 包含路径表达式必须引用在类型上定义的导航属性。对引用导航属性使用虚线路径,对集合导航属性使用 Select 运算符。 参数名称:路径

我想要的 SQL 查询如下所示:

SELECT * FROM dbo.[User] AS A 
INNER JOIN Expense AS B 
ON A.Id = B.AddedBy
WHERE A.Name = 'John' AND B.Description = 'Lunch'

型号: Image

PS:我是实体框架的新手

谢谢。

【问题讨论】:

  • 显示你的模型类
  • 可以在where条件前尝试使用Include方法吗
  • 我不认为你可以Include 只有导航属性的一些实例.Include(user =&gt; user.Expenses) 应该可以工作
  • @mshwf 我添加了一张图片
  • @Md.AbdulAlim 还是一样

标签: c# entity-framework linq lambda


【解决方案1】:

我的提议是使用投影查询。

db.Users.Where(u => u.Name == "John")
            .Where(u => u.Expenses.Any(e => e.Description == "Lunch")
            .Select(u => new
            {
                u,
                Expenses= u.Expenses.Where(e => e.Description == "Lunch")
            });

名为u 的属性是User 类型,它包含所有费用,另一个属性Expenses 将仅包含为Launch 生成的费用

或者使用Entity Framework Plus

db.Users.Where(user => user.Name == "John")
   .IncludeFilter(user => user.Expenses.Where(e => e.Description == "Launch"));

【讨论】:

  • Expences 应该是Expenses,你最后错过了),而.Where不会返回bool,对吧?跨度>
  • 这仍将包括在Description == "Lunch" 上至少有一笔费用的用户的所有费用。我认为 OP 只需要"John" 用户的"Lunch" 费用。 (从问题中,强调我的:“获取所有名为 John 的用户,并仅包含费用描述为午餐的费用”
  • EF+ 的东西很有趣,我会为此 +1。虽然最后仍然没有); ;)
  • 感谢您的评论。 :)
  • @ThierryV Entity Framework Plus 就像一个魅力。谢谢。
【解决方案2】:

通常,将选定数据从数据库传输到本地进程是查询速度较慢的部分之一。因此,最好不要获取比您实际计划使用的更多的属性。

您需要多个Users 以及部分或全部Expenses。每个User 都有一个主键Id,每个Expense 都有一个外键UserId,你可以确定它等于它的UserId

如果您获取 1000 个用户,每个用户有 1000 个费用,您的方法将转移 100 万个 UserIds,其中您已经知道其价值。真是浪费!

仅当您计划更改获取的值时才使用 Include。如果没有,请使用 Select`

要选择一些Users 的一些属性和它们的一些Expenses

var result = myDbContext.Users               // From the collection of all Users
    .Where(user => user.Name == ...)         // Select only those that have a name ...
    .Select(user => new                      // from every remaining user make one new object
    {
        // Select only the properties you actually plan to use:
        Id = user.Id,
        Name = user.Name,
        ...

        Expenses = user.Expenses
            .Where(expense => expense.Description == ...)
            .Select(expense => new
            {
                 // again, select only the properties you plan to use
                 Id = expense.Id,   
                 Description = expense.Description,

                 // not needed, you know it equals user.Id:
                 // UserId = expense.UserId

                 ...
            })
            .ToList(),
    });

有时你真的想要Users with their Expenses,例如你需要使用它们作为你的过程的返回值。即使这样,也可以使用 Select 并填充用例中真正需要的属性(或填充所有属性)

var result = myDbContext.Users
    .Where(user => user.Name == ...)
    .Select(user => new User()
    {
        // Select only the properties you actually plan to use:
        Id = user.Id,
        Name = user.Name,
        ...

        Expenses = user.Expenses
            .Where(expense => expense.Description == ...)
            .Select(expense => new Expense()
            {
                 // again, select only the properties you plan to use
                 Id = expense.Id,   
                 Description = expense.Description,
                 ...
            })
            .ToList(),
    });

这样做的缺点是数据库表的布局会渗透到通信层。如果您的数据库发生更改,所有调用者都需要更改,即使他们不使用新更改的属性。我的建议是在数据层之外使用您想要的类。这可能会导致UsersWithTheirAddressesUsersWithTheirExpenses,甚至可能导致UsersWithTheirFoodExpenses。如果用户得到一个额外的列,例如CountryCode,那么UsersWithTheirExpenses 就不必更改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-20
    • 1970-01-01
    • 2018-06-14
    • 1970-01-01
    • 1970-01-01
    • 2021-10-09
    相关资源
    最近更新 更多