লিনক এক্সটেনশন পদ্ধতিগুলি ব্যবহার করে আপনি কীভাবে বাম বাহিরের জয়েন সম্পাদন করেন


272

ধরে নিলাম আমার যেমন একটি বাম বাহিরের জোড় রয়েছে:

from f in Foo
join b in Bar on f.Foo_Id equals b.Foo_Id into g
from result in g.DefaultIfEmpty()
select new { Foo = f, Bar = result }

আমি কীভাবে এক্সটেনশন পদ্ধতি ব্যবহার করে একই কাজটি প্রকাশ করব? যেমন

Foo.GroupJoin(Bar, f => f.Foo_Id, b => b.Foo_Id, (f,b) => ???)
    .Select(???)

উত্তর:


444

একটি (বাইরের বামে) টেবিলের যোগদানের জন্য Barএকটি টেবিল সঙ্গে Fooউপর Foo.Foo_Id = Bar.Foo_Idল্যামডা স্বরলিপি:

var qry = Foo.GroupJoin(
          Bar, 
          foo => foo.Foo_Id,
          bar => bar.Foo_Id,
          (x,y) => new { Foo = x, Bars = y })
       .SelectMany(
           x => x.Bars.DefaultIfEmpty(),
           (x,y) => new { Foo=x.Foo, Bar=y});

27
এটি বাস্তবে যতটা পাগল তা মনে হয় না। মূলত GroupJoinবাম বাহ্যিক যোগ দেয়,SelectMany অংশটি কেবলমাত্র আপনি নির্বাচন করতে চান তার উপর নির্ভর করে।
জর্জ মাউয়ার

6
এই নিদর্শনটি দুর্দান্ত কারণ এন্টি ফ্রেমওয়ার্ক এটিকে বাম জোড় হিসাবে স্বীকৃতি দেয়, যা আমি বিশ্বাস করি যে অসম্ভবতা ছিল
নিদর্শনটি এন্টি জোড়

3
@nam আচ্ছা আপনার একটি বিবৃতি দরকার, x. বার == নাল
টড

2
@ আবদুলকরিমকানান হ্যাঁ - সিলেক্টম্যানি 1-অনেকের 2 টি স্তরকে প্রতিটি জোড়ায় একটি এন্ট্রি দিয়ে 1 লেয়ারে ফ্ল্যাট করে
মার্ক গ্র্যাভেল

1
@ মার্কগ্রাভেল আমি আপনার কোড বন্ধনে কী করেছেন তার কিছুটা ব্যাখ্যা যোগ করার জন্য আমি একটি সম্পাদনার পরামর্শ দিয়েছিলাম।
বি - রায়ান

109

যেহেতু এটি পদ্ধতি (এক্সটেনশন) সিনট্যাক্স ব্যবহার করে বাম বাহ্যিক যোগদানের জন্য ডি ফ্যাক্টোর এসও প্রশ্ন হিসাবে মনে হয়েছে, তাই আমি ভেবেছিলাম যে আমি বর্তমানে নির্বাচিত উত্তরের একটি বিকল্প যুক্ত করব যা (আমার অভিজ্ঞতায় কমপক্ষে) আমি সাধারণভাবে যা করেছি তার থেকে বেশি হয়েছে পরে

// Option 1: Expecting either 0 or 1 matches from the "Right"
// table (Bars in this case):
var qry = Foos.GroupJoin(
          Bars,
          foo => foo.Foo_Id,
          bar => bar.Foo_Id,
          (f,bs) => new { Foo = f, Bar = bs.SingleOrDefault() });

// Option 2: Expecting either 0 or more matches from the "Right" table
// (courtesy of currently selected answer):
var qry = Foos.GroupJoin(
                  Bars, 
                  foo => foo.Foo_Id,
                  bar => bar.Foo_Id,
                  (f,bs) => new { Foo = f, Bars = bs })
              .SelectMany(
                  fooBars => fooBars.Bars.DefaultIfEmpty(),
                  (x,y) => new { Foo = x.Foo, Bar = y });

