Question

J'ai une méthode de sélection du niveau données qui renvoie un objet datable. Elle est appelée depuis une méthode de niveau métier qui devrait ensuite renvoyer une liste générique fortement typée.

Ce que je veux faire est très similaire (mais pas identique à) cette question:
Comment convertir un DataTable en liste générique?

Ce qui est différent, c’est que je veux que la liste contienne des objets fortement typés plutôt que des datarows (aussi, linq n’est pas encore disponible ici).

Je suis préoccupé par les performances. La méthode de niveau entreprise sera à son tour appelée à partir du niveau de présentation et les résultats seront itérés pour être affichés à l'utilisateur. Il semble très inutile d'ajouter une itération supplémentaire au niveau des entreprises, mais réessayez-le tout de suite pour la présentation. Je souhaite donc que cela soit le plus rapide possible.

C’est une tâche courante, je suis donc vraiment à la recherche d’un bon motif qui puisse être répété plusieurs fois.

Était-ce utile?

La solution

Connaissez-vous à l’avance la structure du DataTable et de l’objet saisi? Vous pouvez utiliser un délégué pour faire le mappage. Si vous ne le connaissez pas (c’est-à-dire que tout ce que vous savez est un Type et des propriétés), il existe des moyens d’accélérer l’accès des membres dynamiques (tels que HyperDescriptor).

Dans les deux cas, considérons un bloc d'itérateurs; De cette façon, vous n'avez pas à mettre les objets en mémoire tampon une seconde fois; Bien sûr, si vous ne travaillez qu'avec des compteurs de lignes minuscules, ce n'est pas un problème.

Pouvez-vous clarifier certains de ces points? Je peux ajouter beaucoup plus de détails ...

Au plus simple, qu'est-ce qui ne va pas avec:

DataTable table = new DataTable {
    Columns = {
        {"Foo", typeof(int)},
        {"Bar", typeof(string)}
     }
};
for (int i = 0; i < 5000; i++) {
    table.Rows.Add(i, "Row " + i);
}

List<MyType> data = new List<MyType>(table.Rows.Count);
foreach (DataRow row in table.Rows) {
    data.Add(new MyType((int)row[0], (string)row[1]));
}

(les problèmes ci-dessus pourraient orienter la bonne approche ...)

Autres conseils

Le problème avec l'exemple ci-dessus est qu'il est terriblement lent. J'ai un DataTable avec environ 400 lignes et cette conversion prend 5 ou 6 secondes!

Cela semble être une tâche assez commune, alors je suis surpris de ne pas voir quelqu'un ici avec une solution plus performante.

* Mettre à jour !! * Juste pour le plaisir, je pensais essayer de convertir en utilisant LINQ au lieu d’itérer à travers le DataTable et d’ajouter à ma liste. Voici ce que j'ai fait:

   List<MyObject> returnList = new List<MyObject>();

   MyDataTable dtMyData = new MyTableAdapter().GetMyData();

   returnLists = (from l in dtMyData
                 select new MyObject
                 {
                    Active = l.IsActive,
                    Email = l.Email,
                    //...
                    //About 40 more properties
                    //...
                    ZipCode = l.Zipcode
                  }).ToList();

La première méthode (parcourant chaque ligne) a pris 5,3 secondes et la méthode utilisant LINQ a pris 1,8 secondes!

Vous devrez quand même parcourir les datarows et les convertir en objets, mais vous pouvez écrire une collection personnalisée avec un constructeur qui prend une collection de datarows et convertit chaque élément en votre type d'objet lorsque demandé à la place. de tous à la fois.

Supposons que vous définissiez votre collection personnalisée qui implémente IList<T>. Il pourrait alors avoir deux champs internes - une référence à la collection de datarows et un List<T>. Le <=> serait initialisé à la longueur de la collection de datarows mais avec des valeurs NULL.

Maintenant, dans l'indexeur, vous pouvez vérifier si le <=> contient une valeur à cet index et, dans le cas contraire, vous pouvez créer l'objet en utilisant le moyen qui vous serait utile et le stocker avant de le renvoyer.

Cela retarderait la création de vos objets jusqu'à ce qu'ils soient demandés et vous ne créeriez que les objets demandés.

Vos objets auraient probablement besoin d’un constructeur prenant un DataRow ou vous auriez besoin d’une sorte d’usine pour les créer, mais c’est un autre sujet.

Juste pour apporter plus de convivialité à la réponse de Mark dans une application console simple:

