Почему только примитивные типы или типы перечисления поддерживаются в этом контексте с помощью EF? - PullRequest
0 голосов
/ 23 марта 2020

В моем приложении я пытаюсь выполнить запрос Join , используя EntityFramework , Шаблон репозитория , выдает ошибку ниже. В чем проблема в запросе link ? Позвольте мне объяснить подробнее

Описание ошибки

Невозможно создать постоянное значение типа ' Анонимный тип '. В этом контексте поддерживаются только примитивные типы или типы перечисления

Инициализация

_repository = new GenericRepository<WeeklyEntry>();
_repositoryGroup = new GenericRepository<Group>();
_repositoryGroupMember = new GenericRepository<GroupMember>();

Выборка Logi c

 var groups = _repositoryGroup.GetAll().OrderBy(o => o.ID)
                              .Select(s => new { s.ID, s.Name }).ToList();
 var groupMembers = _repositoryGroupMember.GetAll().OrderBy(o => o.ID)
                                          .Select(s => new { s.GroupID, s.ID, s.Name })
                                          .ToList();

Основной запрос [Не работает]

 var results = (from we in _repository.GetAll()
                join g in groups on we.GroupID equals g.ID into grpjoin
                from g in grpjoin.DefaultIfEmpty()
                join gm in groupMembers on we.DepositedByMemberID equals gm.ID into gmjoin
                from gm in gmjoin.DefaultIfEmpty()
                where gm.GroupID == g.ID
                select new
                {
                     GroupID = g.ID,
                     GroupName = g.Name,
                     MemberID = grpmresult.ID,
                     grpmresult.Name,
                     we.ID                                  
                 }).ToList();

Чтобы попытаться достичь ниже SQL Запрос

select w.GroupID, g.Name, gm.Name, w.ID
from [dbo].[WeeklyEntry] as w
left outer join [dbo].[Group] as g on g.ID = w.GroupID
left outer join [dbo].[GroupMember] as gm on gm.GroupID = g.ID
AND gm.ID = w.DepositedByMemberID
order by w.GroupID

Странные выводы

Если я включу .ToList(); с каждым запросом, например from we in _repository.GetAll().ToList(), весь запрос будет работать и даст ожидаемый результат без ОШИБКИ !!!

Таким образом, если я преобразую каждый тип возвращаемого запроса в оперативную память или IEnumerable<>, он будет работать, как и ожидалось, без каких-либо ошибок, но запрос IQueryable<> не будет работать, как ожидалось.

Новый фрагмент кода [Рабочий]

var results = (from we in _repository.GetAll().ToList()
                join g in groups on we.GroupID equals g.ID into grpjoin
                from g in grpjoin.DefaultIfEmpty()
                join gm in groupMembers on we.DepositedByMemberID equals gm.ID into gmjoin
                from gm in gmjoin.DefaultIfEmpty()
                where gm.GroupID == g.ID
                select new {...}.ToList();

Ответы [ 2 ]

1 голос
/ 23 марта 2020

Невозможно объединить таблицу базы данных с коллекцией в памяти (в вашем случае, List):

Невозможно создать постоянное значение типа Только примитивные типы или типы перечисления поддерживаются в этом контексте

Вы конвертируете их в коллекции в памяти, вызывая ToList:

 var groups = _repositoryGroup.GetAll().OrderBy(o => o.ID)
                              .Select(s => new { s.ID, s.Name }).ToList();
 var groupMembers = _repositoryGroupMember.GetAll().OrderBy(o => o.ID)
                                          .Select(s => new { s.GroupID, s.ID, s.Name })
                                          .ToList();

, к которому вы затем пытаетесь присоединиться в следующем бите code.

Если вы просто удалите эти вызовы ToList, соединение должно сработать (вы можете сохранить соединение для конечного результата, если хотите).

Помните, что IEnumerable является ленивым и фактически выполняет запрос SQL, только если вы «выполняете его» путем итерации (обычно с помощью foreach l oop или какой-либо функции, например ToList).

0 голосов
/ 26 марта 2020

Позвольте мне объяснить!

У вас есть класс GenericRepository<T>, который выглядит примерно так:

public class GenericRepository<T>
{
    MyDbContext dbContext;
    public GenericRepository()
    {
        dbContext = new MyDbContext();
    }
    public IQueryable<T> GetAll()
    {
        // whatever
    }
}

, а затем у вас есть:

_repository = new GenericRepository<WeeklyEntry>();
_repositoryGroup = new GenericRepository<Group>();
_repositoryGroupMember = new GenericRepository<GroupMember>();

var groups = _repositoryGroup.GetAll().ToList(); // other query operators are irrelevant and removed
var groupMembers = _repositoryGroupMember.GetAll().ToList();

Вызов ToList() запускает ваши запросы и переносит данные в память. Теперь у вас есть две коллекции в памяти (List<T>).

Когда вы пишете:

var results = from we in _repository.GetAll() // <-- this is IQueryable<T>
              join g in groups                // <-- this is List<T> (IEnumerable<T>)
              on we.GroupID equals g.ID into grpjoin
              ...

, вы присоединяетесь к IQueryable с помощью памяти список. При выполнении этого запроса EF не может узнать, что ваши списки в памяти (groups и groupMembers) на самом деле являются запросами из базы данных. Он видит только два списка, содержащие некоторые данные. Он не может перевести это в SQL и, следовательно, выдает ошибку.

Чтобы исправить это, вы должны удалить вызовы на ToList(). Таким образом, у вас есть три IQueryable, которые соединены вместе. EF может перевести это в SQL, только если они из одного DbContext. А поскольку это не так, выдается еще одна ошибка, сообщающая вам именно это.

Вы создаете экземпляр DbContext для каждого из GenericRepository<T> экземпляров. Итак, groups, groupMembers и we происходят из трех разных DbContexts.

Чтобы решить эту ошибку, вам нужно каким-то образом использовать один DbContext для всех ваших GenericRepository<T> с.

Например:

using (var dbContext = new MyDbContext())
{
    var groups = dbContext.Set<Group>();
    var groupMembers = dbContext.Set<GroupMember>();

    var results = from we in dbContext.Set<WeeklyEntry>()
                  join g in groups
                  on we.GroupID equals g.ID into grpjoin
                  ...

}
...