Левое внешнее соединение через Linq To Entities через Entity Framework с предложением Where - PullRequest
4 голосов
/ 22 марта 2011

Я прочитал все сообщения, связанные с реализацией эквивалента LEFT OUTER JOIN в Linq to Entities (.NET 3.5) при использовании Entity Framework, но пока не нашел решения следующей проблемы.

Учитывая две таблицы, представленные объектами ниже:

    public class Foo
    {
        public int FooId;  // primary key
        public string Name;
    }

    public class Bar
    {
        public int BarId;  // primary key
        public int FooId;  // foreign key
        public string Desc;
    }

Мне нужно создать оператор Linq to Entities, который является ЭКВИВАЛЕНТОМ следующего оператора SQL. Обратите внимание, что оператор WHERE содержит два условия OR'd, которые охватывают обе таблицы, и использование квалификатора DISTINCT.

SELECT DISTINCT
    Foo.*
FROM
    Foo
    LEFT OUTER JOIN Bar ON Foo.FooId = Bar.FooId
WHERE
    (Foo.Name = 'fooname' OR Bar.Desc = 'bardesc')

Сгенерированный мной запрос Linq - это Linq to Entities через Entity Framework, и он (будем надеяться) сгенерирует один SQL-оператор, который будет выполнен на сервере. Linq to Entities не поддерживает предложение расширения FirstOrDefault (), поэтому стандартный синтаксис Linq для LEFT OUTER JOIN не будет работать.

Вот решение, которое у меня есть ТАК, но я не могу выполнить одно из следующих действий:

1) Создание результирующего набора, который содержит набор комбинаций Foo / Bar, которые будут возвращены операцией LEFT OUTER JOIN.

2) Реализуйте эквивалент предложения WHERE: WHERE (Foo.Name = 'fooname' OR Bar.Desc = 'bardesc')

    private class JoinSet
    {
        public Foo Foo;
        public IQueryable<Bar> Bars;
    };

    private class FooBar
    {
        public Foo Foo;
        public Bar Bar;
    };

    IEnumerable<Foo> OuterJoinTest()
    {
        IQueryable<Foo> fooBaseQuery = dbContext.FooSet;
        IQueryable<Bar> barBaseQuery = dbDontext.BarSet;

        IQueryable<JoinSet> joinQuery =
            from foo in fooBaseQuery
            select new JoinSet
                    {
                        Foo = foo,
                        Bars = barBaseQuery.Where(bar => bar.FooId == foo.FooId)
                    };

        // How do I generate a result set containing FooBar objects ?

        // How or where do I insert the equivalent of: ?
        //  WHERE (Foo.Name = 'fooname' OR Bar.Description = 'bardesc')

        IQueryable<Foo> resultQuery =
            from joinSet in joinQuery
            select joinSet.Foo;

        resultQuery = resultQuery.Distinct();

        return resultQuery.ToList();
    }

Любая помощь, идеи или предложения будут оценены.

EulerOperator

1 Ответ

11 голосов
/ 22 марта 2011

.NET 3.5

private class FooBar
{
   public Foo Foo { get; set; }
   public Bar? Bar { get; set; }
}

var innerQuery = from foo in context.Foos
                 from bar in context.Bars
                 where foo.Name == 'fooname' || bar.Description == 'bardesc'
                 where foo.FooId == bar.FooId
                 select new FooBar { Foo = foo, Bar = bar };


var outerQuery = from foo in context.Foos
                 where foo.Name == 'fooname' 
                 where !context.Bars.Any(b => b.FooId == foo.FooId)
                 select new FooBar { Foo = foo, Bar = null };

var leftouterjoinQuery = innerQuery.Union(outerQuery).Distinct();

.NET 4.0

var query = (from foo in context.Foo
            join b in context.Bar
            on foo.FooId equals b.FooId into Bar
            from bar in Bar.DefaultIfEmpty()
            where foo.Name = 'fooname' || bar.Description = 'bardesc'
            select new { foo, bar }).Distinct();
...