Linq इकाईयों का उपयोग करते हुए 'समाहित है ()'?


86

मैं एक क्वेरी बनाने की कोशिश कर रहा हूं, जिसमें सिल्वरलाइट ADO.Net डेटा सर्विसेज क्लाइंट एपीआई (और इसलिए Linq To Entities) का उपयोग करते हुए आईडी की सूची का उपयोग करता है। क्या किसी को पता है कि इसमें शामिल होने के लिए समर्थन नहीं किया जा रहा है?

मैं कुछ इस तरह करना चाहता हूं:

List<long?> txnIds = new List<long?>();
// Fill list 

var q = from t in svc.OpenTransaction
        where txnIds.Contains(t.OpenTransactionId)
        select t;

इस की कोशिश की:

var q = from t in svc.OpenTransaction
where txnIds.Any<long>(tt => tt == t.OpenTransactionId)
select t;

लेकिन "विधि 'किसी भी समर्थित नहीं है"।


35
नोट: एंटिटी फ्रेमवर्क 4 (.NET 4 में) में "कंटेंस" विधि है, बस अगर कोई ऐसा होता है जो इसे पढ़ रहा होता है जो इसके बारे में नहीं जानता है। मुझे पता है कि ओपी EF1 (.NET 3.5) का उपयोग कर रहा था।
डेरेलनॉर्टन

7
@Darrell मैंने केवल एक आधा घंटा बर्बाद किया क्योंकि मैं आपकी टिप्पणी पर छोड़ दिया था। काश मैं आपकी टिप्पणी पलक झपकते और स्क्रीन के पार कर पाता।
क्रिस ड्वायर

जवाबों:


97

अपडेट: ईएफ supports 4 Containsसीधे (चेकआउट Any) का समर्थन करता है , इसलिए आपको किसी भी वर्कअराउंड की आवश्यकता नहीं है।

public static IQueryable<TEntity> WhereIn<TEntity, TValue>
  (
    this ObjectQuery<TEntity> query,
    Expression<Func<TEntity, TValue>> selector,
    IEnumerable<TValue> collection
  )
{
  if (selector == null) throw new ArgumentNullException("selector");
  if (collection == null) throw new ArgumentNullException("collection");
  if (!collection.Any()) 
    return query.Where(t => false);

  ParameterExpression p = selector.Parameters.Single();

  IEnumerable<Expression> equals = collection.Select(value =>
     (Expression)Expression.Equal(selector.Body,
          Expression.Constant(value, typeof(TValue))));

  Expression body = equals.Aggregate((accumulate, equal) =>
      Expression.Or(accumulate, equal));

  return query.Where(Expression.Lambda<Func<TEntity, bool>>(body, p));
}

//Optional - to allow static collection:
public static IQueryable<TEntity> WhereIn<TEntity, TValue>
  (
    this ObjectQuery<TEntity> query,
    Expression<Func<TEntity, TValue>> selector,
    params TValue[] collection
  )
{
  return WhereIn(query, selector, (IEnumerable<TValue>)collection);
}

उपयोग:

public static void Main()
{
  using (MyObjectContext context = new MyObjectContext())
  {
    //Using method 1 - collection provided as collection
    var contacts1 =
      context.Contacts.WhereIn(c => c.Name, GetContactNames());

    //Using method 2 - collection provided statically
    var contacts2 = context.Contacts.WhereIn(c => c.Name,
      "Contact1",
      "Contact2",
      "Contact3",
      "Contact4"
      );
  }
}

6
चेतावनी; जब arg बड़ा संग्रह होता है (मेरा 8500 आइटम इंट लिस्ट था), स्टैक ओवरफ्लो। आप इस तरह की सूची को पारित करने के लिए पागल हो सकते हैं, लेकिन मुझे लगता है कि यह इस दृष्टिकोण में एक दोष को उजागर करता है, फिर भी।
dudeNumber4

2
अगर मैं ग़लत हूं तो मेरी गलती सुझाएं। लेकिन इसका मतलब यह है कि जब पारित संग्रह (फ़िल्टर) एक खाली सेट है, तो यह मूल रूप से सभी डेटा का कारण होगा क्योंकि यह क्वेरी परम को वापस कर देता है। मैं इसे सभी मूल्य को फ़िल्टर करने की उम्मीद कर रहा था, क्या ऐसा करने का कोई तरीका है?
नेप

