【问题标题】:Entity Framework - Query only returning one result实体框架 - 查询只返回一个结果
【发布时间】:2021-06-23 11:56:47
【问题描述】:

我有一个包含三个实体的应用程序:events、eventBands、bands。我在控制器类中编写了以下查询,但它似乎只返回一个结果。我希望它返回数据库中链接到该事件的所有乐队。

// EventController.cs(编辑方法)


    var viewModel = new EventUpdateData();
                    Event et = await _context.Event.Where(e => e.EventId == id)
                                                   .Include(b => b.EventBands)                                               
                                                   .FirstOrDefaultAsync();
    
                    viewModel.Event = et;
                    viewModel.EventBand = evtBand;
                    viewModel.Bands = et.EventBands.Select(b => b.Band).ToList();
    
                    return View(viewModel);

// EventUpdateData (ViewModel)

    public class EventUpdateData
    {
        public Event Event { get; set; }

        public EventBand EventBand { get; set; }

        public IEnumerable <Band> Bands { get; set; }
        
    }

// Edit.cshtml(选择字段的数据库中所有波段的列表)

            <div class="col-sm-2">
                <label for="band-list">Bands</label>
            </div>
            <div class="col-sm-5">
                <select name="band-list" id="band-list" class="band-list form-control">
                    <option value="#">--Please select a band--</option>
                    @if (Model.Bands != null)
                    {
                        @foreach (var band in Model.Bands)
                        {
                            <option value="@band.BandId">@band.BandTitle</option>
                        }
                    }
                </select>
            </div>

// Edit.cshtml(与事件相关的所有乐队的列表)


    <tbody class="bands">
    
                                    @foreach (var band in Model.Bands)
                                    {                                    
                                        var bandHours = Int32.Parse(band.BandHourlyRate) * Model.EventBand.EventBandHours;
                                    
                                        <tr class="event-band" data-bandID="@band.BandId">
                                            <td>
                                                <a href="#">@band.BandTitle</a>&nbsp;&nbsp;
                                            </td>
                                            <td class="band-rate" data-val="@band.BandHourlyRate">
                                                @bandHours
                                            </td>
                                        </tr>
                                    }
                                </tbody>

// dbo.Band(数据库)

   band_id band_title   band_contact band_phone band_rate event_id
    1   Green Fields    Mike Ellery 07110291029 100 1
    2   House Chicks    Dave Hart   07111928193 200 1
    3   Groove Surfaces Marie Penn  07566910295 150 1

(我希望查询获取所有这些波段)。

// dbo.EventBand

   EventBandId EventId BandId EventBandHourlyRate
    54  1   2   1
    55  1   1   2
    56  1   1   2
    57  1   2   1
    58  1   3   3
    59  1   1   1
    60  1   1   1
    61  1   2   2
    62  1   2   2
    63  1   1   1
    64  1   2   1

// dbo.Event(数据库)

1   Jenny's Garden Party 14/05/2021 00:00:00    Jenny Wren  The Grove
2   Bob's Balloon Party  15/06/2021 00:00:00    Bob The Garden

// Event.cs

public class Event
{
    public int EventId { get; set; }
    public string EventTitle { get; set; }

    [DataType(DataType.Date)]
    public DateTime EventDate { get; set; }
    public string EventCustomer { get; set; }
    public string EventVenue { get; set; }
    public virtual ICollection<Band> Band { get; set; }
    public virtual ICollection<EventBand> EventBand { get; set; }
    public virtual ICollection<EventCaterer> Caterer { get; set; }
}

// EventBand.cs

public class EventBand
{
    public int EventBandId { get; set; }

    public int EventBandHours { get; set; }

    public int EventId { get; set; }

    public int BandId { get; set; }

    public virtual Event Event { get; set; }

    public virtual Band Band { get; set; }
}

// Band.cs

public class Band
{
    public int BandId { get; set; }

    public string BandTitle { get; set; }

    public string BandContact { get; set; }

    public string BandPhone { get; set; }

    public string BandHourlyRate { get; set; }

    public virtual ICollection<EventBand> EventBand { get; set; }

}

【问题讨论】:

  • dbo.EventBand 表的内容是什么?而且,你得到的结果是什么?
  • 为什么不将 ICollection 变量命名为复数形式
  • 数据库中链接到该事件的所有乐队。什么是“那个事件”?您没有指定事件。
  • 请参阅上面的修改。我已经指定了一个事件(顶行)并列出了 EventBand 表的内容。我得到的结果是 1 Green Fields Mike Ellery 07110291029 100 1。但每次我选择不同的乐队时,我都会得到那个乐队作为一个结果。
  • 检查您的运行时连接字符串是否指向您正在检查的同一个数据库。它可能指向一个数据库,该数据库只有一个乐队用于该事件。

标签: c# .net entity-framework


【解决方案1】:

仔细查看您的示例代码后,我认为您的映射可能会混淆 EventBands 和 Bands 的关系。为什么你同时拥有多对多(Event.EventBands)和一对多? (Event.Bands)

EF 可以根据连接表的用途以两种方式表达多对多关系。由于您要在连接表中跟踪小时数,因此需要连接实体,它形成了更多的一对多对一关系。一个 Event 有许多 EventBand,每个 EventBand 都有一个 Band。如果依赖 EF 默认的基于约定的映射,同时拥有 EventBands 和 Bands 的集合可能会导致一些混乱。对于拥有一组 Band 的 Event,它将期望 Band 有一个 EventId。它没有,所以 EF 必须以某种方式映射关系,或者它正在沸腾到 EventBand。

