अनाम प्रकार के परिणाम लौटाएँ?


194

नीचे दिए गए सरल उदाहरण का उपयोग करते हुए, लाइनक से एसक्यूएल का उपयोग करके कई तालिकाओं से परिणाम वापस करने का सबसे अच्छा तरीका क्या है?

कहो कि मेरे पास दो टेबल हैं:

Dogs:   Name, Age, BreedId
Breeds: BreedId, BreedName

मैं उनके साथ सभी कुत्तों को वापस करना चाहता हूं BreedName। मुझे सभी कुत्तों को कुछ इस तरह से उपयोग करना चाहिए कि कोई समस्या न हो:

public IQueryable<Dog> GetDogs()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select d;
    return result;
}

लेकिन अगर मुझे नस्लों वाले कुत्ते चाहिए और कोशिश करो कि मुझे समस्या हो:

public IQueryable<Dog> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new
                        {
                            Name = d.Name,
                            BreedName = b.BreedName
                        };
    return result;
}

अब मुझे एहसास हुआ कि संकलक मुझे अनाम प्रकार का एक सेट वापस नहीं करने देगा क्योंकि यह कुत्तों की उम्मीद कर रहा है, लेकिन क्या कस्टम प्रकार बनाने के बिना इसे वापस करने का एक तरीका है? या क्या मुझे DogsWithBreedNamesउस प्रकार का चयन करने के लिए अपनी खुद की कक्षा बनानी होगी और उस प्रकार को निर्दिष्ट करना होगा? या कोई और आसान तरीका है?


जिज्ञासा से बाहर, सभी लिनक उदाहरण अनाम प्रकारों का उपयोग करके क्यों दिखाते हैं, अगर वे काम नहीं करते हैं। जैसे, यह उदाहरण हैforeach (var cust in query) Console.WriteLine("id = {0}, City = {1}", cust.CustomerID, cust.City);
हॉट लिक्स

@Hot Licks - उन उदाहरणों में ग्राहक तालिका एक वर्ग द्वारा दर्शाई गई इकाई है। उदाहरण केवल उन वर्गों की परिभाषाओं को दिखाने के लिए प्रकट नहीं होता है।
जोनाथन एस।

न ही यह आपको बताता है कि एक संकलक स्विज़ल वर्ग नाम के साथ "var" की जगह ले रहा है।
हॉट लिप्स

जवाबों:


213

मैं इस पैटर्न के लिए जाना चाहता हूँ:

public class DogWithBreed
{
    public Dog Dog { get; set; }
    public string BreedName  { get; set; }
}

public IQueryable<DogWithBreed> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new DogWithBreed()
                        {
                            Dog = d,
                            BreedName = b.BreedName
                        };
    return result;
}

इसका मतलब है कि आपके पास एक अतिरिक्त वर्ग है, लेकिन यह त्वरित और आसानी से कोड, आसानी से एक्स्टेंसिबल, पुन: प्रयोज्य और टाइप-सुरक्षित है।


मुझे यह दृष्टिकोण पसंद है लेकिन अब मुझे यकीन नहीं है कि कुत्ते का नाम कैसे प्रदर्शित किया जाए। यदि मैं परिणाम को DataGrid के लिए बाध्य कर रहा हूं, तो क्या मुझे डॉगविथ ब्रीड क्लास में स्पष्ट रूप से परिभाषित किए बिना डॉग से गुण प्राप्त कर सकते हैं या क्या मुझे प्रत्येक फ़ील्ड के लिए गेटटर / सेटर बनाना होगा जिसे मैं प्रदर्शित करना चाहता हूं?
जोनाथन एस।

4
क्या DataGrids आपको "Dog.Name" के रूप में संपत्ति निर्दिष्ट करने की अनुमति नहीं देता है? मैं अब भूल जाता हूं कि मैं उनसे नफरत क्यों करता हूं कि उनका इस्तेमाल कभी न करें ...
Teedyay

@JonathanS। आप इसे किस तरह से टेम्पलेट कॉलम में रखते हैं? कृपया मुझे बताएं कि मैं ऐसी ही स्थिति में
हूं

अरे, मुझे दो वर्गों को एक में बदलने का यह तरीका पसंद है। इससे काम करने में आसानी होती है। केवल वर्तमान संदर्भ में उपयोग की जाने वाली सरल कक्षाएं बनाने का उल्लेख नहीं करना, इसे साफ करना है।
अभी भी ताजा