একটি সাধারণ ডেটা সেট ব্যবহার করে পার্থক্য প্রদর্শনের জন্য (ধরে নিই আমরা মানগুলিতে নিজেরাই যোগ দিচ্ছি):

List<int> tableA = new List<int> { 1, 2, 3 };
List<int?> tableB = new List<int?> { 3, 4, 5 };

// Result using both Option 1 and 2. Option 1 would be a better choice
// if we didn't expect multiple matches in tableB.
{ A = 1, B = null }
{ A = 2, B = null }
{ A = 3, B = 3    }

List<int> tableA = new List<int> { 1, 2, 3 };
List<int?> tableB = new List<int?> { 3, 3, 4 };

// Result using Option 1 would be that an exception gets thrown on
// SingleOrDefault(), but if we use FirstOrDefault() instead to illustrate:
{ A = 1, B = null }
{ A = 2, B = null }
{ A = 3, B = 3    } // Misleading, we had multiple matches.
                    // Which 3 should get selected (not arbitrarily the first)?.

// Result using Option 2:
{ A = 1, B = null }
{ A = 2, B = null }
{ A = 3, B = 3    }
{ A = 3, B = 3    }    

বিকল্প 2 টি আদর্শ বাম বাহ্যিক সংযুক্ত সংজ্ঞাটির সাথে সত্য, তবে যেমনটি আমি আগে উল্লেখ করেছি প্রায়শই ডেটা সেটের উপর নির্ভর করে অযথা জটিল।


7
আমার মনে হয় "বিএস.সিংলঅরডিফল্ট ()" কাজ করবে না যদি আপনার কাছে নিম্নলিখিত যোগদান বা অন্তর্ভুক্ত থাকে। এই ক্ষেত্রে আমাদের "bs.FirstOrDefault ()" দরকার।
ধেরিক 3'15

3
সত্য, এনকিটি ফ্রেমওয়ার্ক এবং লিনক থেকে এসকিউএল উভয়ের জন্য যেহেতু তারা Singleযোগদানের মাঝে সহজেই চেকটি করতে পারে না । SingleOrDefaultতবে এই আইএমও প্রদর্শনের আরও একটি "সঠিক" উপায়।
ওসেলোট 20

1
আপনাকে আপনার যোগদানের টেবিলটি অর্ডার করতে হবে বা। ফার্স্টআরডিফল্ট () একাধিক সারি থেকে একটি এলোমেলো সারি পেতে চলেছে যা জোড়ার মানদণ্ডের সাথে মেলতে পারে, ডাটাবেসটি প্রথমে সন্ধানের জন্য যা ঘটুক না কেন।
ক্রিস মোসচিনি 14

1
@ ক্রিসমোচিনি: অর্ডার এবং ফার্স্টআরডিফল্ট অপ্রয়োজনীয় যেহেতু উদাহরণটি 0 বা 1 ম্যাচের জন্য যেখানে আপনি একাধিক রেকর্ডে ব্যর্থ হতে চান (কোডের উপরের মন্তব্যটি দেখুন)।
ওসেলোট 20

2
এটি কোনও "অনাবশ্যকতা" প্রশ্নে অনির্ধারিত নয়, অনেকে যখন "বামে বাহিরের যোগদান" বলেন তখন এটাই মনে করে। এছাড়াও, ধেরিকের দ্বারা উল্লেখ করা ফার্স্টআরডিফল্ট প্রয়োজনীয়তা হল EF / L2SQL আচরণ এবং L2Objects নয় (এগুলির কোনওটিই ট্যাগে নেই)। এই ক্ষেত্রে কল করার জন্য সিঙ্গেলআরডিফল্ট হ'ল একেবারে সঠিক পদ্ধতি। আপনি যদি কোনও সালিশী বাছাইয়ের পরিবর্তে আপনার ডেটা সেটটির চেয়ে বেশি রেকর্ডের মুখোমুখি হন এবং একটি বিভ্রান্তিমূলক অনির্ধারিত ফলাফলের দিকে নিয়ে যান তবে আপনি ব্যতিক্রম ছুঁড়ে ফেলতে চান।
ওসেলোট 20

