724 messaggi dal 11 febbraio 2013
Ho una datagridview con un elenco di tabelle

al click di una cell vorrei visualizzare sotto una gridview che visualizzi i dati contenuti

Leggendo un po i design pattern e soprattutto accingendomi a implementare questa funzionalità mi rendo
conto che di OOP non ha nulla e che lo switch crescerà e mi sopravvivrà !


private async void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            var tableName = (dataGridView1.SelectedCells[0].Value.ToString());
            dynamic source = null;
            switch (tableName)
            {
                case "Agent":
                source = await Agent.GetAgentsAsync();
                    break;
                
            }

            dataGridView2.DataSource = source;
        }


come posso creare una classe/interfaccia che mi consenta di evitare lo switch

perdonate ma non so da dove cominciare... ho solo capito che è la strada sbagliata :)
11.097 messaggi dal 09 febbraio 2002
Contributi
Se il nome della classe e del metodo sono predicibili, allora puoi usare la reflection per ottenere un riferimento al metodo. Esempio da cui iniziare:

Type tipo = System.Reflection.Assembly.GetExecutingAssembly().GetType($"TuoNamespace.{tableName}");
MethodInfo metodo = tipo.GetMethod($"Get{tableName}sAsync");
Task task = (Task) metodo.Invoke(null);
await task.ConfigureAwait(false);
dataGridView2.DataSource = ((dynamic)task).Result;


Anziché usare metodi statici, personalmente userei dei metodi d'istanza, definiti da un'interfaccia custom come ITabledDataProvider che restituisce DataTable e che viene implementata dalla classe contenitrice.

ciao,
Moreno
Modificato da BrightSoul il 21 novembre 2018 13.49 -

Enjoy learning and just keep making
724 messaggi dal 11 febbraio 2013
Grazie Moreno

Dici tipo cosi
public interface ITableDataProvider
    {
        Task<DataTable> GetTableData();
    }

public async Task<DataTable> GetTableData()
        {
            var data = await _context.Agents.ToListAsync();   
            return data.ToDataTable; // qui usare la reflection per riempire il datatable?
        }



...magari ho capito male..

dimenticavo che questo che mi hai girato funziona bene e mi pare di aver anche capito :)

 private async void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            var tableName = (dataGridView1.SelectedCells[0].Value.ToString());
           
            Type tipo = Assembly.GetExecutingAssembly().GetType($"SQLite.Models.{tableName}");
            MethodInfo metodo = tipo.GetMethod($"Get{tableName}Async");
            Task task = (Task)metodo.Invoke(null, null);
            await task.ConfigureAwait(false);

            dataGridView2.DataSource = ((dynamic)task).Result;
           
        }

Modificato da jjchuck il 21 novembre 2018 16.06 -
11.097 messaggi dal 09 febbraio 2002
Contributi

Dici tipo cosi


Sì, ma all'interno del metodo puoi anche non usare entity framework. Infatti, potresti anche non materializzare le entità ma usare direttamente ADO.NET per ottenere il datatable di cui hai bisogno.

Ve vuoi evitare di duplicare il codice, anziché usare l'interfaccia ITableDataProvider puoi usare una classe base astratta in cui definisci il metodo per estrarre il DataTable. Alla fine, l'unica cosa che cambia tra una tabella e l'altra è solo il nome della tabella (o al limite l'intera query SELECT).

Enjoy learning and just keep making
724 messaggi dal 11 febbraio 2013
Dato che nel datagridview ci sono tabelle sql e sqlite ho fatto come mi hai suggerito ... e spero abbia senso
:)

public abstract class DataAccess
    {
        public abstract Task<DataTable> GetDataTable(string tableName);
        
    }



public class SQLiteDataAccess : DataAccess
    {
        const string _stringConnection =...";

        public override async Task<DataTable> GetDataTable(string tableName)
        {
            var table = new DataTable();

            await Task.Run(() => { 
            
            using (var con = new SQLiteConnection(_stringConnection))
            {
                var SQL = $"SELECT * FROM {tableName}";
                using (var adapter = new SQLiteDataAdapter(SQL, con))
                {
                   adapter.Fill(table);
                }
            }
            });
            return table;
        }
    }


infine dalla ui

