我有2个关系表
客户
Id,Nbr,姓名
分配
Id,CustomerId,Location,AssigmentTime
Customer.Id = Assigments.CustomerId
之间存在关系每个客户都可以有很多作业,但我只对根据DateTime字段AssigmentTime的最后一个作业感兴趣
在SQL中,它应该是一个类似的查询:
Select Top 1 From Customer c
Inner Join Assigments a On c.Id = a.CustomerId
Where c.Nbr = 1234
Order By AssigmentTime Desc
构建正确的Lambda查询时遇到问题。
此代码有效,但效果不是很好:
var customerNbr = 1234:
var cst = context.Customers.FirstOrDefault(x => x.Nbr == customerNbr);
if (cst != null && cst. Assigments.Count > 1)
{
cst. Assigments = new List<Assigments>
{
cst.Assigments.OrderByDescending(x => x.AssigmentTime).FirstOrDefault()
};
}
如何在Customer.Assigments List属性中仅获得1个顶级Assigments?
答案 0 :(得分:0)
例如:
var lastAssignment = customers.Where(x => x.Nbr == customerNbr)
.SelectMany(x => x.Assignments)
.OrderByDescending(x => x.AssignTime)
.FirstOrDefault();
答案 1 :(得分:0)
如果您已根据proper coding conventions设置实体框架,那么您已经设计了one-to-many relation as follows:
class Customer
{
public int Id {get; set;} // primary key
// a Customer has zero or more Assignments
public virtual ICollection<Assignment> Assignments {get; set;}
public int Nbr {get; set;}
... // other properties
}
class Assignment
{
public int Id {get; set;} // primary key
// every Assignment belongs to one Customer via foreign key
public int CustomerId {get; set;}
public virtual Customer Customer {get; set;}
public DateTime AssignmentTime {get; set;}
... // other properties
}
public MyDbContext : DbContext
{
public DbSet<Customer> Customers {get; set;}
public DbSet<Assignment> Assignments {get; set;}
}
如果您已经设置了这样的一对多,那么这就是所有实体框架需要知道您设计的一对多关系。如果您不想遵循命名约定,则可能使用了流畅的API或属性来配置一对多。
以最后一次(最新)作业获得Nbr = 1234的客户
using (var dbContext = new MyDbContext())
{
var result = dbContext.Customers
.Where(customer => customer.Nbr == 1234)
.Select(customer => new
{
// select the customer properties you will use, for instance
CustomerId = customer.Id,
CustomerName = customer.Name,
// you only want the newest assignment:
NewestAssignment = customer.Assignments
.OrderByDescending(assignment => assignment.AssignmentTime)
.Select(assignment => new
{ // take only the Assignment properties you will use:
Location = assignment.Location,
AssignmentTime = assignment.AssignmentTime,
}
.FirstOrDefault(),
});
}
}
如果您确定最多只有一个客户的Nbr = 1234,那么您可以以SingleOrDefault结束;否则您的结果将是具有此Nbr。
的客户序列每个客户只会拥有您将使用的客户属性,以及您将使用的最新分配的属性。高效!
答案 2 :(得分:0)
感谢您的建议Harald。我和同一个人一样,但我发现匿名对象有点臃肿。在我的例子中,我使用EF.Reverse.POCO Generator,因此每个对象都严格映射到DB。客户和作业实际上是别的东西 - 包含大量列的表格。我不能将匿名对象作为此函数的返回。
我仍然可以这样做:
using (var dbContext = new MyDbContext())
{
var result = dbContext.Customers
.Where(customer => customer.Nbr == 1234)
.Select(customer => new Customer
{
// select the customer properties you will use, for instance
Id = customer.Id,
Nbr = customer.Nbr,
Name = customer.Name,
//…and lot of other property mapping
// you only want the newest assignment:
Assignments = new Collection<Assignments>
{
customer.Assignments.OrderByDescending(assignment => assignment.AssignmentTime)
.FirstOrDefault()
}
});
}
}
匿名的Customer代将导致大量的属性映射。这是次要问题。
即使我跳过Assignments属性,Select中包含类型对象的解决方案也会在结果中生成异常:
Message =“无法在LINQ to Entities查询中构造实体或复杂类型'MyNamespace.Customer'。”
如果我使用匿名对象,相同的代码工作正常,但正如我上面所写 - 我需要输入类型的对象。