Zwraca anonimowe wyniki typu?

Używając prostego przykładu poniżej, jaki jest najlepszy sposób zwracania wyników z wielu tabel za pomocą Linq do SQL?

Powiedzmy, że mam dwie tabele:

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

Chcę zwrócić wszystkie psy z ich BreedName. Wszystkie psy powinny używać czegoś takiego bez żadnych problemów:

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;
}

Ale jeśli chcę mieć psy z rasami i spróbować to mam problemy:

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;
}

Teraz zdaję sobie sprawę, że kompilator nie pozwala mi zwrócić zestawu anonimowych typów, ponieważ oczekuje psów, ale czy istnieje sposób na zwrócenie tego bez konieczności tworzenia niestandardowego typu? Czy też muszę utworzyć własną klasę dla DogsWithBreedNames i określić ten typ w select? A może jest inny łatwiejszy sposób?

Author: shA.t, 2009-02-11

14 answers

Wybieram ten wzór:

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;
}

Oznacza to, że masz dodatkową klasę, ale jest szybka i łatwa w kodowaniu, łatwo rozszerzalna, wielokrotnego użytku i bezpieczna dla typu.

 188
Author: teedyay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-10 23:35:56

Ty możesz zwracać anonimowe typy, ale to naprawdę nie jest ładne .

W tym przypadku myślę, że o wiele lepiej byłoby stworzyć odpowiedni typ. Jeśli ma być używany tylko z typu zawierającego metodę, niech będzie typem zagnieżdżonym.

Osobiście chciałbym, aby C# otrzymywało "nazwane typy anonimowe" - tzn. takie samo zachowanie jak typy anonimowe, ale z nazwami i deklaracjami własności, ale to wszystko.

EDIT: inni sugerują powrót psów i następnie dostęp do nazwy rasy poprzez ścieżkę właściwości itp. Jest to całkowicie rozsądne podejście, ale IME prowadzi do sytuacji, w których wykonałeś zapytanie w określony sposób ze względu na dane, których chcesz użyć - i że meta-informacje są tracone, gdy po prostu zwracasz IEnumerable<Dog> - zapytanie może być oczekując , że użyjesz (powiedzmy) Breed, a nie Owner z powodu niektórych opcji ładowania itp., ale jeśli o tym zapomnisz i zaczniesz używać innych właściwości, Twoja aplikacja może działać, ale nie tak wydajnie, jak pierwotnie / align = "left" / Oczywiście, mogę mówić bzdury, lub nadmiernej optymalizacji, itp...

 66
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-21 07:15:24

Żeby dodać moje dwa grosze :-) Niedawno nauczyłem się obsługi anonimowych obiektów. Może być używany tylko podczas kierowania. NET 4 framework i że tylko podczas dodawania odniesienia do systemu.Www.dll, ale to dość proste:

...
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" };
    }
}

Aby móc dodać odniesienie do systemu.Www.dll będziesz musiał postępować zgodnie z porad rushonerok : Upewnij się, że docelowy framework twojego projektu to". NET Framework 4", a nie". NET Framework 4 Client Profile".

 16
Author: Peter Perháč,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:48

Nie możesz zwracać anonimowych typów bez przechodzenia przez jakieś sztuczki.

Jeśli nie używasz C#, to czego szukasz (zwracanie wielu danych bez konkretnego typu) nazywa się Krotką.

Istnieje wiele implementacji C# tuple, używając tej pokazanej tutaj , Twój kod będzie działał w ten sposób.

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;
}

I na stronie wywołującej:

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
Author: joshperry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-29 14:48:29

Najpierw musisz użyć metody ToList(), aby pobierać wiersze z bazy danych, a następnie wybierać pozycje jako klasę. Spróbuj tego:

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;}

Więc, sztuczka jest Pierwsza ToList(). To jest natychmiast sprawia, że zapytanie i pobiera dane z bazy danych. Druga sztuczka to Wybieranie elementów i używanie narzędzia object initializer do generowania nowych obiektów z załadowanymi elementami.

Mam nadzieję, że to pomoże.
 7
Author: Hakan KOSE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-18 07:21:44

Mógłbyś zrobić coś takiego:


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();
}
 6
Author: tjscience,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-13 20:03:54

