सी # एंटिटी-फ्रेमवर्क: मैं कैसे जोड़ सकता हूँ। एक मॉडल ऑब्जेक्ट पर।


145

मैं mvcmusicstore अभ्यास ट्यूटोरियल कर रहा हूं। मैंने एल्बम मैनेजर के लिए मचान बनाते समय कुछ देखा (ऐडिट डिलीट एड)।

मैं कोड को सुरुचिपूर्ण ढंग से लिखना चाहता हूं, इसलिए मैं इसे लिखने का सबसे अच्छा तरीका ढूंढ रहा हूं।

FYI करें मैं दुकान को अधिक सामान्य बना रहा हूं:

एल्बम = आइटम

शैली = श्रेणियाँ

कलाकार = ब्रांड

यहाँ बताया गया है कि सूचकांक कैसे पुनः प्राप्त किया जाता है (MVC द्वारा निर्मित):

var items = db.Items.Include(i => i.Category).Include(i => i.Brand);

यहां बताया गया है कि आइटम को हटाने के लिए कैसे पुनर्प्राप्त किया गया है:

Item item = db.Items.Find(id);

पहला आइटम सभी आइटमों को वापस लाता है और आइटम मॉडल के अंदर श्रेणी और ब्रांड मॉडल को पॉप्युलेट करता है। दूसरा वाला, श्रेणी और ब्रांड को आबाद नहीं करता है।

मैं दूसरे को कैसे लिखूं और क्या पता लगाऊं और अंदर व्हाट्सएप करें (अधिमानतः 1 पंक्ति में) ... सैद्धांतिक रूप से - कुछ इस तरह:

Item item = db.Items.Find(id).Include(i => i.Category).Include(i => i.Brand);

अगर किसी को उदारतापूर्वक इन-कोर करने की आवश्यकता है, तो मेरा उत्तर देखें
जॉनी 5

जवाबों:


162

आपको Include()पहले उपयोग करने की आवश्यकता है , फिर परिणामस्वरूप क्वेरी से किसी एक ऑब्जेक्ट को पुनः प्राप्त करें:

Item item = db.Items
              .Include(i => i.Category)
              .Include(i => i.Brand)
              .SingleOrDefault(x => x.ItemId == id);

24
मैं वास्तव में उत्तरार्द्ध (SingleOrDefault) का उपयोग करने की सलाह दूंगा, ToList पहले सभी प्रविष्टियों को पुनः प्राप्त करेगा और फिर एक का चयन करेगा
Sander Rijken

5
यह टूट जाता है अगर हमारे पास एक समग्र प्राथमिक कुंजी है और प्रासंगिक खोज अधिभार का उपयोग कर रहे हैं।
झापोल्ड्ट

78
यह काम करेगा, लेकिन "Find" और "SingleOrDefault" का उपयोग करने के बीच अंतर है। "ढूंढें" विधि ऑब्जेक्ट को स्थानीय ट्रैक किए गए स्टोर से वापस लौटाती है यदि यह मौजूद है, तो डेटाबेस के लिए एक दौर की यात्रा से बचने के लिए, जहां "SingleOrDefault" का उपयोग किसी भी तरह से डेटाबेस के लिए क्वेरी को बाध्य करेगा।
इरावनची

3
@ इरावनची सही है। यह उपयोगकर्ता के लिए काम कर सकता है, लेकिन ऑपरेशन और इसके साइड-इफेक्ट्स फाइंड के बराबर नहीं हैं, जहां तक ​​मुझे पता है।
मेलविसन

3
वास्तव में ऑप्स प्रश्न का उत्तर नहीं देता है क्योंकि यह प्रयोग नहीं कर रहा है।
.Find

73

डेनिस का जवाब उपयोग कर रहा है Includeऔर SingleOrDefault। उत्तरार्द्ध डेटाबेस के लिए राउंड-ट्रिपिंग जाता है।

एक विकल्प, संबंधित संस्थाओं के स्पष्ट लोडिंग के लिए, के Findसाथ संयोजन में उपयोग करना Loadहै ...

MSDN उदाहरण के नीचे :