6
यह वास्तव में इस सवाल का जवाब नहीं देता है कि ओपी के पास, "कस्टम प्रकार बनाने के लिए बिना इसे वापस करने का एक तरीका है"?
tjscience

69

आप गुमनाम प्रकार लौट सकते हैं, लेकिन यह वास्तव में सुंदर नहीं है

इस मामले में मुझे लगता है कि उपयुक्त प्रकार का निर्माण करना कहीं बेहतर होगा। यदि यह केवल विधि वाले प्रकार के भीतर से उपयोग होने जा रहा है, तो इसे एक नेस्टेड प्रकार बनाएं।

व्यक्तिगत रूप से मुझे "अनाम प्रकार" प्राप्त करने के लिए C # पसंद है - यानी अनाम प्रकारों के समान व्यवहार, लेकिन नामों और संपत्ति की घोषणाओं के साथ, लेकिन यही है।

संपादित करें: अन्य लोग कुत्तों को लौटाने का सुझाव दे रहे हैं, और फिर एक संपत्ति पथ आदि के माध्यम से नस्ल के नाम का उपयोग कर रहे हैं, यह एक पूरी तरह से उचित दृष्टिकोण है, लेकिन IME यह उन स्थितियों की ओर ले जाता है जहां आपने डेटा के कारण किसी विशेष तरीके से एक क्वेरी की है। उपयोग - और वह मेटा-सूचना तब खो जाती है जब आप बस लौटते हैं IEnumerable<Dog>- क्वेरी आपसे अपेक्षा कर सकती है कि आप Breedइसके बजाय (कहने) का उपयोग करेंOwner कुछ लोड विकल्प आदि की वजह से है, लेकिन अगर आपको लगता है कि भूल जाते हैं और अन्य संपत्तियों का उपयोग शुरू, अपने अनुप्रयोग काम कर सकते हैं लेकिन उतनी कुशलता से नहीं, जितना आपने मूल रूप से परिकल्पित किया था। बेशक, मैं बकवास बात कर सकता हूं, या अति-अनुकूलन, आदि ...


3
अरे, मैं डर के कारण सुविधाओं से नहीं चाहता हूं कि वे जिस तरह से दुर्व्यवहार करेंगे, लेकिन क्या आप उन कूट-कूटों के प्रकारों की कल्पना कर सकते हैं, जिन्हें हम देखते हैं कि क्या वे नाम के प्रकारों को पारित होने की अनुमति देते हैं? (कंपकंपी)
डेव मार्कल

19
हम कुछ दुरुपयोग देख सकते हैं। हम कुछ बहुत सरल कोड भी देख सकते हैं, जहाँ हम मूल रूप से एक टपल चाहते हैं। सब कुछ जटिल व्यवहार के साथ एक वस्तु होने की जरूरत नहीं है। कभी-कभी "बस डेटा" राइट थिंग है। आईएमओ, ज़ाहिर है।
जॉन स्कीट

1
धन्यवाद, इसलिए आपकी प्राथमिकता इस तरह के एक-बंद दृश्य के लिए भी प्रकार बनाने की है? मेरे पास बहुत सारी रिपोर्टें हैं जो एक ही डेटा को अलग-अलग तरीके से स्लाइस करती हैं और उम्मीद कर रही थी कि ये सभी अलग-अलग तरह के (डॉगसविथ ब्रीड्स, डॉग्सविथऑनरनाम, आदि) नहीं बनाएंगे
जोनाथन एस।

1
मैं इसे बहुत सारे तरीकों से स्लाइस करने की आवश्यकता नहीं है, या स्लाइसिंग भाग को उस स्थान पर रख सकता हूं जिसे डेटा की आवश्यकता है ताकि आप गुमनाम प्रकारों का उपयोग कर सकें - लेकिन इसके अलावा, हाँ। यह कुछ मायनों में बेकार है, लेकिन ऐसा जीवन है जो मुझे डर लगता है :(
जॉन स्कीट

17

बस अपने दो सेंट जोड़ने के लिए :-) मैंने हाल ही में गुमनाम वस्तुओं को संभालने का एक तरीका सीखा है। इसका उपयोग केवल .NET 4 फ्रेमवर्क को लक्षित करते समय किया जा सकता है और यह केवल System.Web.dll के संदर्भ में जोड़ने पर होता है, लेकिन तब यह काफी सरल है:

...
using System.Web.Routing;
...