class Program
{
    static void Main(string[] args)
    {
        //define a DataTable obj
        DataTable table = new DataTable
        {
            Columns = {
            {"Foo", typeof(int)},
            {"Bar", typeof(string)}
         }
        };
        //populate it the DataTable 
        for (int i = 0; i < 3; i++)
        {
            table.Rows.Add(i, "Row " + i);
        }

        List<MyType> listWithTypedObjects= new List<MyType>(table.Rows.Count);
        foreach (DataRow row in table.Rows)
        {
            listWithTypedObjects.Add(new MyType((int)row[0], (string)row[1]));
        }

        Console.WriteLine(" PRINTING THE POPULATED LIST ");
        foreach (MyType objMyType in listWithTypedObjects)
        {
            Console.Write(" I have object of the type " + objMyType.ToString() + " => " );
            Console.Write(" with Prop1OfTypeInt " + objMyType.Prop1OfTypeInt.ToString() + " , ");
            Console.WriteLine(" with Prop1OfTypeInt " + objMyType.Prop2OfTypeString.ToString() + "  "); 
        }

        Console.WriteLine(" \n \n \n HIT A KEY TO EXIT THE PROGRAM ");
        Console.ReadKey();
    }
}

class MyType {

    public int Prop1OfTypeInt { get; set; }
    public string Prop2OfTypeString { get; set; } 

    /// <summary>
    /// Note the order of the passed parameters is important !!!
    /// </summary>
    public MyType( int prop1OfTypeInt , string prop2OfTypeString)
    {
        this.Prop1OfTypeInt = prop1OfTypeInt;
        this.Prop2OfTypeString = prop2OfTypeString; 

    }
}
public partial class issuereceive_manageroffice_bal
{
    public int issue_id{get;set;}
    public string process{get;set;}
    public DateTime issue_date{get;set;}
    public TimeSpan issue_time{get;set;}
    public string eg_no{get;set;}
    public string lotno{get;set;}
    public string clarity{get;set;}
    public string sieves{get;set;}
    public string shape{get;set;}
    public double issue_carat{get;set;}
    public int issue_pieces{get;set;}
    public int receive_pieces{get;set;}
    public double receive_carat{get;set;}
    public int kp_pieces{get;set;}
    public decimal kp_carat{get;set;}
    public double loss{get;set;}
    public string issue_manager{get;set;}
    public string issue_by{get;set;}
    public string receive_by{get;set;}
    public int status{get;set;}
    public DateTime receive_date{get;set;}
    public string receive_time{get;set;}
    public int factory_id{get;set;}

}


List<issuereceive_manageroffice_bal> issue_receive_list = new List<issuereceive_manageroffice_bal>();
issue_receive_list =
      (from DataRow dr in DataTable.Rows
      select new issuereceive_manageroffice_bal()
           {
               issue_id = 0,
               issue_time = TimeSpan.Parse("0"),
               receive_time = null,
               shape = null,
               process = dr["process"].ToString(),
               issue_date = Convert.ToDateTime(dr["issue_date"]),
               eg_no = dr["eg_no"].ToString(),
               lotno = dr["lotno"].ToString(),
               clarity = dr["clarity"].ToString(),
               sieves = dr["sieves"].ToString(),
               issue_carat = dr["issue_carat"].ToString() != "" ? double.Parse(dr["issue_carat"].ToString()) : 0,
               issue_pieces = dr["issue_pieces"].ToString() != "" ? int.Parse(dr["issue_pieces"].ToString()) : 0,
               receive_carat = dr["receive_carat"].ToString() != "" ? double.Parse(dr["receive_carat"].ToString()) : 0,
               kp_pieces = dr["kp_pieces"].ToString() != "" ? int.Parse(dr["kp_pieces"].ToString()) : 0,
               kp_carat = dr["kp_carat"].ToString() != "" ? decimal.Parse(dr["kp_carat"].ToString()) : 0,
               loss = dr["loss"].ToString() != "" ? double.Parse(dr["loss"].ToString()) : 0,
               issue_manager = dr["lotno"].ToString(),
               issue_by = dr["issue_by"].ToString(),
               receive_by = dr["receive_by"].ToString(),
               status = dr["status"].ToString() != "" ? int.Parse(dr["status"].ToString()) : 0,
               receive_date = Convert.ToDateTime(dr["receive_date"]),
               factory_id = dr["factory_id"].ToString() != "" ? int.Parse(dr["factory_id"].ToString()) : 0,

           }).ToList();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top