teraz zdaję sobie sprawę, że kompilator nie pozwala mi zwrócić zestawu anonimowych typów, ponieważ oczekuje psów, ale czy istnieje sposób, aby zwrócić to bez konieczności tworzenia niestandardowego typu?

Użyj use object , aby zwrócić listę anonimowych typów bez tworzenia własnego typu. Będzie to działać bez błędu kompilatora (w.Net 4.0). Zwróciłem listę do klienta, a następnie przetworzyłem ją na JavaScript:

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;
}
 4
Author: Sergey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-09 16:38:09

Po prostu wybierz psy, a następnie użyj dog.Breed.BreedName, powinno działać dobrze.

Jeśli masz dużo psów, użyj DataLoadOptions.LoadWith w celu zmniejszenia liczby wywołań db.

 3
Author: Andrey Shchekin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-10 23:28:50

Nie można zwracać typów anonimowych bezpośrednio, ale można je zapętlić za pomocą metody generycznej. Podobnie jak większość metod rozszerzenia LINQ. Nie ma tam żadnej magii, a wygląda na to, że zwracają anonimowe typy. Jeśli parametr jest anonimowy, wynik może być również anonimowy.

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;
    }
}

Poniżej przykład oparty na kodzie z oryginalnego pytania:

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;
}
 2
Author: George Mamaladze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 20:58:31

Jeśli masz w swojej bazie danych układ relacji z kluczem foriegn na BreedId, to już tego nie rozumiesz?

Mapowanie relacji DBML http://www.doodle.co.uk/userfiles/image/relationship.png

Więc mogę teraz zadzwonić:

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

I w kodzie, który wywołuje, że:

var album = GetAlbum(1);

foreach (Photo photo in album.Photos)
{
    [...]
}
Więc w Twoim przypadku nazywałbyś coś jak pies.Rasa.BreedName - jak już mówiłem, zależy to od tego, że Twoja baza danych jest skonfigurowana z tymi relacjami.

Jako inni wspominali, że DataLoadOptions pomoże zmniejszyć wywołania bazy danych, jeśli jest to problem.

 1
Author: Zhaph - Ben Duguid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-10 23:34:07

W C # 7 możesz teraz używać krotek!... co eliminuje potrzebę tworzenia klasy tylko po to, aby zwrócić wynik.

Oto przykładowy kod:

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();
}

Być może trzeba będzie zainstalować System.ValueTuple pakiet nuget chociaż.

 1
Author: Rosdi Kasim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-29 07:51:10

Jeśli zwracasz psy, to zrobisz:

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;
}

Jeśli chcesz, aby rasa była chętna, a nie leniwa, użyj odpowiedniej konstrukcjiDataLoadOptions .

 0
Author: Dave Markle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-10 23:14:49

BreedId w tabeli Dog jest oczywiście kluczem obcym do odpowiedniego wiersza w tabeli Breed. Jeśli masz poprawnie skonfigurowaną bazę danych, LINQ do SQL powinien automatycznie utworzyć skojarzenie między dwiema tabelami. Wynikowa Klasa psów będzie miała własność rasy, A Klasa rasy powinna mieć kolekcję psów. Ustawiając go w ten sposób, nadal możesz zwrócić IEnumerable<Dog>, który jest obiektem zawierającym właściwość rasy. Jedynym zastrzeżeniem jest to, że musisz wstępnie załadować obiekt rasy wraz z obiektami dog w zapytaniu, aby mogły być dostępne po usunięciu kontekstu danych i (jak sugerował inny plakat) wykonaj metodę na kolekcji, która spowoduje natychmiastowe wykonanie zapytania (w tym przypadku ToArray):

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

}

Jest wtedy trywialne, aby uzyskać dostęp do rasy dla każdego psa:

foreach (var dog in GetDogs())
{
    Console.WriteLine("Dog's Name: {0}", dog.Name);
    Console.WriteLine("Dog's Breed: {0}", dog.Breed.Name);        
}
 0
Author: kad81,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-03 23:34:20

Jeśli głównym założeniem jest, aby polecenie SQL select wysłane do serwera bazy danych zawierało tylko wymagane pola, a nie wszystkie pola encji, to u można to zrobić:

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
Author: Reader Man San,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-04 17:23:51