class Program
{
    static void Main(string[] args)
    {

        object anonymous = CallMethodThatReturnsObjectOfAnonymousType();
        //WHAT DO I DO WITH THIS?
        //I know! I'll use a RouteValueDictionary from System.Web.dll
        RouteValueDictionary rvd = new RouteValueDictionary(anonymous);
        Console.WriteLine("Hello, my name is {0} and I am a {1}", rvd["Name"], rvd["Occupation"]);
    }

    private static object CallMethodThatReturnsObjectOfAnonymousType()
    {
        return new { Id = 1, Name = "Peter Perhac", Occupation = "Software Developer" };
    }
}

System.Web.dll का संदर्भ जोड़ने में सक्षम होने के लिए आपको रशोनरोक की सलाह का पालन ​​करना होगा : सुनिश्चित करें कि आपका [प्रोजेक्ट का] टारगेट फ्रेमवर्क ".NET फ्रेमवर्क 4" है ".NET फ्रेमवर्क 4 क्लाइंट प्रोफाइल" नहीं।


2
ASP.NET Mvc School;)
T-moty

8

नहीं, आप कुछ प्रवंचनाओं से गुज़रे बिना गुमनाम प्रकार नहीं लौटा सकते।

यदि आप C # का उपयोग नहीं कर रहे थे, तो आप जिस चीज की तलाश कर रहे थे (एक ठोस प्रकार के बिना कई डेटा लौटाते हैं) को ट्यूपल कहा जाता है।

यहाँ पर दिखाए गए एक का उपयोग करके C # tuple कार्यान्वयन की एक बहुत कुछ है , आपका कोड इस तरह काम करेगा।

public IEnumerable<Tuple<Dog,Breed>> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new Tuple<Dog,Breed>(d, b);

    return result;
}

और कॉलिंग साइट पर:

void main() {
    IEnumerable<Tuple<Dog,Breed>> dogs = GetDogsWithBreedNames();
    foreach(Tuple<Dog,Breed> tdog in dogs)
    {
        Console.WriteLine("Dog {0} {1}", tdog.param1.Name, tdog.param2.BreedName);
    }
}

7
यह काम नहीं करता। NotSupportedException को फेंकता है : LINQ में केवल पैरामीटर रहित कन्स्ट्रक्टर और इनिशियलाइज़र का उपयोग एंटिटीज़ में किया जाता है
mshsayem

1
सच है, टपल क्लास में डिफ़ॉल्ट कंस्ट्रक्टर नहीं है और इस तरह से LINQ से लेकर Entities तक सही तरीके से काम नहीं करेगा। हालांकि यह प्रश्न में LINQ से SQL के साथ ठीक काम करता है। मैंने यह कोशिश नहीं की है, लेकिन यह साथ काम कर सकता है ... select Tuple.Create(d, b)
जॉस्पर

1
चूंकि टुपल्स कुछ LINQ प्रदाताओं द्वारा समर्थित नहीं हैं, इसलिए आप एक अनाम प्रकार का चयन नहीं कर सकते, इसे IEnumerable में परिवर्तित कर सकते हैं, फिर उसमें से एक tuple चुनें?
19

8

आप ऐसा कुछ कर सकते हैं:


public System.Collections.IEnumerable GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new
                        {
                            Name = d.Name,
                            BreedName = b.BreedName
                        };
    return result.ToList();
}

8

ToList()डेटाबेस से पंक्तियों को लेने के लिए आपको पहले विधि का उपयोग करना होगा और फिर एक वर्ग के रूप में आइटम का चयन करना होगा। इसे इस्तेमाल करे:

public partial class Dog {
    public string BreedName  { get; set; }}

List<Dog> GetDogsWithBreedNames(){
    var db = new DogDataContext(ConnectString);
    var result = (from d in db.Dogs
                  join b in db.Breeds on d.BreedId equals b.BreedId
                  select new
                  {
                      Name = d.Name,
                      BreedName = b.BreedName
                  }).ToList()
                    .Select(x=> 
                          new Dog{
                              Name = x.Name,
                              BreedName = x.BreedName,
                          }).ToList();
return result;}

तो, चाल पहले हैToList() । यह तुरंत क्वेरी बनाता है और डेटाबेस से डेटा प्राप्त करता है। दूसरी ट्रिक है आइटमों का चयन करना और ऑब्जेक्ट इनिशियलज़र का उपयोग करना लोड की गई वस्तुओं के साथ नई वस्तुओं को उत्पन्न लिए ।

उम्मीद है की यह मदद करेगा।


8

