IQueryable - 为什么这个逻辑会失败?

时间:2017-12-04 05:37:49

标签: c# sql sql-server asp.net-mvc

我有一个包含多个表的数据库,在这种情况下相关的是

VolunteerSignups和ProgramEvents:

CREATE TABLE [dbo].[VolunteerSignups] (
    [EventId]      INT            IDENTITY (1, 1) NOT NULL,
    [VolunteerNum] INT            NOT NULL,
    [UserId]       NVARCHAR (128) NULL,
    CONSTRAINT [PK_dbo.VolunteerSignups] PRIMARY KEY CLUSTERED ([EventId] ASC, [VolunteerNum] ASC),
    CONSTRAINT [FK_VolunteerSignups_ProgramEvents_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id]) ON DELETE SET NULL ON UPDATE CASCADE,
    CONSTRAINT [FK_VolunteerSignups_ProgramEvents_EventId] FOREIGN KEY ([EventId]) REFERENCES [dbo].[ProgramEvents] ([EventId])
);
--Triggers and other unneeded info

CREATE TABLE [dbo].[ProgramEvents] (
    [EventId]            INT            IDENTITY (1, 1) NOT NULL,
    [EventTitle]         NVARCHAR (256) NOT NULL,
    [NumVolunteers]      INT            NOT NULL,
    [EventLocation]      NVARCHAR (512) NOT NULL,
    [ServiceDescription] NVARCHAR (MAX) NOT NULL,
    [StartTime]          DATETIME       NOT NULL,
    [EndTime]            DATETIME       NOT NULL,
    CONSTRAINT [PK_dbo.ProgramEvents] PRIMARY KEY CLUSTERED ([EventId] ASC)
);
--Triggers and other unneeded info

总的来说,我所拥有的是一个简单的网络应用程序,可以进行各种活动(每个活动可能需要1至N名志愿者),志愿者可以报名参加活动。当然,限制是志愿者不能两次报名参加同一活动,并且他们无法注册重叠(时间)插槽。

除了志愿者插槽要求的后半部分外,我完全在MVC应用程序中完成了其他所有工作。我挣扎了一些,生气了,所以我把它留到了最后,而不是让它推迟整个项目。但现在我不能再把它推迟了,因为其他一切都有效,但是这一个问题......

到目前为止我所拥有的是:

[Authorize( Roles = "Administrator, Volunteer" )]
public class VolunteerSignupsController : Controller
{
    private EntitiesDb db = new EntitiesDb();

    // GET: VolunteerSignups
    public async Task<ActionResult> Index()
    {
        var uid = User.Identity.GetUserId();
        var exclude = ( db.VolunteerSignups.Where( o => o.UserId == uid ) );

        var volunteerSignups = db.VolunteerSignups.Include( v => v.AspNetUser ).Include( v => v.ProgramEvent );
        var diff = volunteerSignups.Where( u => ( !exclude.Any( p => p.EventId == u.EventId ) ) && u.UserId == null );

        return View( await diff.ToListAsync() );
    }

此代码完美地用于检索用户尚未注册的事件。问题是它在注册事件后返回重叠事件。所以我将代码修改为以下内容:

[Authorize( Roles = "Administrator, Volunteer" )]
public class VolunteerSignupsController : Controller
{
    private EntitiesDb db = new EntitiesDb();

    // GET: VolunteerSignups
    public async Task<ActionResult> Index()
    {
        ApplicationDbContext db1 = new ApplicationDbContext();

        var uid = User.Identity.GetUserId();
        var exclude = ( db.VolunteerSignups.Where( o => o.UserId == uid ) );

        var volunteerSignups = db.VolunteerSignups.Include( v => v.AspNetUser ).Include( v => v.ProgramEvent );
        var diff = volunteerSignups.Where( u => ( !exclude.Any( p => p.EventId == u.EventId ) ) && u.UserId == null );

        //GET: ProgramEvents that user has already signed up for
        var alreadyVolunteeredFor = db.ProgramEvents.Where( i => exclude.Any( u => i.EventId == u.EventId ) );

        //GET: ProgramEvents that the user has not signed up for 
        var qualifiesFor = db.ProgramEvents.Where( i => diff.Any( u => i.EventId == u.EventId ) );

        /*Get: VolunteerSignups where the ProgramEvent is one where:
               -The user has not previously volunteered
               -The user has not previously volunteered for a different event where the time overlaps with an event they have not signed up for*/
        var intersect = db.VolunteerSignups.Where( i => ( diff.Any( d => ( d.EventId == i.EventId ) &&
            !qualifiesFor.Any( q => q.EventId == i.EventId &&
             alreadyVolunteeredFor.Any( a => a.EventId == i.EventId &&
               a.EndTime.CompareTo( q.StartTime ) >= 0  &&
               a.StartTime.CompareTo( q.EndTime ) <= 0  ) ) ) ) );

        return View( await intersect.ToListAsync() );
    }

我很确定逻辑失败在于构成intersect的lambda表达式。在编写时,它只返回diff之前所做的完全相同的事情。但是,我对它所做的任何更改似乎都会删除该列表中的所有内容。

编辑以回复mjwillis评论:

diff中的数据将是VolunteerSignups行,这些行未针对用户未自愿提供的事件填充。列是
 1。xyz 1 null

2。xyz 2 null

3。abc 1 null

qualifiesFor应对应ProgramEventsEventIddiff中的条目相同{
1}}  1。xyz "First Title" 2 "Some Location" "A Description" "11/1/2020 4:00pm" "11/1/2020 7:00pm"
 2. abc "Second Title" 1 "A Location" "Some Description" "11/1/2020 5:00pm" "11/1/2020 6:00pm"

alreadyVonteeredFor应与人自愿提供的ProgramEvents行相对应,EventId列中的任何一列都不应相等,如果diff或{{1}也不应该相等}}:

  1. qualifiesFor
  2. 我需要的是dsd "Already Volunteered" 1 "Some Place" "Time overlaps" "11/1/2020 3:00pm" "11/1/2020 4:30pm"仅包含intersect第3行,因为第1行和第2行对应的事件的时间与志愿者已经附加的事件重叠。

1 个答案:

答案 0 :(得分:0)

我找到了解决方案。问题出在a => a.EventId == i.EventId && lambda中的intersect。这是无稽之谈,我不知道它为什么会在那里。 alreadyVolunteeredFor中的任何内容都不在qualifiesFor中,EventId列中也不会包含任何内容。

正确答案最终是将intersect的声明更改为:

var intersect = db.VolunteerSignups.Where( i => ( diff.Any( d => ( d.EventId == i.EventId ) &&
            qualifiesFor.Any( q => q.EventId == i.EventId &&
             !alreadyVolunteeredFor.Any( a =>
               (a.EndTime.CompareTo( q.StartTime ) >= 0)  &&
               (a.StartTime.CompareTo( q.EndTime ) <= 0 ) ) ) ) ) );