2009-04-16 8 views
5

J'écris une méthode pour retourner une ligne 'asset' de la base de données. Il contient des chaînes, des entiers et un tableau d'octets (cela peut être une image/un film/un document).Le moyen le plus efficace d'obtenir une ligne de données à partir de DB dans ASP.NET

Maintenant, pour la plupart des accès aux lignes, j'utilise la méthode suivante qui retourne un NameValueCollection car il s'agit d'un objet léger, facile à utiliser et à utiliser avec des chaînes et des int. Maintenant, mon apporach pour ce type d'accès aux données serait normalement d'obtenir une méthode pour retourner une base de données.

 public static DataRow ReturnDataRow(Database db, DbCommand dbCommand) 
    { 
     var dt = new DataTable(); 

     using (IDataReader dr = db.ExecuteReader(dbCommand)) 
      if (dr != null) dt.Load(dr); 

     dbCommand.Dispose(); 
     return dt.Rows.Count != 0 ? dt.Rows[0] : null; 
    } 

Il semble assez fastidieux de créer un DataTable puis de retourner sa première valeur de données.

Y a-t-il un meilleur moyen de le faire? Je pense peut-être à un dictionnaire d'objets que j'ai ensuite lancé manuellement chaque membre de.

Serait intéressant de voir comment les autres se sont attaqués à cela. Je sais que cela tombe dans le domaine de la micro-optimisation et aussi longtemps que je ne retourne pas DataSets pour chaque requête de ligne (je voudrais avoir une livre pour chaque fois que j'ai vu cela dans une ligne de code) ça devrait aller. Cela dit, cette méthode est susceptible d'être appelée pour l'attribution de requêtes d'accès aux données sur l'attribution de sites sur une seule boîte.

Vive

Steve

+1

Juste un petit problème, mais l'appelant devrait appeler la méthode DbCommand.Dispose, pas vos méthodes de lecture de données. Puisque votre appelant a fourni l'objet dbCommand, il doit le dipuler. –

Répondre

7

Comment ça va?

Y a-t-il une raison pour laquelle vous n'avez pas de conteneurs d'objets qui représentent une ligne dans votre base de données? Créer un objet personnalisé est beaucoup plus facile à gérer dans les autres niveaux de votre solution. Donc, avec cette approche, il existe deux solutions très viables à vos problèmes.

Supposons que vous ayez un objet personnalisé représentant un produit dans votre base de données. Vous souhaitez définir l'objet comme celui-ci:

public class Product { 
    public int ProductID { get; set; } 
    public string Name { get; set; } 
    public byte[] Image { get; set; } 
} 

Et vous remplissez une collection de produits de (Collection) comme celui-ci:

var collection = new Collection<Product>(); 

using (var reader = command.ExecuteReader()) { 
    while (reader.Read()) { 
     var product = new Product(); 

     int ordinal = reader.GetOrdinal("ProductID"); 
     if (!reader.IsDBNull(ordinal) { 
      product.ProductID = reader.GetInt32(ordinal); 
     } 

     ordinal = reader.GetOrdinal("Name"); 
     if (!reader.IsDBNull(ordinal)) { 
      product.Name = reader.GetString(ordinal); 
     } 

     ordinal = reader.GetOrdinal("Image"); 
     if (!reader.IsDBNull(ordinal)) { 
      var sqlBytes = reader.GetSqlBytes(ordinal); 
      product.Image = sqlBytes.Value; 
     } 

     collection.Add(product); 
    } 
} 

Notez que je récupérer une valeur via le lecteur de Obtenez x x est le type que je veux récupérer de la colonne. Ceci est la méthode recommandée par Microsoft pour extraire des données pour une colonne par http://msdn.microsoft.com/en-us/library/haa3afyz.aspx (deuxième paragraphe) car la valeur récupérée ne doit pas nécessairement être encadrée dans System.Object et être déballée dans un type primitif.

Puisque vous avez mentionné que cette méthode sera appelée beaucoup, plusieurs fois, dans une application ASP.NET, vous souhaiterez peut-être reconsidérer une approche générique comme celle-ci. La méthode que vous utilisez pour renvoyer un NameValueCollection est très non performante dans ce scénario (et peut-être dans de nombreux autres scénarios). Sans mentionner que vous convertissez chaque colonne de base de données en une chaîne sans tenir compte de la culture de l'utilisateur actuel, et la culture est un facteur important dans une application ASP.NET. Je dirais que ce NameValueCollection ne devrait pas être utilisé dans vos autres efforts de développement. Je pourrais continuer encore et encore à ce sujet, mais je vais vous sauver mes diatribes.

Bien sûr, si vous créez des objets qui correspondent directement à vos tables, vous pouvez aussi regarder dans LINQ to SQL ou ADO.NET Entity Framework. Vous serez heureux de l'avoir fait.

+0

+1 parce que je n'ai jamais remarqué le lecteur.Getxxx méthodes avant maintenant et c'est un bon conseil! – BenAlabaster

2

Qu'est-ce que vous demonstarting est une odeur de code appelé Primitive Obsession. Créez un type personnalisé et renvoyez-le à partir de votre méthode de référentiel. N'essayez pas d'être trop générique ... vous finirez par introduire cette complexité dans votre code d'entreprise car vous interagirez avec vos entités en utilisant un code purement procédural. Mieux vaut créer des objets qui modèlent votre entreprise.

Si vous êtes préoccupé par un trop grand nombre de codes d'accès aux données, considérez l'utilisation d'un framework ORM pour prendre soin de générer ceci pour vous. Vous ne devriez pas laisser cette préoccupation dicter une mauvaise conception dans votre couche d'application.

3

En termes d'efficacité du code, vous l'avez probablement fait avec le moins de frappes, et bien qu'il semble inutile, c'est probablement le plus simple à maintenir. Toutefois, si vous êtes tous sur l'efficacité de ne faire que ce qui est strictement nécessaire, vous pouvez créer une structure légère/classe pour remplir les données et utiliser quelque chose de similaire à:

public class MyAsset 
{ 
    public int ID; 
    public string Name; 
    public string Description; 
} 

public MyAsset GetAsset(IDBConnection con, Int AssetId) 
{ 
    using (var cmd = con.CreateCommand("sp_GetAsset")) 
    { 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.Add(cmd.CreateParameter("AssetID")); 
     using(IDataReader dr = cmd.ExecuteReader()) 
     { 
      if (!dr.Read()) return null; 

      return new MyAsset() { 
       ID = dr.GetInt32(0), 
       Name = dr.GetString(1), 
       Description = dr.GetString(2) 
      }; 
     } 
    } 
} 

De même, vous pouvez vider les données de façon similaire à droite dans votre collection de KVPS ...

il est pas tout à fait aussi propre recherche que votre code d'origine, mais il ne crée pas toute la table juste pour obtenir la seule ligne ...

comme cela a été mentionné dans un autre article concernant l'odeur du code, je ne serais probablement pas passer la commande en paramètre, je pense que je serais plus susceptible d'encapsuler la commande à l'intérieur de cette méthode, en passant seulement la connexion à la base de données et l'identifiant de l'actif que je voulais - en supposant que je n'utilisais pas la mise en cache bien sûr, et en renvoyant l'instance MyAsset. Cela garde la méthode assez générique pour pouvoir être utilisée sur n'importe quel type de base de données - en supposant que le proc stocké existait bien sûr. De cette façon, le reste de mon code est à l'abri d'avoir besoin de savoir quoi que ce soit sur la base de données, à part le type de base de données ...et dans le reste de mon application, je peux référencer des informations sur les biens en utilisant MyAssetInstance.ID, MyAssetInstance.Name, MyAssetInstance.Description etc ...

0

Vous obtiendrez beaucoup plus d'avantages de données de mise en cache que d'essayer d'optimiser le retour d'une seule ligne . Si vous sélectionnez par clé primaire, il est peu probable que vous verrez une différence entre renvoyer un DataTable ou un DataRow ou un objet personnalisé. Cela me semble être une optimisation prématurée. Je serais plus précis mais je ne suis pas sûr si avoir un tableau d'octets dans le mélange change les choses.

0

Merci pour tous les gars d'entrée. Je sais que l'ORM est probablement la voie à suivre et que le cadre MVC est le prochain sur ma liste. Pour donner un peu plus de détails, le code que je montre provient de la section helpers de ma couche d'accès aux données qui passe ensuite la collection de valeur row ou name à la couche business pour devenir des objets.

Je pense que les exemples de code mnero0429 et balabaster me donnent la bonne direction. Utilisez un lecteur de données et récupérez manuellement les données comme cela sans avoir à manipuler les objets intermédiaires. Merci pour le lien MS détaillée mnero0429. Juste sur l'obsession primative - que je fais vraiment une classe d'actifs dans la couche de gestion;)

Je vais regarder dans le cadre d'entité ADO aussi.

Encore une fois, merci pour le conseil - je sais que le monde continuerait à tourner même si j'utilisais DataSet.Tables [0].Rows [0] ["bob"] ou certains tels, mais quand vous obtenez cette démangeaison - quel est le meilleur wat pour le faire, c'est agréable de l'avoir rayé!

Questions connexes