有两个表,User和Expense,具有一对多关系。因此,UserClass包含类型为ICollection <>的名为Expenses的导航属性。我想要的是让所有名字叫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” 包含路径表达式必须引用在类型上定义的导航属性。使用虚线路径作为参考导航属性,使用“选择”运算符作为集合导航属性。 参数名称:路径
我想要的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:我是实体框架的新手
谢谢。
答案 0 :(得分: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
生成的那些费用< / p>
db.Users.Where(user => user.Name == "John")
.IncludeFilter(user => user.Expenses.Where(e => e.Description == "Launch"));
答案 1 :(得分:1)
通常,将所选数据从数据库传输到本地进程是查询中较慢的部分之一。因此,优良作法是不要获取比您实际计划使用的更多的属性。
您想要几个Users
和它们的部分或全部Expenses
。每个User
都有一个主键Id
,每个Expense
都有一个外键UserId
,您可以确定它等于其{{1 }}。
如果您获取1000个用户(每个用户有1000个费用),则您的方法将转移100万个Id
,您已经知道其价值。真浪费!
仅当计划更改获取的值时才使用包括。如果没有,请使用Select`
要使用某些User
来选择某些UserIds
的某些属性:
Users
有时您确实需要Expenses
,例如,您需要将它们用作过程的返回值。即使这样,也可以使用Select并填充用例中真正需要的属性(或填充所有属性)
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
和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(),
});
,甚至可能导致UsersWithTheirAddresses
。如果用户获得了诸如UsersWithTheirExpenses
之类的额外列,则UsersWithTheirFoodExpenses
无需更改。