52

দুটি ডাটা সেটে যোগদানের জন্য গ্রুপ জয়েন পদ্ধতিটি অপ্রয়োজনীয়।

ভেতরের যোগ দিতে:

var qry = Foos.SelectMany
            (
                foo => Bars.Where (bar => foo.Foo_id == bar.Foo_id),
                (foo, bar) => new
                    {
                    Foo = foo,
                    Bar = bar
                    }
            );

বাম জন্য যোগ দিন কেবলমাত্র DefaultIfEmpty () যোগ করুন

var qry = Foos.SelectMany
            (
                foo => Bars.Where (bar => foo.Foo_id == bar.Foo_id).DefaultIfEmpty(),
                (foo, bar) => new
                    {
                    Foo = foo,
                    Bar = bar
                    }
            );

EF এবং লিনিক্যু এসকিউএলে সঠিকভাবে এসকিউএলে রূপান্তরিত করে। লিনকু টু অবজেক্টের জন্য গ্রুপজউইন ব্যবহার করে যোগ দেওয়া ভাল কারণ এটি অভ্যন্তরীণভাবে লুকআপ ব্যবহার করে । তবে আপনি যদি ডিবি-কে জিজ্ঞাসাবাদ করছেন তবে গ্রুপজয়েনের বাদ দেওয়াটা এএফএইকে অভিনয় হিসাবে রয়েছে as

আমার পক্ষে ব্যক্তিগতভাবে গ্রুপজউইনের () এর তুলনায় আরও পঠনযোগ্য Select


এটি আমার জন্য একটি .Join বেশী ভালো perfomed, প্লাস আমি আমার conditonal যৌথ করতে পারে যে আমি চেয়েছিলেন (right.FooId == left.FooId || right.FooId == 0)
অ্যান্ডার্স

linq2sql এই পদ্ধতির বাম জোড় হিসাবে অনুবাদ করে। এই উত্তরটি আরও ভাল এবং সহজ। +1
গাইডো মোচা

15

আপনি এর মতো এক্সটেনশন পদ্ধতি তৈরি করতে পারেন:

public static IEnumerable<TResult> LeftOuterJoin<TSource, TInner, TKey, TResult>(this IEnumerable<TSource> source, IEnumerable<TInner> other, Func<TSource, TKey> func, Func<TInner, TKey> innerkey, Func<TSource, TInner, TResult> res)
    {
        return from f in source
               join b in other on func.Invoke(f) equals innerkey.Invoke(b) into g
               from result in g.DefaultIfEmpty()
               select res.Invoke(f, result);
    }

দেখে মনে হচ্ছে এটি (আমার প্রয়োজনের জন্য) কাজ করবে। আপনি একটি উদাহরণ প্রদান করতে পারেন? আমি লিনকিউ এক্সটেনশনে নতুন এবং আমার বাম দিকটি জুড়ে আমার মাথাটি মুড়িয়ে ফেলার জন্য খুব কঠিন সময় কাটাচ্ছে যে পরিস্থিতিতে আমি রয়েছি ...
শিব

@ স্কিচান হতে পারে আমার এটি দেখার দরকার হতে পারে, এটি পুরানো উত্তর এবং সেই সময় কাজ করছিল। আপনি কোন ফ্রেমওয়ার্ক ব্যবহার করছেন? মানে। নেট সংস্করণ?
হাজীরাজিন

2
এটি লিনাক টু অবজেক্টের পক্ষে কাজ করে তবে কোনও ডাটাবেস অনুসন্ধানের সময় নয় যখন আপনাকে আইকিউরেবলের কাজ করতে হবে এবং তার পরিবর্তে ফানস এর এক্সপ্রেশনগুলি ব্যবহার করতে হবে
বব ভ্যালে