C # 7 में अब आप tuples का उपयोग कर सकते हैं! ... जो परिणाम को वापस करने के लिए केवल एक वर्ग बनाने की आवश्यकता को समाप्त करता है।

यहाँ एक नमूना कोड है:

public List<(string Name, string BreedName)> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
             join b in db.Breeds on d.BreedId equals b.BreedId
             select new
             {
                Name = d.Name,
                BreedName = b.BreedName
             }.ToList();

    return result.Select(r => (r.Name, r.BreedName)).ToList();
}

हालाँकि आपको System.ValueTuple nuget पैकेज स्थापित करने की आवश्यकता हो सकती है।


4

अब मुझे एहसास हुआ कि संकलक मुझे अनाम प्रकार का एक सेट वापस नहीं करने देगा क्योंकि यह कुत्तों की अपेक्षा कर रहा है, लेकिन क्या कस्टम प्रकार बनाने के बिना इसे वापस करने का कोई तरीका है?

कस्टम प्रकार बनाए बिना अनाम प्रकारों की सूची वापस करने के लिए ऑब्जेक्ट ऑब्जेक्ट का उपयोग करें । यह संकलक त्रुटि (.net 4.0 में) के बिना काम करेगा। मैंने ग्राहक को सूची लौटा दी और फिर उसे जावास्क्रिप्ट पर पार्स किया:

public object GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new
                        {
                            Name = d.Name,
                            BreedName = b.BreedName
                        };
    return result;
}

1
मेरे विचार में यह अधिक सही और पठनीय होगा यदि आपकी विधि हस्ताक्षर इस तरह दिखे: सार्वजनिक IEnumerable <object> GetDogsWithBreedNames ()
पिस्तौल-पीट

3

बस कुत्तों का चयन करें, फिर उपयोग करें dog.Breed.BreedName , यह ठीक काम करना चाहिए।

यदि आपके पास बहुत सारे कुत्ते हैं, तो db कॉल्स की संख्या कम करने के लिए DataLoadOptions.LoadWith का उपयोग करें।


2

आप गुमनाम प्रकारों को सीधे नहीं लौटा सकते हैं, लेकिन आप उन्हें अपने सामान्य तरीके से पा सकते हैं। इसलिए अधिकांश LINQ एक्सटेंशन मेथड्स करें। वहां कोई जादू नहीं है, जबकि ऐसा लग रहा है कि वे गुमनाम प्रकार लौटेंगे। यदि पैरामीटर अनाम है तो परिणाम भी अनाम हो सकता है।

var result = Repeat(new { Name = "Foo Bar", Age = 100 }, 10);

private static IEnumerable<TResult> Repeat<TResult>(TResult element, int count)
{
    for(int i=0; i<count; i++)
    {
        yield return element;
    }
}

मूल प्रश्न से कोड के आधार पर एक उदाहरण के नीचे:

var result = GetDogsWithBreedNames((Name, BreedName) => new {Name, BreedName });


public static IQueryable<TResult> GetDogsWithBreedNames<TResult>(Func<object, object, TResult> creator)
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                    join b in db.Breeds on d.BreedId equals b.BreedId
                    select creator(d.Name, b.BreedName);
    return result;
}

0

ठीक है, यदि आप कुत्तों को वापस कर रहे हैं, तो आप करेंगे:

public IQueryable<Dog> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    return from d in db.Dogs
           join b in db.Breeds on d.BreedId equals b.BreedId
           select d;
}

यदि आप चाहते हैं कि ब्रीड उत्सुक-लोडेड हो और आलसी-लोडेड न हो, तो बस उपयुक्त डेटालैडऑक्शन निर्माण का उपयोग करें ।


0

BreedIdDogतालिका में स्पष्ट रूप से तालिका में संबंधित पंक्ति के लिए एक विदेशी कुंजी है Breed। यदि आपने अपना डेटाबेस ठीक से सेट कर लिया है, तो LINQ to SQL को स्वचालित रूप से दो तालिकाओं के बीच एक जुड़ाव बनाना चाहिए। परिणामी डॉग क्लास में ब्रीड प्रॉपर्टी होगी, और ब्रीड क्लास में डॉग्स का कलेक्शन होना चाहिए। इसे इस तरह सेट करना, आप अभी भी वापस आ सकते हैं IEnumerable<Dog>, जो एक ऐसी वस्तु है जिसमें नस्ल की संपत्ति शामिल है। एकमात्र चेतावनी यह है कि आपको क्वेरी ऑब्जेक्ट को डॉग ऑब्जेक्ट्स के साथ क्वेरी में प्रीलोड करने की आवश्यकता है ताकि डेटा संदर्भ के निपटारे के बाद उन्हें एक्सेस किया जा सके, और (जैसा कि किसी अन्य पोस्टर ने सुझाव दिया है) संग्रह पर एक विधि निष्पादित करें जो कारण होगा क्वेरी तुरंत की जाएगी (इस मामले में सुधार करें):