1
यदि आपका मतलब है कि जब चेकिंग संग्रह खाली है, तो इसका कोई परिणाम नहीं होना चाहिए, उपरोक्त स्निपेट में if (!collection.Any()) //action;- को प्रतिस्थापित करें - कार्रवाई को केवल सर्वश्रेष्ठ प्रदर्शन के लिए अनुरोधित प्रकार के खाली क्वेरी को वापस करने के साथ बदलें - या बस इस लाइन को हटा दें।
शिम्मी वेइटहैंडलर

1
रिटर्न कहां पर (क्वेरी, चयनकर्ता, संग्रह); रिटर्न कहां से बदला जाना चाहिए (क्वेरी, चयनकर्ता, (IEnumerable <TValue>) संग्रह); अवांछित पुनरावृत्ति से बचने के लिए।
एंटोनी ऑब्री

1
मेरा मानना ​​है कि कोड में एक बग है। यदि मानों की आपूर्ति की गई सूची खाली है, तो सही व्यवहार को कोई परिणाम नहीं लौटाए जाने चाहिए - अर्थात संग्रह में मौजूद कोई भी वस्तु नहीं है। हालांकि, कोड सटीक विपरीत करता है - सभी मान लौटाए जाते हैं, उनमें से कोई भी नहीं। मेरा मानना ​​है कि आप चाहते हैं "अगर (!! संग्रह। किसी भी) वापसी क्वेरी (कहीं = (झूठी))"
ShadowChaser

18

आप कुछ ई-sql कोडिंग पर वापस गिर सकते हैं (कीवर्ड "it" पर ध्यान दें):

return CurrentDataSource.Product.Where("it.ID IN {4,5,6}"); 

यहाँ कोड है कि मैं एक संग्रह से कुछ ई- sql उत्पन्न करने के लिए इस्तेमाल किया है, YMMV:

string[] ids = orders.Select(x=>x.ProductID.ToString()).ToArray();
return CurrentDataSource.Products.Where("it.ID IN {" + string.Join(",", ids) + "}");

1
क्या आपके पास "यह" पर कोई और जानकारी है? "यह" उपसर्ग MSDN नमूनों में दिखाई देता है, लेकिन कहीं भी मुझे इस बारे में स्पष्टीकरण नहीं मिल सकता है कि कब / क्यों "इसकी आवश्यकता है"।
रॉबर्ट क्लेपुल

1
एंटिटी फ्रेमवर्क डायनामिक क्वेरी में प्रयुक्त , geekswithblogs.net/thanigai/archive/2009/04/29/… पर एक नज़र डालें , थानीगैनाथन सिरनजीवी इसे बताते हैं।
शिम्मी वेइटहैंडलर

13

से MSDN :

static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
    Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
    if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
    if (null == values) { throw new ArgumentNullException("values"); }
    ParameterExpression p = valueSelector.Parameters.Single();

    // p => valueSelector(p) == values[0] || valueSelector(p) == ...
    if (!values.Any())
    {
        return e => false;
    }

    var equals = values.Select(
             value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));

    var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));

    return Expression.Lambda<Func<TElement, bool>>(body, p);
} 

और क्वेरी बन जाती है:

var query2 = context.Entities.Where(BuildContainsExpression<Entity, int>(e => e.ID, ids));

3
यदि आप contains सम्‍मिलित नहीं ’करना चाहते हैं, तो केवल BuildContainsExpression विधि में निम्‍नांकित संपादन करें: - Expression.Equal Expression.NotEqual - Expression.Or हो जाता है। Expression.And
Merritt

2

मैं सिल्वरलिथ के बारे में निश्चित नहीं हूं, लेकिन इन प्रश्नों के लिए हमेशा मैं किसी भी () का उपयोग करता हूं।

var q = from t in svc.OpenTranaction
        where txnIds.Any(t.OpenTransactionId)
        select t;

5
कोई भी अनुक्रम प्रकार की वस्तु नहीं लेता है - इसका या तो कोई पैरामीटर नहीं है (जिस स्थिति में यह सिर्फ "यह खाली है या नहीं") या यह एक विधेय लेता है।
जॉन स्कीट

मुझे यह उत्तर पाकर बहुत खुशी हुई:) +1 धन्यवाद एंड्रियास
एसडीआरईज

1

रिकॉर्ड को पूरा करने के लिए, यहाँ कोड मैं अंत में इस्तेमाल किया गया (त्रुटि की जाँच स्पष्टता के लिए छोड़ दी गई) ...

