Vraag Anoniem-type resultaten retourneren?


Gebruik makend van het eenvoudige onderstaande voorbeeld, wat is de beste manier om resultaten uit meerdere tabellen te retourneren met behulp van Linq to SQL?

Stel dat ik twee tabellen heb:

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

Ik wil alle honden met hun terugbrengen BreedName. Ik zou ervoor moeten zorgen dat alle honden zoiets zonder problemen gebruiken:

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

Maar als ik honden met rassen wil en dit probeer, heb ik problemen:

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

Nu realiseer ik me dat de compiler me geen reeks anonieme typen laat retourneren omdat het honden verwacht, maar is er een manier om dit terug te zetten zonder een aangepast type te hoeven maken? Of moet ik mijn eigen klas maken voor DogsWithBreedNames en specificeer dat type in de selectie? Of is er nog een eenvoudiger manier?


175
2018-02-10 23:10


oorsprong


antwoorden:


Ik heb de neiging om voor dit patroon te gaan:

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

Het betekent dat je een extra klasse hebt, maar het is snel en gemakkelijk te coderen, gemakkelijk uitbreidbaar, herbruikbaar en typeveilig.


186
2018-02-10 23:35



U kan terugkeer anonieme types, maar het is echt niet mooi.

In dit geval denk ik dat het veel beter zou zijn om het juiste type te maken. Als het alleen van binnen het type dat de methode bevat wordt gebruikt, maak het dan een genest type.

Persoonlijk zou ik willen dat C # "benoemde anonieme typen" krijgt - d.w.z. hetzelfde gedrag als anonieme typen, maar met namen en eigendomsverklaringen, maar meer dan dat.

EDIT: Anderen suggereren terugkerende honden, en dan toegang tot de rasnaam via een eigenschappenpad enz. Dat is een volkomen redelijke benadering, maar IME leidt tot situaties waarin je op een bepaalde manier een vraag hebt gedaan vanwege de gegevens die je wilt gebruik - en dat meta-informatie verloren gaat als je net terugkomt IEnumerable<Dog> - de vraag kan zijn ervan uitgaand je moet (laten we zeggen) gebruiken Breed liever dan Ownervanwege sommige laadopties, enz., maar als je dat vergeet en andere eigenschappen begint te gebruiken, werkt je app mogelijk niet zo efficiënt als je oorspronkelijk had gedacht. Natuurlijk kan ik over vuilnis praten of te veel optimaliseren, enz ...


66
2018-02-10 23:15



Gewoon om mijn twee cent toe te voegen :-) Ik heb onlangs een manier geleerd om anonieme objecten te behandelen. Het kan alleen worden gebruikt bij het targeten van het .NET 4-framework en alleen als er een verwijzing naar System.Web.dll wordt toegevoegd, maar dan is het vrij eenvoudig:

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

Om een ​​verwijzing naar System.Web.dll te kunnen toevoegen, moet je het volgen het advies van rushonerok : Zorg ervoor dat uw [project] doelkader is ".NET Framework 4" niet ".NET Framework 4 Client Profile".


16
2018-01-07 11:20



Je moet gebruiken ToList() methode eerst om rijen uit de database te nemen en vervolgens items als een klasse te selecteren. Probeer dit:

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

Dus, de truc is eerste ToList(). Het maakt meteen de query en haalt de gegevens uit de database. De tweede slag is Items selecteren en de initialisatie van objecten gebruiken om nieuwe objecten te genereren met geladen items.

Ik hoop dat dit helpt.


8
2018-04-13 09:02



Nee, je kunt geen anonieme types retourneren zonder een paar bedrieglijke dingen te doen.

Als u C # niet gebruikt, wordt wat u zoekt (meerdere gegevens retourneren zonder een concreet type) een Tuple genoemd.

Er zijn veel C # tuple-implementaties, met behulp van de ene hier getoond, je code zou zo werken.

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

En op de aanroepende site:

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
2018-02-10 23:27



Je zou zoiets als dit kunnen doen:


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
2017-10-13 20:03



Nu realiseer ik me dat de compiler me geen reeks anonieme typen laat retourneren omdat het honden verwacht, maar is er een manier om dit terug te zetten zonder een aangepast type te hoeven maken?

Gebruik gebruiken voorwerp om een ​​lijst met anonieme typen te retourneren zonder een aangepast type te maken. Dit werkt zonder de compileerfout (in .net 4.0). Ik heb de lijst teruggestuurd naar de client en deze vervolgens in JavaScript geparseerd:

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
2018-04-09 16:38



Selecteer gewoon honden en gebruik dan dog.Breed.BreedName, dit zou goed moeten werken.

Als u veel honden heeft, gebruikt u DataLoadOptions.LoadWith om het aantal db-oproepen te verminderen.


3
2018-02-10 23:28



U kunt anonieme typen niet rechtstreeks retourneren, maar u kunt ze door uw generieke methode lussen. Dat geldt ook voor de meeste LINQ-uitbreidingsmethoden. Er zit geen magie in, terwijl het erop lijkt dat ze anonieme types zouden teruggeven. Als parameter anoniem is, kan resultaat ook anoniem zijn.

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

Hieronder een voorbeeld op basis van de code van de oorspronkelijke vraag:

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
2017-07-24 20:58



Als u een relatie-instelling in uw database hebt met een foriegn-sleutelbeperking op BreedId, krijgt u dat dan niet?

DBML-relatietoewijzing http://www.doodle.co.uk/userfiles/image/relationship.png

Dus ik kan nu bellen:

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

En in de code die dat roept:

var album = GetAlbum(1);

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

Dus in jouw geval zou je zoiets als een hond noemen. Breed.BreedName - zoals ik al zei, dit is afhankelijk van het feit dat je database is opgezet met deze relaties.

Zoals anderen al hebben vermeld, kunnen de DataLoadOptions de database-oproepen verminderen als dat een probleem is.


1
2018-02-10 23:34