4

ওসেলোট ২০ এর উত্তরে উন্নতি করা, যদি আপনার কোনও টেবিল থাকে তবে আপনি কেবল যেখানে 0 বা 1 সারি বের করতে চান তার সাথে বাইরের যোগসূত্র ছেড়ে চলেছেন, তবে এতে একাধিক থাকতে পারে, আপনাকে আপনার যোগদানের টেবিলটি অর্ডার করতে হবে:

var qry = Foos.GroupJoin(
      Bars.OrderByDescending(b => b.Id),
      foo => foo.Foo_Id,
      bar => bar.Foo_Id,
      (f, bs) => new { Foo = f, Bar = bs.FirstOrDefault() });

নইলে আপনি যোগদানের ক্ষেত্রে কোন সারিটি এলোমেলো হতে চলেছে (বা আরও সুনির্দিষ্টভাবে, ডিবিটি প্রথমে এটি খুঁজে পেতে যা ঘটবে)।


এটাই! কোনও সম্পর্কহীন একের সাথে একটি সম্পর্ক।
it3xl

2

মার্ক গ্র্যাভেলের উত্তরকে একটি এক্সটেনশন পদ্ধতিতে পরিণত করে, আমি নিম্নলিখিতগুলি তৈরি করেছি।

internal static IEnumerable<Tuple<TLeft, TRight>> LeftJoin<TLeft, TRight, TKey>(
    this IEnumerable<TLeft> left,
    IEnumerable<TRight> right,
    Func<TLeft, TKey> selectKeyLeft,
    Func<TRight, TKey> selectKeyRight,
    TRight defaultRight = default(TRight),
    IEqualityComparer<TKey> cmp = null)
{
    return left.GroupJoin(
            right,
            selectKeyLeft,
            selectKeyRight,
            (x, y) => new Tuple<TLeft, IEnumerable<TRight>>(x, y),
            cmp ?? EqualityComparer<TKey>.Default)
        .SelectMany(
            x => x.Item2.DefaultIfEmpty(defaultRight),
            (x, y) => new Tuple<TLeft, TRight>(x.Item1, y));
}

2

স্বীকৃত উত্তরটি কাজ করে এবং লিনকের পক্ষে বস্তুগুলির পক্ষে ভাল এটি আমাকে জাগিয়ে তোলে যে এসকিউএল কোয়েরিটি কেবল একটি সরল বাম বাহ্যিক যোগদান নয়।

নিম্নলিখিত কোডটি লিঙ্ককিট প্রকল্পের উপর নির্ভর করে যা আপনাকে অভিব্যক্তিগুলি পাস করতে এবং তাদেরকে আপনার ক্যোয়ারিতে অনুরোধ করতে সহায়তা করে।

static IQueryable<TResult> LeftOuterJoin<TSource,TInner, TKey, TResult>(
     this IQueryable<TSource> source, 
     IQueryable<TInner> inner, 
     Expression<Func<TSource,TKey>> sourceKey, 
     Expression<Func<TInner,TKey>> innerKey, 
     Expression<Func<TSource, TInner, TResult>> result
    ) {
    return from a in source.AsExpandable()
            join b in inner on sourceKey.Invoke(a) equals innerKey.Invoke(b) into c
            from d in c.DefaultIfEmpty()
            select result.Invoke(a,d);
}

এটি নিম্নলিখিত হিসাবে ব্যবহার করা যেতে পারে

Table1.LeftOuterJoin(Table2, x => x.Key1, x => x.Key2, (x,y) => new { x,y});

-1

এটির একটি সহজ সমাধান রয়েছে

শুধু নির্বাচন করুন। আপনার নির্বাচন করুন

.Select(s => new 
{
    FooName = s.Foo_Id.HasValue ? s.Foo.Name : "Default Value"
}

খুব সহজ, গ্রুপজয়েন্ট বা অন্য কিছুর দরকার নেই

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.