使用具有 EventBandId 和额外属性的显式 EventBand 实体,您需要设置映射:

public class Event
{
    [Key]
    public int EventId { get; set; }
    // ...

    // public virtual ICollection<Band> Bands { get; set; } <- Remove.
    public virtual ICollection<EventBand> EventBands { get; set; } = new List<EventBand>();
}

public class Band
{
    [Key]
    public int BandId { get; set; }
    // ...

    public virtual ICollection<EventBand> EventBands { get; set; } = new List<EventBand>();
}

public class EventBand
{
    [Key]
    public int EventBandId { get; set; }
    [ForeignKey("Event")]
    public int EventId { get; set; }
    [ForeignKey("Band")]
    public int BandId { get; set; }
    
    public int EventBandHours { get; set; }
    public virtual Event Event { get; set; }
    public virtual Band Band { get; set; }
}

EF 应该能够毫不费力地解决此映射,但明确表示也无妨。从活动和乐队结束:

// Event config.
HasMany(x => x.EventBands)
    .WithRequired(x => x.Event); // EF 6
    //.WithOne(x => x.Event).Required(); // EF Core

// Band config.
HasMany(x => x.EventBands)
    .WithRequired(x => x.Band); // EF 6
    //.WithOne(x => x.Band).Required(); // EF Core

或者,从 EventBand 配置中:

HasRequired(x => x.Event)
    .WithMany(x => x.EventBands); 

HaRequired(x => x.Band)
    .WithMany(x => x.EventBands);

避免从两端配置关系,这可能会导致问题。要么从 Event & Band -> EventBand 映射它,要么从 EventBand -> Event & Band 映射它,而不是两者。

更新:EF 6 中关系的示例实体、映射和查询...

实体:

public class Event
{
    public int EventId { get; set; }
    public string Name { get; set; }

    public ICollection<EventBand> EventBands { get; set; } = new List<EventBand>();
}

public class Band
{
    public int BandId { get; set; }
    public string Name { get; set; }

    public ICollection<EventBand> EventBands { get; set; } = new List<EventBand>();
}


public class EventBand
{
    public int EventBandId { get; set; }
    public int Rank { get; set; }

    public virtual Event Event { get; set; }
    public virtual Band Band { get; set; }
}

关系配置:

public class EventConfiguration : EntityTypeConfiguration<Event>
{
    public EventConfiguration()
    {
        ToTable("Events");
        HasKey(x => x.EventId)
            .Property(x => x.EventId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        HasMany(x => x.EventBands)
            .WithRequired(x => x.Event)
            .Map(x => x.MapKey("EventId"));
    }
}

public class BandConfiguration : EntityTypeConfiguration<Band>
{
    public BandConfiguration()
    {
        ToTable("Bands");
        HasKey(x => x.BandId)
            .Property(x => x.BandId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        HasMany(x => x.EventBands)
            .WithRequired(x => x.Band)
            .Map(x => x.MapKey("BandId"));
    }
}

查询:

[Test]
public void TestEventBandEagerLoading()
{
    using (var context = new TestDbContext())
    {
        context.Configuration.LazyLoadingEnabled = false;
        var bands = context.Events
            .Include(x => x.EventBands)
            .Include(x => x.EventBands.Select(eb => eb.Band))
            .ToList();
    }
}
  • 禁用延迟加载以证明数据是预先加载的。

【讨论】:

  • 好的@Steve Py,感谢您的帮助,但我将如何收集与事件相关的所有乐队并将它们输出到视图中?我已经更新了上面的代码,以显示用于获取事件、事件波段和波段的控制器操作,以及我的编辑视图的摘录。谢谢。
  • 加载事件时,您可以使用 .Include(e =&gt; e.EventBands).Include(e=&gt;e.EventBands.Select(eb =&gt; eb.Band)); (EF6) 或 .Include(e =&gt; e.EventBands).ThenInclude(eb=&gt;eb.Band)); (EF Core) 预先加载 EventBands 和关联的乐队。最终,尽管我建议阅读使用 Select 将 Event->EventBands->Band 多对多域建模转换为类似 EventViewModel -> BandViewModel 一对多视图建模的投影。从事件中添加/删除乐队时,您只需将乐队 ID 与操作一起传回,而不是整个域对象图。
  • 尝试了上面的方法,它说“在 Include 中使用的 Lambda 表达式无效。”事件 et = await _context.Event.Where(e => e.EventId == id) .Include(e => e.EventBands).Include(e => e.EventBands.Select(eb => eb.Band)) .FirstOrDefaultAsync();
  • 您使用的是 EF6 吗?嵌套包含使用 Include(Select) 仅在 EF6 中受支持,对于 EF Core,您需要根据上面的评论使用 ThenInclude 加载。我已经为 EF6 创建了一个测试映射,我将把它附加到有效的答案 /w 查询中。
  • 不,我使用的是 EF Core 3.1。感谢您的提示。对于 EventConfiguration 和 BandConfiguration,我应该将它们放入哪些文件?他们应该放在自己的文件中,还是我可以将它们放在 EventPlannerContext 中?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多