2015-04-17 4 views
2

У меня есть два объекта под названием CountryMobility, которые, я считаю, мне нужно объединить с полным внешним соединением. Как это сделать с помощью linq?Linq Full Outer Join on Two Objects

public class CountryMobility 
{ 
    public string countryCode { get; set; } 
    public int inbound { get; set; } 
    public int outbound { get; set; } 
} 

Я хочу, чтобы объединить два из этих объектов, как так:

inboundStudents: 
countryCode | inbound | outbound 
     EG | 2 |  0 
     CA | 3 |  0 
     CH | 5 |  0 

outboundStudents: 
countryCode | inbound | outbound 
     PE | 0 |  1 
     CA | 0 |  4 
     CH | 0 |  5 


         - 
         - 
         - 
         - 
         V 

combinedStudents: 
countryCode | inbound | outbound 
     PE | 0 |  1 
     CA | 3 |  4 
     CH | 5 |  5 
     EG | 2 |  0 

Я попытался следующие заявления Linq, но не смогли выяснить правильный синтаксис. В настоящее время я получаю синтаксическую ошибку около temp.DefaultIfEmpty (new {first.ID, inbound = 0, outbound = 0}) в обоих операторах.

var leftOuterJoin = 
    from first in inboundActivities 
    join last in outboundActivities 
    on first.countryCode equals last.countryCode 
    into temp 
    from last in temp.DefaultIfEmpty 
    (new { first.countryCode, inbound = 0, outbound=0 }) 
    select new CountryMobility 
    { 
     countryCode = first.countryCode, 
     inbound = first.inbound, 
     outbound = last.outbound, 
    }; 
var rightOuterJoin = 
    from last in outboundActivities 
    join first in inboundActivities 
    on last.countryCode equals first.countryCode 
    into temp 
    from first in temp.DefaultIfEmpty 
    (new { last.countryCode, inbound = 0, outbound = 0 }) 
    select new CountryMobility 
    { 
     countryCode = last.countryCode, 
     inbound = first.inbound, 
     outbound = last.outbound, 
    }; 

var fullOuterJoin = leftOuterJoin.Union(rightOuterJoin); 
+0

возможного дубликата [LINQ - полное внешнее соединение] (http://stackoverflow.com/questions/5489987/linq-full-external-join) –

+0

Какие заявления linq вы пробовали, что они производили? Что с ними было неправильно, что вам нужна помощь? – Alex

+0

@ Josh L. Пробовал это, но я не мог заставить его работать правильно с моим. Я думаю, проблема заключалась в том, что он выводил анонимный объект, и я не мог заставить его выводить его как объект «CountryMobility». –

ответ

1

После вашей последней информации. Мне кажется, что вы можете сделать что-то намного проще. А именно: UNION ALL, который затем группируется по коду страны. A UNION ALL может быть создан с использованием метода Concat.

Образец ниже работает для меня (используется в коллекциях памяти). Запрос показан в методе Run.

public class CountryMobility 
{ 
    public string countryCode { get; set; } 
    public int inbound { get; set; } 
    public int outbound { get; set; } 
} 

public static class JoinedMobilityQuery 
{ 
    static CountryMobility[] inbound = { 
     new CountryMobility() { countryCode = "EG", inbound = 2 }, 
     new CountryMobility() { countryCode = "CA", inbound = 3 }, 
     new CountryMobility() { countryCode = "CH", inbound = 5 }, 
    }; 
    static CountryMobility[] outbound = { 
     new CountryMobility() { countryCode = "PE", outbound = 1 }, 
     new CountryMobility() { countryCode = "CA", outbound = 4 }, 
     new CountryMobility() { countryCode = "CH", outbound = 6 }, 
    }; 

    static IQueryable<CountryMobility> Inbound() 
    { 
     return inbound.AsQueryable(); 
    } 

    static IQueryable<CountryMobility> Outbound() 
    { 
     return outbound.AsQueryable(); 
    } 

    public static void Run() 
    { 
     var transfers = from t in Inbound().Concat(Outbound()) 
         group t by t.countryCode into g 
         select new CountryMobility() { 
          countryCode = g.Key, 
          inbound = g.Sum(x => x.inbound), 
          outbound = g.Sum(x => x.outbound), 
         }; 
     foreach (var transfer in transfers) 
      Console.WriteLine("{0}\t{1}\t{2}", transfer.countryCode, transfer.inbound, transfer.outbound); 
    } 
} 
+0

Спасибо! Это сработало, и мне нравится, насколько это проще! –

1

Ваш DefaultIfEmpty является thropwing ошибки, потому что вы определяете анонимный объект, но вы создаете stronly типизированных объектов в операторе отбора. Оба они должны быть одного типа.

Таким образом определить объект по умолчанию, как это:

var defaultActivity = new CountryMobility() { countryCode = String.Empty, outbound = 0, inbound = 0 }; 

После этого, используйте его в DefaultIfEmpty() метод:

from last in temp.DefaultIfEmpty(defaultActivity) 
select new CountryMobility 
{ 
    //... 
}; 

последний, но не менее, вы должны сделать GroupBy к получить желаемые результаты:

var fullOuterJoin = leftOuterJoin.Union(rightOuterJoin) 
          .GroupBy (oj => oj.countryCode) 
          .Select (oj => oj.FirstOrDefault()); 

Выход: enter image description here

Full Demo Код:

http://share.linqpad.net/u46gar.linq

0

Вы можете сделать, как этот

List<CountryMobility> inboundStudents = new List<CountryMobility>{ 
      new CountryMobility { countryCode="EG", inbound=2, outbound = 0}, 
      new CountryMobility { countryCode="CA", inbound=3, outbound = 0}, 
      new CountryMobility { countryCode="CH", inbound=5, outbound = 0}}; 

     List<CountryMobility> outboundStudents = new List<CountryMobility>{ 
      new CountryMobility { countryCode="PE", inbound=0, outbound = 1}, 
      new CountryMobility { countryCode="CA", inbound=0, outbound = 4}, 
      new CountryMobility { countryCode="CH", inbound=0, outbound = 5}}; 

       var joinedList = inboundStudents.Concat(outboundStudents).GroupBy(item => new { item.countryCode}); 
       var result = joinedList.Select(x => new 
       { 
        countryCode = x.Key.countryCode, 
        inbound = x.Sum(i => i.inbound), 
        outbound = x.Sum(i => i.outbound) 
       });