मुझे लगता है कि इनमें से अधिकांश के साथ समस्याएं हैं, जिनमें स्वीकृत उत्तर भी शामिल हैं, क्योंकि वे आईक्यूएबल पर लाइनक के साथ अच्छी तरह से काम नहीं करते हैं या तो बहुत अधिक सर्वर राउंड ट्रिप करने और बहुत अधिक डेटा रिटर्न, या बहुत अधिक क्लाइंट निष्पादन करने के कारण।
IEnumerable के लिए मुझे Sehe का उत्तर या समान पसंद नहीं है क्योंकि इसमें अत्यधिक मेमोरी का उपयोग होता है (एक साधारण 10000000 दो सूची परीक्षण ने लिनक्पैड को मेरी 32 जीबी मशीन पर मेमोरी से बाहर कर दिया)।
इसके अलावा, अधिकांश अन्य वास्तव में एक उचित पूर्ण आउटर जॉइन को कार्यान्वित नहीं करते हैं, क्योंकि वे एक संघट का उपयोग कर रहे हैं एक सम्मिलित विरोधी सम्मिलित सम्मिलित सम्मिलित के बजाय सम्मिलित सम्मिलित का उपयोग कर रहे हैं, जो न केवल परिणाम से डुप्लीकेट आंतरिक जुड़ने वाली पंक्तियों को समाप्त करता है, बल्कि कोई भी उचित डुप्लिकेट जो मूल रूप से बाएं या दाएं डेटा में मौजूद था।
तो यहाँ मेरे एक्सटेंशन हैं जो इन सभी मुद्दों को संभालते हैं, SQL उत्पन्न करते हैं और साथ ही LINQ में SQL को सीधे लागू करते हैं, सर्वर पर क्रियान्वित करते हैं, और Enumerables पर दूसरों की तुलना में तेज़ और कम मेमोरी के साथ होते हैं:
public static class Ext {
public static IEnumerable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftItems,
IEnumerable<TRight> rightItems,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TResult> resultSelector) {
return from left in leftItems
join right in rightItems on leftKeySelector(left) equals rightKeySelector(right) into temp
from right in temp.DefaultIfEmpty()
select resultSelector(left, right);
}
public static IEnumerable<TResult> RightOuterJoin<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftItems,
IEnumerable<TRight> rightItems,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TResult> resultSelector) {
return from right in rightItems
join left in leftItems on rightKeySelector(right) equals leftKeySelector(left) into temp
from left in temp.DefaultIfEmpty()
select resultSelector(left, right);
}
public static IEnumerable<TResult> FullOuterJoinDistinct<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftItems,
IEnumerable<TRight> rightItems,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TResult> resultSelector) {
return leftItems.LeftOuterJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector).Union(leftItems.RightOuterJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector));
}
public static IEnumerable<TResult> RightAntiSemiJoin<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftItems,
IEnumerable<TRight> rightItems,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TResult> resultSelector) {
var hashLK = new HashSet<TKey>(from l in leftItems select leftKeySelector(l));
return rightItems.Where(r => !hashLK.Contains(rightKeySelector(r))).Select(r => resultSelector(default(TLeft),r));
}
public static IEnumerable<TResult> FullOuterJoin<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftItems,
IEnumerable<TRight> rightItems,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TResult> resultSelector) where TLeft : class {
return leftItems.LeftOuterJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector).Concat(leftItems.RightAntiSemiJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector));
}
private static Expression<Func<TP, TC, TResult>> CastSMBody<TP, TC, TResult>(LambdaExpression ex, TP unusedP, TC unusedC, TResult unusedRes) => (Expression<Func<TP, TC, TResult>>)ex;
public static IQueryable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(
this IQueryable<TLeft> leftItems,
IQueryable<TRight> rightItems,
Expression<Func<TLeft, TKey>> leftKeySelector,
Expression<Func<TRight, TKey>> rightKeySelector,
Expression<Func<TLeft, TRight, TResult>> resultSelector) {
var sampleAnonLR = new { left = default(TLeft), rightg = default(IEnumerable<TRight>) };
var parmP = Expression.Parameter(sampleAnonLR.GetType(), "p");
var parmC = Expression.Parameter(typeof(TRight), "c");
var argLeft = Expression.PropertyOrField(parmP, "left");
var newleftrs = CastSMBody(Expression.Lambda(Expression.Invoke(resultSelector, argLeft, parmC), parmP, parmC), sampleAnonLR, default(TRight), default(TResult));
return leftItems.AsQueryable().GroupJoin(rightItems, leftKeySelector, rightKeySelector, (left, rightg) => new { left, rightg }).SelectMany(r => r.rightg.DefaultIfEmpty(), newleftrs);
}
public static IQueryable<TResult> RightOuterJoin<TLeft, TRight, TKey, TResult>(
this IQueryable<TLeft> leftItems,
IQueryable<TRight> rightItems,
Expression<Func<TLeft, TKey>> leftKeySelector,
Expression<Func<TRight, TKey>> rightKeySelector,
Expression<Func<TLeft, TRight, TResult>> resultSelector) {
var sampleAnonLR = new { leftg = default(IEnumerable<TLeft>), right = default(TRight) };
var parmP = Expression.Parameter(sampleAnonLR.GetType(), "p");
var parmC = Expression.Parameter(typeof(TLeft), "c");
var argRight = Expression.PropertyOrField(parmP, "right");
var newrightrs = CastSMBody(Expression.Lambda(Expression.Invoke(resultSelector, parmC, argRight), parmP, parmC), sampleAnonLR, default(TLeft), default(TResult));
return rightItems.GroupJoin(leftItems, rightKeySelector, leftKeySelector, (right, leftg) => new { leftg, right }).SelectMany(l => l.leftg.DefaultIfEmpty(), newrightrs);
}
public static IQueryable<TResult> FullOuterJoinDistinct<TLeft, TRight, TKey, TResult>(
this IQueryable<TLeft> leftItems,
IQueryable<TRight> rightItems,
Expression<Func<TLeft, TKey>> leftKeySelector,
Expression<Func<TRight, TKey>> rightKeySelector,
Expression<Func<TLeft, TRight, TResult>> resultSelector) {
return leftItems.LeftOuterJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector).Union(leftItems.RightOuterJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector));
}
private static Expression<Func<TP, TResult>> CastSBody<TP, TResult>(LambdaExpression ex, TP unusedP, TResult unusedRes) => (Expression<Func<TP, TResult>>)ex;
public static IQueryable<TResult> RightAntiSemiJoin<TLeft, TRight, TKey, TResult>(
this IQueryable<TLeft> leftItems,
IQueryable<TRight> rightItems,
Expression<Func<TLeft, TKey>> leftKeySelector,
Expression<Func<TRight, TKey>> rightKeySelector,
Expression<Func<TLeft, TRight, TResult>> resultSelector) {
var sampleAnonLgR = new { leftg = default(IEnumerable<TLeft>), right = default(TRight) };
var parmLgR = Expression.Parameter(sampleAnonLgR.GetType(), "lgr");
var argLeft = Expression.Constant(default(TLeft), typeof(TLeft));
var argRight = Expression.PropertyOrField(parmLgR, "right");
var newrightrs = CastSBody(Expression.Lambda(Expression.Invoke(resultSelector, argLeft, argRight), parmLgR), sampleAnonLgR, default(TResult));
return rightItems.GroupJoin(leftItems, rightKeySelector, leftKeySelector, (right, leftg) => new { leftg, right }).Where(lgr => !lgr.leftg.Any()).Select(newrightrs);
}
public static IQueryable<TResult> FullOuterJoin<TLeft, TRight, TKey, TResult>(
this IQueryable<TLeft> leftItems,
IQueryable<TRight> rightItems,
Expression<Func<TLeft, TKey>> leftKeySelector,
Expression<Func<TRight, TKey>> rightKeySelector,
Expression<Func<TLeft, TRight, TResult>> resultSelector) {
return leftItems.LeftOuterJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector).Concat(leftItems.RightAntiSemiJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector));
}
}
एक राइट एंटी-सेमी-ज्वाइन के बीच का अंतर ज्यादातर लाइनक के साथ ऑब्जेक्ट्स या सोर्स में म्यूट होता है, लेकिन अंतिम जवाब में सर्वर (SQL) साइड पर फर्क पड़ता है, एक अनावश्यक को हटा देता है JOIN
।
एक लैम्ब्डा में Expression
विलय करने के लिए हैंड कोडिंग को Expression<Func<>>
लिनकिट के साथ बेहतर बनाया जा सकता है, लेकिन यह अच्छा होगा यदि भाषा / संकलक ने इसके लिए कुछ मदद जोड़ी है। FullOuterJoinDistinct
और RightOuterJoin
कार्यों संपूर्णता के लिए शामिल किए गए हैं, लेकिन मैं नहीं फिर से लागू कियाFullOuterGroupJoin
अभी तक।
मैंने इसके लिए एक पूर्ण बाहरी जुड़ाव का दूसरा संस्करण लिखाIEnumerable
मामलों के जुड़ाव जहां कुंजी क्रमबद्ध है, जो बाएं बाहरी जोड़ को सही विरोधी अर्ध जोड़ के साथ संयोजित करने की तुलना में लगभग 50% तेज है, कम से कम छोटे संग्रह पर। यह सिर्फ एक बार छांटने के बाद प्रत्येक संग्रह से गुजरता है।
मैंने उस संस्करण के लिए एक और उत्तर भी जोड़ा है जो ईएफ के साथ काम करता हैInvoke
जो कस्टम विस्तार के ।