使用WPF应用程序中的LINQ,在相关表中返回基于FK的EF实体列表

时间:2017-12-12 14:39:55

标签: c# sql entity-framework linq

我想返回与单个设备相关联的组件列表。我有3个表,组件,设备,EquipmentComponent。

一台设备可以有多个组件,如图所示。 enter image description here

我在数据服务上有一个方法,它应该返回一个组件列表。

apply

我做了一个SQL查询来完成这个:

v = df.apply(foo, axis=1)
v

0           Thank you It is
1           Thank you It is
2    I am surprised So am I
3    I am surprised So am I
4    Hello Nice to meet you
5    Hello Nice to meet you
dtype: object

df['relevantspeeches'] = v

但我不确定如何将此SQL查询转换为LINQ,然后返回组件列表。是否可以仅使用扩展方法?目前我在不使用LINQ的情况下从数据库返回单个组件:

public async Task<IEnumerable<Component>> GetComponentsByEquipmentIdAsync(int equipmentId)
        {
            using (var ctx = _contextCreator())
            {
                //query components code
            }
        }

2 个答案:

答案 0 :(得分:1)

稍微依赖于您的实体以及您如何建立M:N关系......

如果您将EF配置为了解M:N关系,那么您可以/应该在任何一侧都有一个导航属性,您可以使用该属性从一个元素从关系的一侧获取到相关元素另一边,反过来......

在代码第一种方法中它看起来像这样:

modelBuilder.Entity<EntityA>().HasMany(e => e.correspondingBs).WithMany(e => e.correspondingAs).Map(e => e.ToTable("AtoBTable").MapLeftKey("aId").MapRightKey("BId"));

在这种情况下,你可以在你的问题的注释中使用像KinSlayerUY提供的选择(EF处理M:N部分并隐藏在引擎盖下处理的映射表)

另一个选择是将映射表视为一个实体本身(如果关系本身具有其他属性,可能你想要这个)

在这种情况下,你最终会得到类似的东西:

ctx.Set<EntityA>().Where(x=>x.id==entityAid).SelectMany(x=>x.correspondingAtoBs.Select(y=>y.correspondingB))

(请注意代码中的拼写错误,我现在手头没有IDE)

答案 1 :(得分:1)

有两种类型的LINQ函数:使用延迟执行的函数和不使用延迟执行函数的函数。

使用延迟执行的函数(Where,Select,GroupBy,Join,...)仅更改IQueryable的表达式。只有在你调用像ToList(),Any(),FirstOrDefault()之类的非延迟函数之后,才能实际执行查询。

异步版本仅对非延迟函数有用。毕竟,有一些东西,你的线程可以做其他事情,而不是等待数据库的结果。

幸运的是,DbSet有等待的异步扩展功能。你可以使用它们。

但是,如果您设置了many-to-many relationship according to entity-framework default conventions,那么您的查询将非常简单。

class Equipment
{
    public int Id {get; set;}
    ...
    // every equipment has zero or more Components (many-to-many)
    public virtual ICollection<Component> Components {get; set;}
}
class Component
{
    public int Id {get; set;}
    public string Name {get; set;}
    // Every Component belongs to zero or more Equipments (many-to-many)
    public virtual ICollection<Equipment> Equipments {get; set;}
}
class MyDbcontext : DbContext
{
    public DbSet<Equipment> Equipments {get; set;}
    public DbSet<Component> Components {get; set;}
}

这就是所有实体框架需要了解您想要设计多对多关系。您不必提及EquipmentComponent表。实体框架将理解它是需要的,并为您创建它。

返回查询
您希望属于设备的所有组件的所有ComponentIds和ComponentNames都具有Id等于equipmentId

正确设置了多对多,您可以使用ICollections获取数据:

var result = dbContext.Equipments                     // from all equipment
    .Where(equipment => equipment.Id == equipmentId)  // take only those with equipmentId
    .SelectMany(equipment => equipment.Components     // from their components
        .Select(component => new
        {
            Id = component.Id,                        // select the Id
            Name = component.Name,                    // and the Name
        }));

这样做的结果仍然是IQueryable。尚未与数据库进行通信。要执行查询异步,您必须使用非延迟函数

 public async Task<IEnumerable<Component>> 
     GetComponentsByEquipmentIdAsync(int equipmentId)
{
    using (var dbContext = new MyDbContext())
    {
        var result = ... // see above
        return await result.ToListAsync();
    }
 }

如果你真的更喜欢偏离标准实体框架的多对多约定,并且你不想使用ICollections,但设计自己的连接,那么你必须自己做。

使用linq方法语法的内部联接没有问题,但是如果连接三个表,则内部联接看起来很可怕。 See How to perform multiple tables using lambda expressions

因此我将使用查询语法创建IQueryable,然后使用异步函数获取结果:

public async Task<IEnumerable<Component>> GetComponentsByEquipmentIdAsync (int equipmentId)
{
    using (var dbContext = new MyDbContext())
    {
         var result = from equipment in dbContext.Equipments
         .Where(equipment => equipment.Id == equipmentId)
         join equipmentmodel in dbContext.EquipmentModel
            on equipment.Id equals equipmentModel.EquipmentId
         join model in dbContext.Models
            on equipmentmodel.ModelId equals model.Id
         select new
         {
             Id = component.Id,
             Name = component.Name
         };
    return await result.ToListAsync(); // or whatever non-deferred you want
}