Retourne les résultats de type anonyme?

En utilisant l’exemple simple ci-dessous, quelle est la meilleure façon de retourner les résultats de plusieurs tables à l’aide de Linq to SQL?

Disons que j’ai deux tables:

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

Je veux retourner tous les chiens avec leur BreedName . Je devrais faire en sorte que tous les chiens utilisent quelque chose comme ça sans aucun problème:

 public IQueryable GetDogs() { var db = new DogDataContext(ConnectSsortingng); 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 avec des races et que j’essaye ça, j’ai des problèmes:

 public IQueryable GetDogsWithBreedNames() { var db = new DogDataContext(ConnectSsortingng); 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 laissera pas retourner un ensemble de types anonymes car il attend des chiens, mais y a-t-il un moyen de retourner ceci 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 facile?

J’ai tendance à choisir ce modèle:

 public class DogWithBreed { public Dog Dog { get; set; } public ssortingng BreedName { get; set; } } public IQueryable GetDogsWithBreedNames() { var db = new DogDataContext(ConnectSsortingng); 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; } 

Cela signifie que vous avez une classe supplémentaire, mais elle est rapide et facile à coder, facilement extensible, réutilisable et sécurisée.

Vous pouvez retourner des types anonymes, mais ce n’est vraiment pas joli .

Dans ce cas, je pense qu’il serait préférable de créer le type approprié. S’il ne s’agit que du type contenant la méthode, en faire un type nested.

Personnellement, je voudrais que C # obtienne des “types anonymes nommés” – c’est-à-dire le même comportement que les types anonymes, mais avec des déclarations et des noms de propriétés, mais c’est tout.

EDIT: D’autres suggèrent de retourner des chiens, puis d’accéder au nom de la race via un chemin de propriété, etc. C’est une approche parfaitement raisonnable, mais l’IME mène à des situations où vous avez effectué une requête particulière à cause des données que vous voulez use – et cette méta-information est perdue quand vous retournez simplement IEnumerable – la requête peut s’attendre à ce que vous utilisiez (disons) Breed plutôt que Owner raison de certaines options de chargement, etc., mais si vous l’oubliez Votre application peut fonctionner mais pas aussi efficacement que vous l’aviez prévu à l’origine. Bien sûr, je pourrais parler d’ordures, ou sur-optimiser, etc.

Juste pour append ma valeur de deux cents 🙂 J’ai récemment appris une manière de manipuler des objects anonymes. Il ne peut être utilisé que pour cibler le framework .NET 4 et cela uniquement lors de l’ajout d’une référence à System.Web.dll, mais c’est assez simple:

 ... using System.Web.Routing; ... class Program { static void Main(ssortingng[] 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" }; } } 

Pour pouvoir append une référence à System.Web.dll, vous devrez suivre les conseils de rushonerok : Assurez-vous que l’ infrastructure cible de votre [projet] est “.NET Framework 4” et non “.NET Framework 4 Client Profile”.

Vous devez d’abord utiliser la méthode ToList() pour ToList() lignes de la firebase database, puis sélectionner des éléments en tant que classe. Essaye ça:

 public partial class Dog { public ssortingng BreedName { get; set; }} List GetDogsWithBreedNames(){ var db = new DogDataContext(ConnectSsortingng); 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;} 

Donc, le tour est d’ abord ToList() . Il fait immédiatement la requête et récupère les données de la firebase database. La deuxième astuce consiste à sélectionner des éléments et à utiliser l’initialiseur d’object pour générer de nouveaux objects avec les éléments chargés.

J’espère que cela t’aides.

Non, vous ne pouvez pas renvoyer des types anonymes sans passer par une ruse.

Si vous n’utilisiez pas C #, ce que vous cherchiez (retourner plusieurs données sans un type concret) est appelé un tuple.

Il y a beaucoup d’implémentations de tuple C #, en utilisant celle montrée ici , votre code fonctionnerait comme ceci.

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

Et sur le site appelant:

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

Vous pourriez faire quelque chose comme ça:

 public System.Collections.IEnumerable GetDogsWithBreedNames() { var db = new DogDataContext(ConnectSsortingng); 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(); } 

Maintenant, je me rends compte que le compilateur ne me laissera pas retourner un ensemble de types anonymes car il attend des chiens, mais y a-t-il un moyen de retourner ceci sans avoir à créer un type personnalisé?

Utilisez object object pour renvoyer une liste de types anonymes sans créer de type personnalisé. Cela fonctionnera sans l’erreur de compilation (dans .net 4.0). J’ai renvoyé la liste au client puis l’ai analysée en JavaScript:

 public object GetDogsWithBreedNames() { var db = new DogDataContext(ConnectSsortingng); 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; } 

Il suffit de sélectionner les chiens, puis d’utiliser dog.Breed.BreedName , cela devrait fonctionner dog.Breed.BreedName .

Si vous avez beaucoup de chiens, utilisez DataLoadOptions.LoadWith pour réduire le nombre d’appels de firebase database.

Vous ne pouvez pas renvoyer directement des types anonymes, mais vous pouvez les parcourir via votre méthode générique. Alors faites 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.

 var result = Repeat(new { Name = "Foo Bar", Age = 100 }, 10); private static IEnumerable Repeat(TResult element, int count) { for(int i=0; i 

Ci-dessous un exemple basé sur le code de la question originale:

 var result = GetDogsWithBreedNames((Name, BreedName) => new {Name, BreedName }); public static IQueryable GetDogsWithBreedNames(Func creator) { var db = new DogDataContext(ConnectSsortingng); 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; } 

Si vous avez une configuration de relation dans votre firebase database avec une limitation de clé de change sur BreedId, ne l’avez-vous pas déjà?

Cartographie des relations DBML http://soffr.miximages.com/c%23/relationship.png

Je peux donc maintenant appeler:

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

Et dans le code qui appelle cela:

 var album = GetAlbum(1); foreach (Photo photo in album.Photos) { [...] } 

Donc, dans votre cas, vous appelez quelque chose comme dog.Breed.BreedName – comme je l’ai dit, cela dépend de la configuration de votre firebase database avec ces relations.

Comme d’autres l’ont mentionné, DataLoadOptions aidera à réduire les appels à la firebase database si cela pose un problème.

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:

 public List<(string Name, string BreedName)> GetDogsWithBreedNames() { var db = new DogDataContext(ConnectSsortingng); 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(); } 

Vous devrez peut-être installer le package nuget System.ValueTuple.

Eh bien, si vous revenez Chiens, vous feriez:

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

Si vous souhaitez que la race soit chargée avec impatience et non paresseuse, utilisez simplement la construction DataLoadOptions appropriée.

BreedId dans la table Dog est évidemment une clé étrangère à la ligne correspondante dans la table Breed . Si votre firebase database est correctement configurée, LINQ to SQL doit automatiquement créer une association entre les deux tables. La classe Dog qui en résulte aura une propriété Breed, et la classe Breed devrait avoir une collection Dogs. En le configurant de cette façon, vous pouvez toujours retourner IEnumerable , qui est un object qui inclut la propriété race. La seule mise en garde est que vous devez précharger l’object de la race avec les objects de chien dans la requête pour pouvoir y accéder après l’élimination du contexte de données et, comme l’a suggéré une autre affiche, exécuter une méthode sur la collection. requête à effectuer immédiatement (ToArray dans ce cas):

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

Il est alors sortingvial d’accéder à la race pour chaque chien:

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

Si l’idée principale est de faire en sorte que l’instruction SQL select envoyée au serveur de firebase database n’ait que les champs obligatoires et non tous les champs Entity, alors vous pouvez le faire:

 public class Class1 { public IList getCarsByProjectionOnSmallNumberOfProperties() { try { //Get the SQL Context: CompanyPossessionsDAL.POCOContext.CompanyPossessionsContext dbContext = new CompanyPossessionsDAL.POCOContext.CompanyPossessionsContext(); //Specify the Context of your main entity eg Car: var oDBQuery = dbContext.Set(); //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 eg Car var queryConvertAnonymousToOriginal = from x in queryProjectedOnSmallSetOfProperties select new Car { carNo = x.carNo, eName = x.eName, aName = x.aName }; //return the IList that is wanted var lst = queryConvertAnonymousToOriginal.ToList(); return lst; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.ToSsortingng()); throw; } } }