查询多对多以显示某些事件

时间:2018-04-23 21:36:24

标签: c# linq

我申请志愿者申请仪式。

他们查找可用的仪式列表,只需单击“连接”即可创建关系。

但是,如果这个仪式是其中的一部分,我希望将这个仪式从列表中删除。

这是我在控制器中的行动:

 public ActionResult ViewAvailableCeremonies()
    {
        string username = Membership.GetUser().UserName;

        var getVolunteer = (from vol in db.Volunteers
                            where username == vol.Username
                            select vol).SingleOrDefault();


        var ceremonies = (from a in getVolunteer.Appointments
                          where a.Fee != null
                          && a.Slots != 0
                          //&& getVolunteer.Appointments.Any(c => a.)
                          select a).ToList();



        return View(ceremonies);
    }

我一直在想着如何解决这个问题,但我的大脑却完全被炒了。我知道我需要查询列表以检查它们是否在那里。

一如既往,感谢您的帮助

2 个答案:

答案 0 :(得分:0)

您似乎根据用户约会过滤了可用的cerimonies。

首先,你必须搜索所有的cerimonies:

<强> E.G。

var ceremonies = (from a in db.Cerimonies
                  select a).ToList();

因此,您根据用户约会过滤了cerimonies:

我假设您在Ceremony和Appointment之间有关系,其中Appointment类中的外键是CerimonyID,而Ceremony类中的主键是ID

<强> E.G。

string username = Membership.GetUser().UserName;

var getVolunteer = (from vol in db.Volunteers
                    where username == vol.Username
                    select vol).SingleOrDefault();

var userAppointmentsIds = (from a in getVolunteer.Appointments
                            where a.Fee != null &&
                            a.Slots != 0
                            select a.CerimonyID).ToList();

var filteredCerimonies = ceremonies
                         .Where(c => !userAppointmentsIds.Contain(c.ID))
                         .ToList();

答案 1 :(得分:0)

因此,在您的应用程序中,您有Volunteer,希望通过其唯一身份标识来识别,或者如果必须使用非唯一用户名。

在此应用程序中,您希望显示此志愿者尚未参加的所有仪式的列表。

在您的示例代码中,我看到了一些完全不同的东西。我看到约会(约会与仪式一样吗?)显然有一个要求是不要使用所有的约会,但是有非零费用和非零转换的东西,

除此之外,我发现您不止一次访问您的数据库(或您的表),这实际上并不高效。

  

出于效率原因:尽量保持LINQ语句IEnumerable / IQueryable,只有当你需要最终结果时才使用ToList / ToDictionary / SingleOrDefault / FirstOrDefault / Any等

LINQ语句的连接很快。它只是改变枚举序列所需的表达式。缓慢的部分是在ToList / Any / etc。

中开始枚举

说完这个。我不确定您是否使用实体框架来访问您的数据库。如果是这样,你的答案很容易。见结尾。

没有实体框架的解决方案

因此,为了识别您的志愿者,您有一个独特的volunteerId,或者如果没有可用的(公平)唯一userName

您还有一系列Volunteers和一系列CeremoniesAppointments,其中包含非空Fee和非零Slots

VolunteersAppointments之间存在多对多关系:每个Volunteer可以有零个或多个Appointments;每个Appointment都有零个或多个Volunteers

在数据库中,这是使用联结表实现的,让我们调用此表VolunteersAppointments

好的,我们已经正确定义了您的问题。以下是课程:

class Volunteer
{
    public int Id {get; set;}
    public string UserName {get; set;}
    ...
}
class Appointment
{
    public int Id {get; set}
    public <someType1> Fee {get; set;}
    public <someType2> Slots {get; set;}
    ...
}
// junction table
class VolunteersAppointments
{
    public int UserId {get; set;}
    public int AppointmentId {get; set;}
}

可能是你以不同的方式定义了你的课程,但你得到了要点。

要获得所有仪式,使用UserName的用户尚未参加:

string userName = ...
IQueryable<Volunteer> volunteers = <your table of all Volunteers>
IQueryable<Appointment> appointments = <your table of all appointments>

// ceremonies are appointments with non-null Fee and non-zero Slots:
var ceremonies = appointments
    .Where(appointment => appointment.Fee != null && appointment.Slots != 0);

var volunteersWithUserName = volunteers
    .Where (volunteer => volunteer.UserName == userName);
    // if Volunteer.UserName unique, this will lead to a sequence with one element
    // if not unique, consider selection by Id

现在,为了获得这位志愿者没有参加的所有仪式,我首先需要参加志愿者参加的仪式。为此,我需要连接表。加入志愿者与仪式的联络表。

通常我会使用LINQ方法 - 语法而不是LINQ查询语法,因为它具有更多功能。 H owever a join with three tables looks horrible in method syntax.为此,我使用查询语法:

var attendedCeremonies = from volunteer in volunteersWithUserName
    join volap in VolunteersAppointments on volap.VolunteerId equals volunteer.Id
    join ceremony in ceremonies on volap.CeremonyId equals ceremony.Id
    select ceremony;

var notAttendedCeremonies = ceremonties.Except(attendedCeremonies);

在您的申请中,我假设您不需要仪式的所有属性。您可能只想显示名称/描述/时间/位置。您还需要Id才能进行新的约会。

var displayableCeremonies = notAttendedCeremonies.Select(ceremony => new
{
    Id = ceremony.Id,
    Name = ceremony.Name,
    Description = ceremony.Description,
    Location = ceremony.Location,
    Time = ceremony.Time,
    Duration = ceremony.Duration,
});

请注意,尽管我创建了许多单独的LINQ查询,但尚未执行任何查询,因此尚未访问您的表。这些语句只更改了查询中的表达式。像这样写它而不是一个大的LINQ语句没有大的性能损失。奖金是它可读,因此更易于测试和维护。

以下将最终进行查询:

var desiredResult = displayableCeremonies.ToList();

实体框架方法

使用实体框架时,您不需要自己执行连接。如果您使用ICollections,实体框架将理解需要三表连接。

class Volunteer
{
    public int Id {get; set;}

    // every volunteer has zero or more Appointments (many-to-many)
    public virtual ICollection<Appointment> Appointments {get; set;}

    ...
}
class Appointment
{
    public int Id {get; set}
    // every Appointment is attended by zero or more Volunteers (many-to-many)
    public virtual ICollection<Volunteer> Volunteers {get; set;}
    ...
}
public MyDbContext : DbContext
{
    public DbSet<Volunteer> Volunteers {get; set;}
    public DbSet<Appointment> Appointments {get; set;}
}

因为我坚持Entity Framework Code First Naming Conventions,这足以让实体框架理解我的意思是设计一个多对多的关系。实体框架将为我创建和维护联结表。我不需要这个表来执行我的查询。

using (var dbContext = new MyDbContext())
{
    var ceremoniesNotAttendedByUser = dbContext.Appointments
        // keep only the Ceremony appointments
        // that are not attended by any Volunteer with UserName
        .Where(appointment => 
            // only ceremonies:
            appointment.Fee != null && appointment.Slots != 0
            // and not attended by any volunteer that has userName
            // = sequence of UserNames of the volunteers that attend this appointment
            //   does not contain userName
            && !appointment.Volunteers.Select(volunteer => volunteer.UserName)
                   .Contains(userName))
        // Fetch only the properties you want to display:
        .Select(ceremony=> new
        {
            Id = ceremony.Id,
            Name = ceremony.Name,
            Description = ceremony.Description,
            Location = ceremony.Location,
            Time = ceremony.Time,
            Duration = ceremony.Duration,
        })
        .ToList();
}

您看到我不需要联结表。实体框架对我来说更自然(我有一个任务由零个或多个志愿者参加)我敢于在一个声明中向您展示。