मैं EntityFramework Core Bug के लिए वैकल्पिक समाधान ढूंढ रहा हूं। मैं एक प्रश्न लिखने की कोशिश कर रहा हूं जो स्वयं को फ़िल्टर करता है।

अस्वीकरण: मैं एक स्पष्ट userId द्वारा फ़िल्टर करने की तुलना में कुछ अधिक जटिल काम कर रहा हूं, मैं इसे सरलता के लिए हार्ड-कोडेड मान के साथ उपयोग कर रहा हूं, क्योंकि सटीक कार्यान्वयन नहीं है मेरे प्रश्न के लिए प्रासंगिक।

protected override void OnModelCreating(ModelBuilder modelBuilder) =>
    modelBuilder.Entity<ConversationSubscription>()
        .HasQueryFilter(x => x.Conversation.ConversationSubscriptions
            .Select(c => c.UserId).Contains(315)
        );

चूंकि क्वेरी फ़िल्टर उस इकाई तक पहुंचने का प्रयास कर रहा है जिसमें वह फ़िल्टर कर रहा है, यह अंतहीन लूप में समाप्त होता है। चूंकि हम ModelBuilder के साथ काम कर रहे हैं, न कि DbSet के साथ, इसे IgnoreQueryFilters के रूप में चिह्नित करने का कोई तरीका नहीं है।

इसे देखते हुए, मैंने स्वयं को फ़िल्टर करने के लिए वर्तमान संदर्भ का उपयोग करने का प्रयास किया:

modelBuilder.Entity<ConversationSubscription>().HasQueryFilter(x => 
    this.Set<ConversationSubscription().AsNoTracking().IgnoreQueryFilters()
        .Where(cs => cs.ConversationId == x.ConversationId)
            .Select(c => c.UserId)
            .Contains(315)
);

हालांकि, यह एक InvalidOperationException फेंकता है, सबसे अधिक संभावना है क्योंकि हम OnModelCreating समाप्त होने से पहले संदर्भ का उपयोग करने का प्रयास कर रहे हैं।

मुझे ऐसा लगता है कि अगर मैं किसी तरह से ConversationSubsriptions को बेनामी टाइप में चुन सकता हूं, तो मैं इसे हैक कर सकता हूं, जैसे कि वे अनफ़िल्टर्ड हैं।

संपादित करें: मैंने एक अनाम प्रकार का उपयोग करके इसे हैक करने का प्रयास किया, लेकिन कोई भाग्य नहीं।

modelBuilder.Entity<ConversationSubscription>().HasQueryFilter(c =>
    x => x.Conversation.Messages.Select(m => new {
        Value = m.Conversation.ConversationSubscriptions.Distinct()
            .Select(cs => cs.UserId).Contains(c.Variable(this._userId)) 
    }).FirstOrDefault().Value
);
3
johnny 5 10 मई 2020, 04:40

2 जवाब

सबसे बढ़िया उत्तर

क्वेरी फ़िल्टर प्रारंभ में नेविगेशन गुणों या डीबी सेट तक पहुंचने का समर्थन नहीं करते थे। ऐसा लगता है कि EF Core 3.0 ने इन सीमाओं को हटा दिया है (शायद इसलिए कि नए एकल एसक्यूएल स्टेटमेंट प्रति LINQ क्वेरी मोड), निम्नलिखित प्रतिबंधों/बगों के साथ:

  1. AsNoTracking() और AsTracking() - समर्थित नहीं है, जो समझ में आता है, क्योंकि क्वेरी फ़िल्टर हमेशा SQL में अनुवादित होता है।

  2. Include / ThenInclude - की अनुमति है, लेकिन उसी कारण से इसे अनदेखा कर दिया गया है।

  3. IgnoreQueryFilters - समर्थित नहीं है। इसे बग माना जा सकता है क्योंकि इसका उपयोग अगले मामलों को हल करने के लिए किया जा सकता था।

  4. क्रॉस रेफ़रेंसिंग फ़िल्टर (जैसे इकाई A फ़िल्टर इकाई B का उपयोग करता है और इकाई B फ़िल्टर इकाई A) का उपयोग नेविगेशन गुणों या डीबी सेट के माध्यम से करता है - कारण StackOverflowException क्योंकि फिल्टर एक दूसरे का उपयोग करने की कोशिश कर रहे हैं। यह एक बग है।

  5. नेविगेशन गुणों के माध्यम से स्व-संदर्भित फ़िल्टर - # 4 के समान बग, # 6 जैसा होना चाहिए।

  6. डीबी सेट के माध्यम से सेल्फ रेफरेंसिंग फ़िल्टर - समर्थित (!), हमेशा फ़िल्टर सबक्वायरी में अनदेखा किया जाता है।

इतना कहने के साथ, सौभाग्य से आपका मामला #6 द्वारा समर्थित है, अर्थात आपका दूसरा प्रयास जिसमें केवल असमर्थित AsNoTracking() और IgnoreQueryFilters() को हटा दिया गया है:

modelBuilder.Entity<ConversationSubscription>().HasQueryFilter(x => 
    this.Set<ConversationSubscription()
        .Where(cs => cs.ConversationId == x.ConversationId)
              .Select(c => c.UserId)
              .Contains(315));
5
Ivan Stoev 10 मई 2020, 10:36

हो सकता है कि मैं जॉनी में कुछ स्पष्ट याद कर रहा हूं, इसलिए यदि मैं निशान से दूर हूं, तो क्षमा करें।

लेकिन मुझे लगता है कि आप ऐसा कर रहे हैं:

  • From conversation subscriptions, join conversations, then join conversation subscriptions where user id = 315

जबकि आप ऐसा कर सकते थे/कर रहे होंगे:

  • From conversation subscriptions where user id = 315, join conversations.

मैं यह देखने में विफल रहा कि आपकी क्वेरी में राउंड ट्रिप किसके लिए आवश्यक है, क्योंकि ऐसा लगता है कि आप ConversationSubscription से क्वेरी कर रहे हैं। ऐसी क्वेरी में, एक साधारण शामिल केवल उन वार्तालापों और वार्तालापों की सदस्यता लौटाएगा, जिनकी उपयोगकर्ता के पास पहुंच है। क्या यही वह नहीं है जिसे आप वापस करना चाहते हैं?

var result = context.ConversationSubscriptions.Include(c => c.Conversation).ToList();

private int _userId = 315;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<ConversationSubscription>()
        .HasQueryFilter(x => x.UserId.Contains(_userId));
}
0
Dennis1679 10 मई 2020, 06:43
डेनिस एक उपयोगकर्ता के पास अन्य उपयोगकर्ताओं की सदस्यता तक पहुंच होनी चाहिए जो एक ही बातचीत में हैं। कल्पना कीजिए कि आप किसी अन्य उपयोगकर्ता के साथ एक वार्तालाप बनाना चाहते हैं, आपको उनके लिए एक सदस्यता बनाने में सक्षम होने की आवश्यकता होगी।
 – 
johnny 5
10 मई 2020, 15:54