using (var context = new BloggingContext()) 
{ 
  var post = context.Posts.Find(2); 

  // Load the blog related to a given post 
  context.Entry(post).Reference(p => p.Blog).Load(); 

  // Load the blog related to a given post using a string  
  context.Entry(post).Reference("Blog").Load(); 

  var blog = context.Blogs.Find(1); 

  // Load the posts related to a given blog 
  context.Entry(blog).Collection(p => p.Posts).Load(); 

  // Load the posts related to a given blog  
  // using a string to specify the relationship 
  context.Entry(blog).Collection("Posts").Load(); 
}

बेशक, Findस्टोर से अनुरोध किए बिना तुरंत लौटता है, अगर वह इकाई पहले से ही संदर्भ से भरी हुई है।


30
Findयदि इकाई मौजूद है, तो इस पद्धति का उपयोग करता है, इकाई के लिए DB के लिए कोई गोल-यात्रा नहीं है। लेकिन, आपके पास प्रत्येक रिश्ते के लिए एक दौर-यात्रा होगी Load, जबकि SingleOrDefaultसंयोजन Includeएक ही बार में सब कुछ लोड करता है।
इरावनची

जब मैंने SQL प्रोफाइलर में 2 की तुलना की, तो ढूँढें / लोड मेरे मामले के लिए बेहतर था (मेरे पास 1: 1 संबंध था)। @ इरावनची: क्या आपके कहने का मतलब है कि अगर मेरे पास 1: मी रिलेशन होता तो इसे मी टाइम स्टोर कहा जाता? ... क्योंकि इससे कोई मतलब नहीं होता।
लर्नर

3
नहीं 1: मी संबंध, लेकिन कई रिश्ते। हर बार जब आप Loadफ़ंक्शन को कॉल करते हैं, तो कॉल वापस आने पर संबंध को पॉप्युलेट किया जाना चाहिए। इसलिए यदि आप Loadकई बार कई संबंधों के लिए कॉल करते हैं, तो हर बार एक गोल यात्रा होगी। यहां तक ​​कि एकल संबंध के लिए, अगर Findविधि को स्मृति में इकाई नहीं मिलती है, तो यह दो दौर की यात्राएं करता है: एक के लिए Findऔर दूसरा Load। लेकिन ए IncludeSingleOrDefaultदृष्टिकोण इकाई और संबंध को एक ही दिशा में ले जाता है जहां तक ​​मुझे पता है (लेकिन मुझे यकीन नहीं है)
इरावानची

1
अच्छा होता अगर कलेक्शन और रेफरेंस को अलग तरीके से ट्रीट करने के बजाय किसी तरह से डिजाइन को फॉलो किया जा सकता था। इससे GetById () मोहरा बनाना अधिक कठिन हो जाता है जो बस अभिव्यक्ति का एक वैकल्पिक संग्रह लेता है <Func <T, object >> (जैसे _repo.GetById (id, x => x.MyChlection))
डेरेक ग्रीर

4
अपनी पोस्ट के संदर्भ का उल्लेख करने का मन: msdn.microsoft.com/en-us/data/jj574232.aspx#explicit
होसैन

1

आपको IQueryable को DbSet में डालना होगा

var dbSet = (DbSet<Item>) db.Set<Item>().Include("");

return dbSet.Find(id);


DbSet में कोई .Find या .FindAsync नहीं है। क्या यह ईएफ कोर है?
थियरी

एफई कोर पर एफई 6 भी है
राफेल आर। सूजा

मैं आशान्वित था और फिर "InvalidCastException"
ZX9

0

मेरे लिए काम नहीं किया। लेकिन मैंने इसे इस तरह से हल किया।

var item = db.Items
             .Include(i => i.Category)
             .Include(i => i.Brand)
             .Where(x => x.ItemId == id)
             .First();

पता है कि अगर एक ठीक समाधान नहीं है। लेकिन दूसरे डेनिस ने मुझे एक बूल त्रुटि दी .SingleOrDefault(x => x.ItemId = id);


4
डेनिस का समाधान भी काम करना चाहिए। शायद आपके पास यह त्रुटि SingleOrDefault(x => x.ItemId = id)केवल =दोहरे के बजाय गलत एकल के कारण है ==?
सलुमा