private async void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            try
            {
                var tableName = (dataGridView1.SelectedCells[0].Value.ToString());
                var columnClicked = dataGridView1.CurrentCell.ColumnIndex;

                if (columnClicked == 2)
                {
                    DataAccess da = new SQLiteDataAccess();
                    var data = await da.GetDataTable(tableName);

                    dataGridView2.DataSource = data;
                }
                else if (columnClicked == 0)
                {
                    DataAccess da = new SqlDataAccess();
                    var data = await da.GetDataTable(tableName);

                    dataGridView2.DataSource = data;
                }
                else
                {
                    return;
                }

                //With Reflection
                //Type tipo = Assembly.GetExecutingAssembly().GetType($"BP_FUT_SQLite.Models.{tableName}");
                //MethodInfo metodo = tipo.GetMethod($"Get{tableName}Async");
                //Task task = (Task)metodo.Invoke(null, null);
                //await task.ConfigureAwait(false);
                //dataGridView2.DataSource = ((dynamic)task).Result;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + " ; " + ex.Source);                
            }
            
           
        }



grazie infinte come sempre
Modificato da jjchuck il 22 novembre 2018 16.03 -
11.097 messaggi dal 09 febbraio 2002
Contributi
Mi pare una buona soluzione.
Solo dovresti spostare il codice all'interno della classe astratta altrimenti tanto vale usare un'interfaccia.
    public abstract class DataAccess
    {
        public abstract Task<DataTable> GetDataTable(string tableName);

        protected async Task<DataTable> GetDataTable(string tableName, DbProviderFactory factory, string connectionString)
        {
            var table = new DataTable();
            //L'obiettivo è eliminare questo Task.Run
            await Task.Run(async () =>
            {
                using (var conn = factory.CreateConnection())
                {
                    conn.ConnectionString = connectionString;
                    await conn.OpenAsync();
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = $"SELECT * FROM {tableName}";
                        using (var adapter = factory.CreateDataAdapter())
                        {
                            adapter.SelectCommand = cmd;
                            adapter.Fill(table);
                        }
                    }
                }
            });
            return table;
        }
    }


E poi la classe che deriva da quella astratta si limita a passare il nome della tabella, il provider e la connection string.
    public class SqlDataAccess : DataAccess
    {
        public override async Task<DataTable> GetDataTable(string tableName)
        {
            return await base.GetDataTable("Tabella", SqlClientFactory.Instance, "Data Soure=...;Initial Catalog=...");
        }
    }

E così ne crei anche una per sqlite.

Comunque, non dovresti riempire l'intero DataTable. Fallo in piccoli batch da 50 righe alla volta. Infatti, perché caricare 100.000 righe se l'utente magari scorre solo le prime 20? Così puoi togliere il Task.Run.

ciao,
Moreno

Enjoy learning and just keep making
724 messaggi dal 11 febbraio 2013
Ok cosi è meglio davvero.

Però non capisco cosa intendi con caricare 50 righe alla volta o meglio capisco perchè ma non come

Vuoi dire che debbo usare un DbDatareader e fare table.load(reader) con un counter ?

oppure che allo scroll della barra carica il resto...una sorta di paginazione?
724 messaggi dal 11 febbraio 2013
Alla fine ho messo un bottone (Carica Records) dove incremento al click i record di 50

public abstract class DataAccess
    {
        public abstract Task<DataTable> GetDataTable(string tableName, int rows = 50);

        protected async Task<DataTable> GetDataTable(string tableName,
            DbProviderFactory factory, string connectionString, int rows = 50)
        {
            var table = new DataTable();
            
                using (var conn = factory.CreateConnection())
                { 
                    conn.ConnectionString = connectionString;
                    await conn.OpenAsync();
                    using (var cmd = conn.CreateCommand())
                    {
                        if (factory == SqlClientFactory.Instance)
                            cmd.CommandText = $"SELECT TOP({rows}) * FROM {tableName}";
                        else
                            cmd.CommandText = $"SELECT * FROM {tableName} LIMIT {rows}";
                        
                        using (var adapter = factory.CreateDataAdapter())
                        {
                            adapter.SelectCommand = cmd;
                            adapter.Fill(table);
                        }
                    }
                }

            return table;
        }
}


Meglio di cosi non ho saputo fare...

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.