Преобразование SQL-запроса в Linq с использованием Entity Framework - PullRequest
0 голосов
/ 07 апреля 2019

Я пытаюсь преобразовать этот SQL-запрос в LINQ в моем хранилище:

SELECT 
    ministry.Id, ministry.Name as ministry, 
    MemberGroup.Name as memberGroup
FROM 
    Ministry, MemberGroup
WHERE 
    ministry.Id = MemberGroup.MinistryId;

Моя попытка Linq:

var ministries = await _context.Ministries
    .Join(_context.MemberGroups, d => d.Id, f => f.MinistryId, (d, f) => d)
    .ToList();
return ministries;

Ответы [ 2 ]

0 голосов
/ 08 апреля 2019

Как жаль, что вы не дали нам свои классы и требования вашего запроса, а вместо этого дали нам какое-то SQL-выражение, из которого мы должны угадать ваши классы и отношения между вашими классами.

Мне кажется, что у вас есть таблица Ministries, где каждый Ministry имеет как минимум свойства Id и Name. У вас также есть таблица MemberGroups, у каждого MemberGroup есть Id, Name и внешний ключ MemberGroupId.

Мне кажется, что существует отношение один-ко-многим между Ministries и MemberGroups: каждый Ministry имеет ноль или более MemberGroups, и каждый MemberGroup принадлежит ровно одному Ministry, это министерство, на которое указывает внешний ключ.

Может случиться так, что один ко многим наоборот. или что это отношение один к одному. Вот почему я хотел бы иметь требование. Если все наоборот, ответ будет аналогичным, но вы получите суть.

Если вы следовали первым соглашениям Entity Framework Code , у вас будут такие классы:

class Ministry
{
    public int Id {get; set;}
    public string Name {get; set;}

    // every Ministry has zero or more MemberGroups (one-to-many)
    public virtual ICollection<MemberGroup> MemberGroups {get; set;}
}
class MemberGroup
{
    public int Id {get; set;}
    public string Name {get; set;}

    // every MemberGroup belongs to exactly one Ministry, using foreign key
    public int MinistryId {get; set;}
    public virtual Ministry Ministry {get; set;}
}
public MyDbContext : DbContext
{
    public DbSet<Ministry> Ministries {get; set;}
    public DbSet<MemberGroup> MemberGroups {get; set;}
}

Это все, что должна знать структура сущностей, чтобы обнаружить ваши таблицы, столбцы и связи между таблицами. Только если вы хотите отклониться от поведения по умолчанию, вам понадобятся атрибуты или свободный API

В структуре сущностей столбцы таблиц представлены не виртуальными свойствами. Виртуальные свойства представляют отношения между таблицами (один ко многим, многие ко многим)

Если вы следовали соглашениям, ваш запрос будет простым и интуитивно понятным:

Требование: дайте мне идентификатор и имя (и, возможно, некоторые другие свойства) всех (или некоторых) министерств, каждое из которых имеет имена (и, возможно, некоторые другие свойства) своих групп пользователей

var result = dbContext.Ministries
    .Where(ministry => ...)               // only if you don't want all Ministries
    .Select(ministry => new
    {
        // Select only the properties that you plan to use
        Id = ministry.Id,
        Name = ministry.Name,
        ...

        MemberGroups = ministry.MemberGroups
            .Where(memberGroup => ...)       // only if you don't want all its MemberGroups
            .Select(memberGroup => new
            {
                // again: only the properties that you plan to use
                Id = memberGroup.Id,
                Name = memberGroup.Name,
                ...

                // not needed: you know the value
                // MinistryId = memberGroup.MinistryId,
            })
            .ToList(),
       });

Платформа сущностей знает ваше отношение «один ко многим» и достаточно умна, чтобы обнаружить, что для этого необходимо (групповое) объединение.

Кстати, если вам нужны только имена членов группы:

...
MemberGroupNames = ministry.MemberGroups
                           .Select(memberGroup => memberGroup.Name)
                           .ToList(),

Если вам действительно нравится делать GroupJoin самостоятельно:

var result = dbContext.Ministries.GroupJoin(
    dbContext.MemberGroup,
    ministry => ministry.Id,
    memberGroup => memberGroup.MinistryId,

    (ministry, memberGroupsOfThisMinistry) => new
    {
        Id = ministry.Id,
        Name = ministry.Name,

        MemberGroups = memberGroupsOfThisMinistry.Select(memberGroup => new
        {
            Id = memberGroup.Id,
            Name = memberGroup.Name,
            ...
        })
        .ToList(),
     });

И, наконец: если вы не хотите, чтобы министерства с их группами участников были в качестве GroupJoin, а в качестве простого объединения, используйте SelectMany вместо Select или Join вместо GroupJoin:

    var result = dbContext.Ministries.SelectMany(ministry.MemberGroups,

      (ministry, memberGroup) => new
      {
           MinistryId = ministry.Id,
           MinistryName = ministry.Name,
           MemberGroupName = memgerGroup.Name,
      });
0 голосов
/ 08 апреля 2019

Чтобы ответить на этот вопрос, нам нужно посмотреть, как выглядят ваши отношения между Министерством и MemberGroup. Смысл и мощь ORM, подобного EF, заключается в том, чтобы он понимал отношения между таблицами и позволял ему автоматически управлять объединением.

Например: если у Министерства есть ссылка на одну MemberGroup, чтобы сформировать 1-к-1 или много-к-1 между Министерством и MemberGroup, у вас может быть структура сущности, подобная этой:

(Много-к-1)

public class Ministry
{
   public int MinistryId { get; set; }
   public string Name { get; set; }

   public virtual MemberGroup MemberGroup { get; set; }
}

public class MemberGroup
{
   public int MemberGroupId { get; set; }
   public string Name { get; set; }
}

Когда отображается так, что министерство настроено с

(EF 6) .HasRequired(x => x.MemberGroup).WithMany().Map(x => x.MapKey("MemberGroupId")

(EF Core) .HasOne(x => x.MemberGroup).WithMany().HasForeignKey("MemberGroupId")

... тогда ваш запрос для получения подробной информации будет выглядеть примерно так:

public class MinistryViewModel
{
   public int MinistryId { get; set; }
   public string Ministry { get; set; } 
   public string MemberGroup { get; set; }
}

var ministries = _context.Ministries
   .Select(x => new MinistryViewModel
   {
      MinistryId = x.MinistryId,
      Ministry = x.Name,
      MemberGroup = x.MemberGroup.Name
   }).ToList();

Ключевые моменты из вашего примера: .Select() может заполнять анонимные типы, но они не могут (или не должны) возвращаться из функций. Создайте модель представления / DTO для деталей данных, которые вы хотите вернуть. Возвращать объекты из методов и вне контекста, из которого они были загружены, также не рекомендуется. Это легко приводит к ошибкам, проблемам производительности и безопасности.

await / async используется, если вы используете асинхронные операции: var ministries = await _context.Ministries

.Select(x => new MinistryViewModel
{
   MinistryId = x.MinistryId,
   Ministry = x.Name,
   MemberGroup = x.MemberGroup.Name
}).ToListAsync();

... нацеливать их на длительные запросы, но лучше оставить простые быстрые запросы в виде синхронных вызовов.

...