En utilisant l'exemple simple ci-dessous, quelle est la meilleure façon de renvoyer les résultats de plusieurs tables à l'aide de Linq vers SQL?
Disons que j'ai deux tables:
Dogs: Name, Age, BreedId
Breeds: BreedId, BreedName
Je veux rendre tous les chiens avec leur BreedName
. Je devrais obliger tous les chiens à utiliser quelque chose comme ça sans problème:
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;
}
Mais si je veux des chiens de races et que j'essaye, j'ai des problèmes:
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;
}
Maintenant, je me rends compte que le compilateur ne me permet pas de renvoyer un ensemble de types anonymes car il attend des chiens, mais existe-t-il un moyen de le retourner sans avoir à créer un type personnalisé? Ou dois-je créer ma propre classe pour DogsWithBreedNames
et spécifier ce type dans la sélection? Ou existe-t-il un autre moyen plus simple?
c#
linq
linq-to-sql
Jonathan S.
la source
la source
foreach (var cust in query) Console.WriteLine("id = {0}, City = {1}", cust.CustomerID, cust.City);
Réponses:
J'ai tendance à opter pour ce modèle:
Cela signifie que vous avez une classe supplémentaire, mais elle est rapide et facile à coder, facilement extensible, réutilisable et sécurisée.
la source
Vous pouvez renvoyer des types anonymes, mais ce n'est vraiment pas joli .
Dans ce cas, je pense qu'il serait bien préférable de créer le type approprié. S'il ne doit être utilisé qu'à partir du type contenant la méthode, faites-en un type imbriqué.
Personnellement, j'aimerais que C # obtienne des "types anonymes nommés" - c'est-à-dire le même comportement que les types anonymes, mais avec des noms et des déclarations de propriété, mais c'est tout.
EDIT: D'autres suggèrent de retourner les chiens, puis d'accéder au nom de la race via un chemin de propriété, etc. C'est une approche parfaitement raisonnable, mais IME cela conduit à des situations où vous avez fait une requête d'une manière particulière en raison des données que vous souhaitez utiliser - et que les méta-informations sont perdues lorsque vous revenez juste
IEnumerable<Dog>
- la requête peut s'attendre à ce que vous utilisiez (disons)Breed
plutôt qu'enOwner
raison de certaines options de chargement, etc., mais si vous l'oubliez et commencez à utiliser d'autres propriétés, votre application peut fonctionner mais pas aussi efficacement que vous l'aviez prévu à l'origine. Bien sûr, je pourrais parler de détritus, ou de sur-optimisation, etc ...la source
Juste pour ajouter ma valeur de deux cents :-) J'ai récemment appris une façon de gérer des objets anonymes. Il ne peut être utilisé que lors du ciblage du framework .NET 4 et uniquement lors de l'ajout d'une référence à System.Web.dll, mais c'est assez simple:
Pour pouvoir ajouter une référence à System.Web.dll, vous devrez suivre les conseils de rushonerok : Assurez-vous que le cadre cible de votre [projet] est ".NET Framework 4" et non ".NET Framework 4 Client Profile".
la source
Non, vous ne pouvez pas retourner de types anonymes sans passer par une supercherie.
Si vous n'utilisiez pas C #, ce que vous recherchez (renvoyer plusieurs données sans type concret) s'appelle un Tuple.
Il existe de nombreuses implémentations de tuple C #, en utilisant celle illustrée ici , votre code fonctionnerait comme ceci.
Et sur le site appelant:
la source
... select Tuple.Create(d, b)
.Vous pouvez faire quelque chose comme ça:
la source
Vous devez d'abord utiliser la
ToList()
méthode pour prendre des lignes de la base de données, puis sélectionner les éléments en tant que classe. Essaye ça:Donc, l'astuce est la première
ToList()
. Il effectue immédiatement la requête et obtient les données de la base de données. La deuxième astuce consiste à sélectionner des éléments et à utiliser l'initialiseur d'objets pour générer de nouveaux objets avec des éléments chargés.J'espère que cela t'aides.
la source
En C # 7, vous pouvez maintenant utiliser des tuples! ... ce qui élimine le besoin de créer une classe juste pour retourner le résultat.
Voici un exemple de code:
Vous devrez peut-être installer le package de nuget System.ValueTuple.
la source
Maintenant, je me rends compte que le compilateur ne me permet pas de renvoyer un ensemble de types anonymes car il attend des chiens, mais existe-t-il un moyen de le retourner sans avoir à créer un type personnalisé?
Utilisez use object pour renvoyer une liste de types anonymes sans créer de type personnalisé. Cela fonctionnera sans l'erreur du compilateur (dans .net 4.0). J'ai renvoyé la liste au client, puis je l'ai analysée sur JavaScript:
la source
Sélectionnez simplement des chiens, puis utilisez
dog.Breed.BreedName
, cela devrait fonctionner correctement.Si vous avez beaucoup de chiens, utilisez DataLoadOptions.LoadWith pour réduire le nombre d'appels db.
la source
Vous ne pouvez pas retourner directement des types anonymes, mais vous pouvez les boucler via votre méthode générique. Tout comme la plupart des méthodes d'extension LINQ. Il n'y a pas de magie là-dedans, alors qu'il semble qu'ils renverraient des types anonymes. Si le paramètre est anonyme, le résultat peut également être anonyme.
Ci-dessous un exemple basé sur le code de la question d'origine:
la source
Eh bien, si vous retournez des chiens, vous feriez:
Si vous voulez que Breed soit chargé et non paresseux, utilisez simplement la construction DataLoadOptions appropriée .
la source
BreedId
dans leDog
tableau est évidemment une clé étrangère vers la ligne correspondante dans leBreed
tableau. Si votre base de données est correctement configurée, LINQ to SQL doit automatiquement créer une association entre les deux tables. La classe Dog résultante aura une propriété Breed, et la classe Breed devrait avoir une collection Dogs. En le configurant de cette façon, vous pouvez toujours revenirIEnumerable<Dog>
, qui est un objet qui inclut la propriété de la race. La seule mise en garde est que vous devez précharger l'objet de race avec les objets de chien dans la requête afin qu'ils soient accessibles après la suppression du contexte de données et (comme une autre affiche l'a suggéré) exécuter une méthode sur la collection qui provoquera la requête à effectuer immédiatement (ToArray dans ce cas):Il est alors trivial d'accéder à la race pour chaque chien:
la source
Si l'idée principale est de faire en sorte que l'instruction SQL select envoyée au serveur de base de données ne contienne que les champs obligatoires et pas tous les champs Entity, alors vous pouvez le faire:
la source
Essayez ceci pour obtenir des données dynamiques. Vous pouvez convertir le code de la liste <>
la source
Si vous avez une configuration de relation dans votre base de données avec une restriction de clé foriegn sur BreedId, ne l'avez-vous pas déjà?
Je peux donc maintenant appeler:
Et dans le code qui appelle ça:
Donc, dans votre cas, vous appelleriez quelque chose comme dog.Breed.BreedName - comme je l'ai dit, cela dépend de la configuration de votre base de données avec ces relations.
Comme d'autres l'ont mentionné, les DataLoadOptions aideront à réduire les appels de base de données en cas de problème.
la source
Cela ne répond pas exactement à votre question, mais Google m'a conduit ici en fonction des mots clés. Voici comment interroger un type anonyme dans une liste:
la source