// How the function is called
var q = (from t in svc.OpenTransaction.Expand("Currency,LineItem")
         select t)
         .Where(BuildContainsExpression<OpenTransaction, long>(tt => tt.OpenTransactionId, txnIds));



 // The function to build the contains expression
   static System.Linq.Expressions.Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
                System.Linq.Expressions.Expression<Func<TElement, TValue>> valueSelector, 
                IEnumerable<TValue> values)
        {
            if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
            if (null == values) { throw new ArgumentNullException("values"); }
            System.Linq.Expressions.ParameterExpression p = valueSelector.Parameters.Single();

            // p => valueSelector(p) == values[0] || valueSelector(p) == ...
            if (!values.Any())
            {
                return e => false;
            }

            var equals = values.Select(value => (System.Linq.Expressions.Expression)System.Linq.Expressions.Expression.Equal(valueSelector.Body, System.Linq.Expressions.Expression.Constant(value, typeof(TValue))));
            var body = equals.Aggregate<System.Linq.Expressions.Expression>((accumulate, equal) => System.Linq.Expressions.Expression.Or(accumulate, equal));
            return System.Linq.Expressions.Expression.Lambda<Func<TElement, bool>>(body, p);
        }


0

बहुत बहुत धन्यवाद। जहां मेरे लिए विस्तार विधि पर्याप्त थी। मैंने इसे प्रोफाइल किया और डेटाबेस के समान SQL कमांड को e-sql के रूप में जेनरेट किया।

public Estado[] GetSomeOtherMore(int[] values)
{
    var result = _context.Estados.WhereIn(args => args.Id, values) ;
    return result.ToArray();
}

यह उत्पन्न:

SELECT 
[Extent1].[intIdFRLEstado] AS [intIdFRLEstado], 
[Extent1].[varDescripcion] AS [varDescripcion]
FROM [dbo].[PVN_FRLEstados] AS [Extent1]
WHERE (2 = [Extent1].[intIdFRLEstado]) OR (4 = [Extent1].[intIdFRLEstado]) OR (8 = [Extent1].[intIdFRLEstado])

0

मुझे लगता है कि LINQ में Join करना वॉकअराउंड हो सकता है।

मैंने हालांकि कोड का परीक्षण नहीं किया है। आशा करता हूँ की ये काम करेगा। चीयर्स। :-)

List<long?> txnIds = new List<long?>();
// Fill list 

var q = from t in svc.OpenTransaction
        join tID in txtIds on t equals tID
        select t;

LINQ में शामिल हों:

http://weblogs.asp.net/salimfayad/archive/2008/07/09/linq-to-entities-join-queries.aspx


0

क्षमा करें नया उपयोगकर्ता, मैंने वास्तविक उत्तर पर टिप्पणी की होगी, लेकिन ऐसा लगता है कि मैं अभी तक ऐसा नहीं कर सकता हूं?

वैसे भी, BuildContainsExpression () के लिए नमूना कोड के साथ उत्तर के संबंध में, इस बात से अवगत रहें कि यदि आप डेटाबेस एंटिटीज (अर्थात इन-मेमोरी ऑब्जेक्ट्स) पर उस पद्धति का उपयोग नहीं करते हैं और आप IQueryable का उपयोग कर रहे हैं, कि यह वास्तव में डेटाबेस से दूर जाना है चूँकि यह मूल रूप से "जहाँ" खंड (इसे देखने के लिए SQL Profiler के साथ चलाएँ) की जाँच करने के लिए मूल रूप से बहुत सारी SQL "या" स्थितियाँ करता है।

इसका मतलब यह हो सकता है, यदि आप कई बिल्डकॉनटेनमेंट एक्सप्रेशन () के साथ एक IQueryable को परिष्कृत कर रहे हैं, तो यह एक SQL कथन में नहीं बदलेगा, जो अंत में आपकी उम्मीद के मुताबिक चलता है।

हमारे लिए समाधान यह था कि एक SQL कॉल में रखने के लिए कई LINQ जॉइन का उपयोग किया जाए।


0

चयनित उत्तर के अलावा।

बदलें Expression.Orसाथ Expression.OrElseNhibernate और ठीक से उपयोग करने के लिए Unable to cast object of type 'NHibernate.Hql.Ast.HqlBitwiseOr' to type 'NHibernate.Hql.Ast.HqlBooleanExpression'अपवाद।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.