public IEnumerable<Dog> GetDogs()
{
    using (var db = new DogDataContext(ConnectString))
    {
        db.LoadOptions.LoadWith<Dog>(i => i.Breed);
        return db.Dogs.ToArray();
    }

}

फिर प्रत्येक कुत्ते के लिए नस्ल तक पहुंच बनाना तुच्छ है:

foreach (var dog in GetDogs())
{
    Console.WriteLine("Dog's Name: {0}", dog.Name);
    Console.WriteLine("Dog's Breed: {0}", dog.Breed.Name);        
}

0

यदि मुख्य विचार डेटाबेस सर्वर को भेजे गए SQL चयन स्टेटमेंट को केवल आवश्यक फ़ील्ड के लिए है, और सभी एंटिटी फ़ील्ड में नहीं है, तो आप यह कर सकते हैं:

public class Class1
{
    public IList<Car> getCarsByProjectionOnSmallNumberOfProperties()
    {

        try
        {
            //Get the SQL Context:
            CompanyPossessionsDAL.POCOContext.CompanyPossessionsContext dbContext 
                = new CompanyPossessionsDAL.POCOContext.CompanyPossessionsContext();

            //Specify the Context of your main entity e.g. Car:
            var oDBQuery = dbContext.Set<Car>();

            //Project on some of its fields, so the created select statment that is
            // sent to the database server, will have only the required fields By making a new anonymouse type
            var queryProjectedOnSmallSetOfProperties 
                = from x in oDBQuery
                    select new
                    {
                        x.carNo,
                        x.eName,
                        x.aName
                    };

            //Convert the anonymouse type back to the main entity e.g. Car
            var queryConvertAnonymousToOriginal 
                = from x in queryProjectedOnSmallSetOfProperties
                    select new Car
                    {
                        carNo = x.carNo,
                        eName = x.eName,
                        aName = x.aName
                    };

            //return the IList<Car> that is wanted
            var lst = queryConvertAnonymousToOriginal.ToList();
            return lst;

        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.ToString());
            throw;
        }
    }
}

0

डायनेमिक डेटा प्राप्त करने के लिए यह प्रयास करें। आप सूची के लिए कोड को परिवर्तित कर सकते हैं <>

public object GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new
                        {
                            Name = d.Name,
                            BreedName = b.BreedName
                        };
    return result.FirstOrDefault();
}

dynamic dogInfo=GetDogsWithBreedNames();
var name = dogInfo.GetType().GetProperty("Name").GetValue(dogInfo, null);
var breedName = dogInfo.GetType().GetProperty("BreedName").GetValue(dogInfo, null);

0

यदि आपके पास अपने डेटाबेस में रिलेशनशिप सेटअप है, तो BreedId पर foriegn key संयम के साथ आपको वह पहले से नहीं मिलता है?

DBML संबंध मानचित्रण

इसलिए मैं अब कॉल कर सकता हूं:

internal Album GetAlbum(int albumId)
{
    return Albums.SingleOrDefault(a => a.AlbumID == albumId);
}

और उस कोड में जो कॉल करता है:

var album = GetAlbum(1);

foreach (Photo photo in album.Photos)
{
    [...]
}

तो आपके उदाहरण में आप कुत्ते की तरह कुछ कह रहे होंगे। नस्ल.नाम - जैसा कि मैंने कहा, यह आपके डेटाबेस पर निर्भर करता है कि इन संबंधों के साथ स्थापित किया जा रहा है।

जैसा कि अन्य लोगों ने उल्लेख किया है, DataLoadOptions डेटाबेस कॉल को कम करने में मदद करेगा यदि यह एक समस्या है।


0

यह आपके प्रश्न का सटीक उत्तर नहीं देता है, लेकिन Google ने मुझे यहां खोजशब्दों के आधार पर आगे बढ़ाया। यह है कि आप किसी सूची से एक अनाम प्रकार की क्वेरी कैसे कर सकते हैं:

var anon = model.MyType.Select(x => new { x.Item1, x.Item2});
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.