Как выполнить внешнее соединение нескольких таблиц с помощью linq to entity

Я использую ASP.NET MVC 3 с Entity Framework CodeFirst.

У меня есть два класса следующим образом:

Вопрос:

public class Question
{
    public int ID { get; set; }
    public Target Target { get; set; }
    public string QuestionText { get; set; }
    public Category Category { get; set; }
    public QuestionType QuestionType { get; set; }
    public virtual ICollection<Answer> Answers { get; set; }
    public Unit Unit { get; set; }
}

Отвечать:

public class Answer
{
    public int ID { get; set; }
    public virtual Question Question { get; set; }
    public string AnswerText { get; set; }
    public int Value { get; set; }
}

У меня также есть эта ViewModel:

public class QuestionViewModel
{
    public int ID { get; set; }
    public string Question { get; set; }
    public string QuestionType { get; set; }
    public string Target { get; set; }
    public string Category { get; set; }
    public string Unit { get; set; }
    public List<Answer> Answers { get; set; }
}

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

Я пробовал этот стиль

        var question = (from q in hontgen.Questions
                        where q.ID == id
                        join qt in db.QuestionTypes on q.QuestionType equals qt
                        join t in db.Targets on q.Target equals t
                        join c in db.Categories on q.Category equals c
                        join u in db.Units on q.Unit equals u
                        join a in db.Answers on q.Answers equals a

                        select new QuestionViewModel() {
                            ID = q.ID,
                            Question = q.QuestionText,
                            QuestionType = qt.Type,
                            Category = c.CategoryName,
                            Unit = u.UnitName,
                            Target = t.TargetName,
                            Answers = a
                        }).Single();

Но это, конечно, не работает, потому что a — это не список ответов, а только один ответ.

Как мне переписать запрос, чтобы получить все ответы в коллекции или все ответы с правильным вопросом в «Вопросе», в то же время принимая пустой список ответов?


person Niclas Lindqvist    schedule 10.02.2011    source источник


Ответы (1)


Как насчет подзапроса, подобного следующему

public class DataRepository
{
    public List<Question> Questions { get; set; }
    public IEnumerable<Answer> Answers { get; set; }

}


public class QandA
{

    DataRepository dr = new DataRepository();

    public void QueryQuestion(int id)
    {
        var question = (from q in dr.Questions
                        where q.ID == id

                        select new QuestionViewModel()
                        {
                            ID = q.ID,
                            Question = q.QuestionText,
                            Answers = (from a in dr.Answers 
                                        where a.Question == q
                                        select a)
                        });
    }

}

}

person Wes Grant    schedule 10.02.2011
comment
Привет, спасибо за ваш быстрый ответ, я пробую это прямо сейчас (в то время мой код немного запутался, но я разбираюсь). Он отлично компилируется и выглядит так, как будто он выполняет эту работу. Однако что, если у меня нет ответов, что произойдет с .ToList()? - person Niclas Lindqvist; 10.02.2011
comment
Я продолжаю получать это .. Любые идеи? LINQ to Entities does not recognize the method 'System.Collections.Generic.List1[Project.Models.Answer] ToListКак ответить(System.Collections.Generic.IEnumerable1[Project.Models.Answer])' method, and this method cannot be translated into a store expression. - person Niclas Lindqvist; 10.02.2011
comment
В Репозитории Что делать, если сделать List‹Answer› Answers в IEnumerable‹Answer› Answers; тогда вы не вызываете функцию ToList(), и запрос будет выполнен. Затем, если вам понадобится функциональность списка позже, вы можете использовать Answers.ToList() вне запроса Linq to Entities. - person Wes Grant; 10.02.2011
comment
Привет еще раз, я сделал то, что вы сказали (более или менее), но у меня есть исключение: Unable to create a constant value of type 'Project.Models.Answer'. Only primitive types ('such as Int32, String, and Guid') are supported in this context. Не могу понять это.. Есть какие-то подсказки и на этот раз? Разве объекты не могут быть перечисляемыми? - person Niclas Lindqvist; 11.02.2011
comment
Да, всякий раз, когда вы используете var в Linq to Entities или Linq to Objects, результат фактически интерпретируется как IEnumerable‹T›, где T — любой примитив или объект. (Я думаю, что Linq to Sql var будет IQueryable‹T›) - person Wes Grant; 11.02.2011
comment
Если ваш код все еще похож на запрос выше, я думаю, что проблема в том, что эта строка присоединяется к a в db.Answers на q.Answers равна a. Вы присоединяетесь к db.Answer a (один ответ из db) с Question.Answers, который является IEnumerable или List. Я бы удалил исходное объединение ответов в начальном объединении. Используя что-то вроде того, что я предложил, вам не нужны ответы в начальном выражении соединения, и это единственное отношение «один ко многим» в выражениях соединения. - person Wes Grant; 11.02.2011
comment
Привет, спасибо за вклад. Да, я перешел к вашему подходу, исключив ответы из исходного запроса, но я все еще застрял с исключением Unable to create constant value. - person Niclas Lindqvist; 11.02.2011
comment
Похоже, существует известная проблема ссылка — ссылки на нескалярные переменные Не поддерживается Ссылка на нескалярные переменные, такие как сущность, в запросе не поддерживается. При выполнении такого запроса создается исключение NotSupportedException с сообщением о том, что невозможно создать постоянное значение типа EntityType. В этом контексте поддерживаются только примитивные типы («такие как Int32, String и Guid»). -- - person Wes Grant; 11.02.2011
comment
Чтобы обойти это, попробуйте использовать свойство объекта в ваших соединениях вместо всего объекта. например, присоединиться к Targets в поле Id, а не в объекте. Пример 'объединить t в db.Targets на q.Target.TargetId равно t.TargetId - person Wes Grant; 11.02.2011
comment
Я переместил запрос ответов из запроса вопросов, и теперь он работает. +1 за ссылку также, приятного чтения! - person Niclas Lindqvist; 11.02.2011