6
हाँ, ऐसा लगता है कि आपने उपयोग नहीं किया = ==। सिंटैक्स गलती;)
राल्फ एन

मैंने उन दोनों को आज़माया == और = फिर भी मुझे एक त्रुटि दी। सिंघलऑर्डिफॉल्ट (x => x.ItemId = id); = / मेरे कोड में कुछ और होना चाहिए जो गलत है। लेकिन जिस तरह से मैंने किया वह एक बुरा तरीका है? शायद मुझे समझ में नहीं आ रहा है कि आपका क्या मतलब है डेनिस के पास कोड में एक एकल = है।
जोहान

0

खोजने के साथ फ़िल्टर करने का कोई वास्तविक आसान तरीका नहीं है। लेकिन मैं कार्यक्षमता को दोहराने के लिए एक करीब से आया हूं, लेकिन कृपया मेरे समाधान के लिए कुछ चीजों पर ध्यान दें।

यह सॉल्यूशंस आपको नेट-कोर में प्राथमिक कुंजी को जाने बिना उदारतापूर्वक फ़िल्टर करने की अनुमति देता है

  1. ढूँढें मूल रूप से अलग है क्योंकि यह इकाई को प्राप्त करता है यदि यह डेटाबेस को क्वेरी करने से पहले ट्रैकिंग में मौजूद है।

  2. इसके अतिरिक्त यह किसी ऑब्जेक्ट द्वारा फ़िल्टर कर सकता है ताकि उपयोगकर्ता को प्राथमिक कुंजी जानने की आवश्यकता न हो।

  3. यह समाधान EntityFramework Core के लिए है।

  4. इसके लिए संदर्भ तक पहुंच की आवश्यकता होती है

यहाँ कुछ विस्तार विधियाँ हैं जो प्राथमिक कुंजी द्वारा फ़िल्टर करने में आपकी सहायता करेंगी

    public static IReadOnlyList<IProperty> GetPrimaryKeyProperties<T>(this DbContext dbContext)
    {
        return dbContext.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties;
    }

    //TODO Precompile expression so this doesn't happen everytime
    public static Expression<Func<T, bool>> FilterByPrimaryKeyPredicate<T>(this DbContext dbContext, object[] id)
    {
        var keyProperties = dbContext.GetPrimaryKeyProperties<T>();
        var parameter = Expression.Parameter(typeof(T), "e");
        var body = keyProperties
            // e => e.PK[i] == id[i]
            .Select((p, i) => Expression.Equal(
                Expression.Property(parameter, p.Name),
                Expression.Convert(
                    Expression.PropertyOrField(Expression.Constant(new { id = id[i] }), "id"),
                    p.ClrType)))
            .Aggregate(Expression.AndAlso);
        return Expression.Lambda<Func<T, bool>>(body, parameter);
    }

    public static Expression<Func<T, object[]>> GetPrimaryKeyExpression<T>(this DbContext context)
    {
        var keyProperties = context.GetPrimaryKeyProperties<T>();
        var parameter = Expression.Parameter(typeof(T), "e");
        var keyPropertyAccessExpression = keyProperties.Select((p, i) => Expression.Convert(Expression.Property(parameter, p.Name), typeof(object))).ToArray();
        var selectPrimaryKeyExpressionBody = Expression.NewArrayInit(typeof(object), keyPropertyAccessExpression);

        return Expression.Lambda<Func<T, object[]>>(selectPrimaryKeyExpressionBody, parameter);
    }

    public static IQueryable<TEntity> FilterByPrimaryKey<TEntity>(this DbSet<TEntity> dbSet, DbContext context, object[] id)
        where TEntity : class
    {
        return FilterByPrimaryKey(dbSet.AsQueryable(), context, id);
    }

    public static IQueryable<TEntity> FilterByPrimaryKey<TEntity>(this IQueryable<TEntity> queryable, DbContext context, object[] id)
        where TEntity : class
    {
        return queryable.Where(context.FilterByPrimaryKeyPredicate<TEntity>(id));
    }

एक बार जब आपके पास ये विस्तार विधियाँ होती हैं, तो आप इन्हें फ़िल्टर कर सकते हैं:

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