2010-10-26 4 views
3

Comment optimiser les appels ActiveRecord dans vos applications Web ASP.NET MVC 2?Comment optimiser les appels Castle ActiveRecord

Je suis assis devant mon projet et tout va bien jusqu'à ce que je commence à remplir les données. Comme beaucoup de projets j'ai une structure de données semblable à ceci:

Une planète a beaucoup de pays. Un pays a beaucoup d'états/provinces. Un État a beaucoup de villes. Une ville a beaucoup de quartiers.

Ci-dessous un exemple de château ActiveRecord/NHibernate objet commercial persistant

[ActiveRecord] 
public class Country { 

    [Property] 
    public String CountryName { get; set; } 

    [HasMany(typeof(States))] 
    public IList<State> States { get; set; } 
} 

Supposons maintenant que vous voulez faire une demande innocente comme obtenir une liste de tous les pays de la planète

Par défaut, ActiveRecord/Nhibernate va charger l'arbre entier, jusqu'à la toute dernière dépendance.

Il peut s'avérer être BEAUCOUP d'appels SQL.

D'accord, nous pouvons résoudre ce avec le chargement paresseux [ActiveRecord(lazy=true)] mais chaque fois que vous voulez faire quelque chose propre comme ci-dessous

String text = "This country of " + country.CountryName + " has the following states:"; 

foreach(State s in country.States) 
{ 
    text += s.StateName + " "; 
} 

Avec le activerecord paresseux charge l'attribut à chaque fois qu'il va chercher pour s.StateName il est un autre appel sql .

C'est beaucoup trop d'appels SQL.

Certains de mes amis ont suggéré d'utiliser des méthodes ActiveRecordBase telles que FindAll (critères).

Mais il finit par être vraiment moche et difficile à lire avec tous ces Expression.Eq et quoi d'autre.

Et puis d'autres personnes ont également suggéré d'utiliser quelque chose comme HQL. HQL ressemble beaucoup à SQL. Donc, en bout de ligne, il semble que la manière la plus propre et la plus optimisée consiste à écrire des requêtes SQL simples et simples. Cela va droit au but.

SELECT * FROM Countries //No loading of an entire tree of dependencies 

SELECT * FROM States WHERE CountryId = @selectedId //1 call and you have all your states 

Quoi qu'il en soit, pour l'instant j'utilise des requêtes SQL et les procédures stockées pour l'extraction de données et ActiveRecord pour enregistrer/supprimer des objets.

Corrigez-moi si je me trompe ...?

Remerciements Apprécié.

Répondre

4

La méthode recommandée utilise applications paresseux par défaut, puis chercher avec impatience ce que vous avez besoin pour chaque cas.

Si vous n'utilisez pas paresseux par défaut, vous récupérez pratiquement toute la base de données dans chaque requête, comme vous l'avez déjà remarqué.

Si vous utilisez paresseux, mais ne pas chercher ardemment, vous rencontrez le problème SELECT N+1 que vous avez remarqué dans votre exemple foreach. Tout ce que j'ai mentionné jusqu'à présent est indépendant de l'API de requête que vous utilisez. Vous pouvez faire tout cela en utilisant HQL, Criteria, Linq ou QueryOver. Certaines requêtes sont plus faciles à exprimer dans HQL, et d'autres fois, il est plus pratique d'utiliser Criteria, Linq ou QueryOver. Mais encore une fois, c'est une question orthogonale.

En outre, cela ne diffère pas tellement de l'exécution de requêtes SQL brutes avec ADO.NET. Dans votre exemple, si vous vouliez un pays avec ses états, vous auriez INNER JOINed pour obtenir tous les états à la place au lieu d'émettre des SELECts individuels pour chaque état.

+0

L'oeil de Bull! Je vous remercie! – TchiYuan

0

Cela semble vraiment bizarre, désolé de le dire!

Un gros avantage des ORM est que vous pouvez résumer votre base de données ...! Donc, oui, HQL peut se sentir, ressembler et ressembler à du SQL, mais cela a une grande différence: il est indépendant du SGBD. Le même HQL fonctionnera pour SQL Server, ou Oracle, ou MySQL ...

Vous aurez également besoin de travailler pour partager votre connexion de base de données entre NHibernate et SQL Server. Faisable, mais un peu moche.

J'irais avec HQL sans même y penser.Parfois, vous avez vraiment besoin d'atteindre un niveau inférieur et d'écrire des requêtes SQL, mais le scénario que vous décrivez est loin d'être dans ce cas. OTOH, sur des scénarios du monde réel, vous aurez probablement besoin de la pagination pour afficher autant de données. Et si c'est le cas, l'approche paresseuse sera beaucoup mieux, même si elle génère potentiellement beaucoup plus d'instructions SQL. Pensez-y: si un utilisateur interroge un ensemble de résultats de 1 000 000 enregistrements, mais qu'il ne voit que les 20 premières entrées d'une page Web, je n'ai pas besoin d'extraire TOUS les 1 000 000 enregistrements de la base de données. Et c'est exactement ce que le chargement paresseux est destiné à ...

Modifier: presque oublié NHibernate premier et deuxième niveau de mise en cache. En utilisant ceci votre application peut éviter en interrogeant la couche de SGBD. Cela seul peut se traduire par un gain de performance énorme ...

+0

Hmm, je comprends votre point de vue. Cependant, dans mon cas, ce sera toujours SQL SERVER. Aussi, je résume déjà toutes les requêtes dans les objets d'accès aux données. Si, pour une raison quelconque, j'ai besoin de changer de base de données, la seule chose que je devrais réécrire serait les objets d'accès aux données. Performance sage Je recommande toujours d'écrire vos requêtes dans SQL entièrement. Surtout dans le scénario où votre base de données exclusive ne changera pas pour les 5 prochaines années. – TchiYuan

+0

Heh, être là, fait cela. Je peux seulement dire que c'est une décision que vous regretterez probablement. Il existe des moyens d'obtenir de meilleures performances de HHibernate (comme NHibernate Profiler), et vous perdrez le chargement paresseux en utilisant des requêtes SQL simples (ce qui est une décision très peu sage). Rappelez-vous, le monde Java a déjà prouvé que vous GAIN les performances en utilisant ORM sur des scénarios du monde réel. Mais être mon invité ... :) – rsenna

+0

Dans mon cas, la performance est de la plus haute importance ... Le scénario que j'ai présenté dans mon post original est une version simplifiée de la complexité de mes structures de données. – TchiYuan

2

Ceci est un problème bien connu avec ORMs. Une façon de résoudre ceci est d'ajouter le paramètre nhibernate BatchSize de sorte que chaque déplacement vers la base de données vous ramène n enregistrements (Etats dans ce cas) au lieu d'un.

[HasMany(typeof(States), BatchSize = 20)] 
public IList<State> States { get; set; } 
+0

Un aperçu perspicace! – TchiYuan

Questions connexes