当有两个表时,Linq to entities 语法帮助

Linq to entities syntax help, when having two tables

预期结果:

我想要一个 IQueryable<Vehicle>,其中包含未分配给任何人的车辆。

数据:

我有两个表:

车辆

AssignedToVehicle

我尝试过的:

query = context.Vehicle.Where(v => v.DeletedAt == null && 
            !v.AssignedToVehicle.Where(a=>a.VehicleId == v.Id && a.UnassignedAt != null).Any());

我的计划是删除所有带有 v.DeletedAt == null 的已删除车辆,然后在 AssignedToVehicle 中查看它是否与该车辆有任何行。如果没有,那么我希望它出现在我的列表中。

这里有哪些选项可以获取未分配车辆的列表?

您可以尝试类似的方法:

var result = vehicles.Where(car => car.DeletedAt == null &&
                            !assignedToVehicle.Any(assigned => assigned.VehicleId == car.Id))
                     .AsQueryable();

其中 vehiclesassignedToVehicle 是您的实体。

这是我制作的完整演示:

.NET Fiddle

所以你有一个 table 与 VehiclesVehicleAssignments,Vehicles 和 VehicleAssignments 之间是一对多的关系:每个 Vehicle 有零个或多个 VehicleAssignments;每个 VehiclAssignment 都属于一个 Vehicle,即外键 VehicleId 所指的那个。

(我稍微更改了你的名字,所以我可以使用复数名词和单数名词来轻松引用 tables 和 tables 中的行)

当您使用 entity framework 时,您将 类 类似于以下内容:

class Vehicle
{
    public int Id {get; set;}
    public string Name {get; set;}
    public DateTime? DeletedAt {get; set;}  // null if not deleted yet

    // every Vehicle has zero or more VehicleAssignments (one-to-many)
    public virtual ICollection<VehicleAssignement> VehicleAssignements {get; set;}
}

class VehicleAssignment
{
    public int Id {get; set;}
    public string Name {get; set;}
    public DateTime? DeletedAt {get; set;}  // null if not deleted yet

    // every VehicleAssignement belongs to exactly one Vehicle, using foreign key
    public int VehicleId {get; set;}
    public virtual Vehicle Vehicle {get; set;}
}

这足以让 entity framework 检测到您的一对多关系。如果你愿意,你可以添加属性或使用 fluent API,但只有当你想偏离标准时才需要这样做。

I would like to have a IQueryable, that contains vehicles that are not assigned to anyone.

或者更准确地说:您想要查询 returns 所有未删除的车辆(= 属性 DeletedAt 的值等于 null)并且没有未删除的 VehicleAssignments .

或者用正常的语言来说:您想要所有具有至少一项分配的车辆。车辆和作业都不能删除。

有两种方法:

  • 最简单的方法:使用 virtual ICollection<...>。 Entity framework 了解您的一对多关系,会为您创建合适的 GroupJoin
  • 自己做 GroupJoin。

使用虚拟 ICollection

IQueryable<Vehicle> assignedVehicles = dbContext.Vehicles
    .Where(vehicle => vehicle.DeletedAd == null
        && vehicle.VehicleAssignements
            .Where(vehicleAssignment => vehicleAssignment.DeletedAdd == null)
            .Any());

换句话说:从 Vehicles 集合中,仅保留那些尚未删除且至少有一个 VehicleAssignment 尚未删除的 Vehicles。

您也可以从 VehicleAssignments 开始:

IQueryable<Vehicle> assignedVehicles = dbContext.VehicleAssignments
    .Where(assignment => assignment.DeletedAt == null)
    .Select(assignment => assignment.Vehicle)
    .Where(vehicle => vehicle.DeletedAt == null)
    .Distinct();

换句话说:从VehicleAssignments的集合中,只保留那些还没有被删除的assignments。从每个剩余的 VehicleAssignments select 中,它是唯一的 Vehicle。结果:一系列车辆。从此序列中仅保留未删除的那些并删除重复项。

做(小组)加入自己

小步骤:

IQueryable<Vehicle> nonDeletedVehicles = dbContext.Vehicles
    .Where(vehicle => vehicle.DeletedAd == null);
IQueryable<VehicleAssignments> nonDeletedAssignments = dbContext.VehiclAssignments
    .Where(vehicleAssignment => vehicleAssignment.DeletedAdd == null);

IQueryable<Vehicle> vehiclesWithAnyAssignment = nonDeletedVehicles
.GroupJoin(noneDeletedAssignments,

vehicle => vehicle.Id,                 // from each Vehicle take the primary key
assignment => assignment.VehicleId,    // from each Assignment take the foreign key

// parameter resultSelector: use each vehicle, with its zero or more assignments,
// to make one new:
(vehicle, assignmentsOfThisVehicle) => 

      // if there is at least one assignment: take the vehicle, otherwise take null
      assignmentsOfThisVehicle.Any() ? vehicle : (Vehicle)null)

// remove all nulls (= vehicles that didn't have an assignment)
.Where(vehicle => vehicle != null);

你也可以做一个简单的连接,它会自动删除没有分配的车辆。您必须删除重复项:

var result = nonDeletedVehicls.Join(nonDeletedAssignments,

    vehicle => vehicle.Id,                    // primary key
    assignment => assignment.VehicleId,       // foreign key

    // parameter resultSelector: whenever you find a matching [vehicle, assignment]
    // combination, keep the Vehicle, because this Vehicle has at least one Assignment
    (vehicle, assignment) => vehicle)

    // remove Duplicates (= Vehicles with several assignments)
    .Distinct(),