2010-11-07 3 views
17

Quindi ho appena ricevuto una raccomandazione da Amazon per LINQ to Objects Using C# 4.0: Using and Extending LINQ to Objects and Parallel LINQ (PLINQ).Alcuni esempi di utilizzo della parola chiave dinamica .NET 4 con Linq?

Si dice che il libro introduce utilizzando la parola chiave dynamic con LINQ, che mi ha fatto pensare:

Che tipo di suggestione si potrebbe fare con la parola chiave dynamic che non si poteva fare con Linq altrimenti?

+1

Se faccio una query LINQ su un oggetto dinamico, ottengo 'errore CS1979: espressioni di query oltre tipo di origine 'dinamica' o con una sequenza join di tipo 'dinamico' non sono allowed': S. –

+0

Una piccola lettura degli attuali limiti dell'uso di LINQ con le dinamiche e alcuni metodi per aggirarli: http://weblogs.asp.net/davidfowler/archive/2010/08/04/dynamic-linq-a-little- more-dynamic.aspx – egoodberry

risposta

20

Ecco un'idea: combinando LINQ con dinamico, è possibile eseguire query su set di dati non tipizzati come se fossero stati digitati.

Ad esempio, supporre che myDataSet sia un DataSet non tipizzato. Con tipizzazione dinamica e un metodo di estensione chiamato AsDynamic(), il seguente è possibile:

var query = from cust in myDataSet.Tables[0].AsDynamic() 
    where cust.LastName.StartsWith ("A") 
    orderby cust.LastName, cust.FirstName 
    select new { cust.ID, cust.LastName, cust.FirstName, cust.BirthDate }; 

Ecco come definire il metodo di estensione AsDynamic. Notate come restituisce IEnumerable di dinamica, che lo rende adatto per le query LINQ:

public static class Extensions 
{  
    public static IEnumerable<dynamic> AsDynamic (this DataTable dt) 
    { 
    foreach (DataRow row in dt.Rows) yield return row.AsDynamic(); 
    } 

    public static dynamic AsDynamic (this DataRow row) 
    { 
    return new DynamicDataRow (row); 
    } 

    class DynamicDataRow : DynamicObject 
    { 
    DataRow _row; 
    public DynamicDataRow (DataRow row) { _row = row; } 

    public override bool TryGetMember (GetMemberBinder binder, out object result) 
    { 
     result = _row[binder.Name]; 
     return true; 
    } 

    public override bool TrySetMember (SetMemberBinder binder, object value) 
    { 
     _row[binder.Name] = value; 
     return true; 
    } 

    public override IEnumerable<string> GetDynamicMemberNames() 
    { 
     return _row.Table.Columns.Cast<DataColumn>().Select (dc => dc.ColumnName); 
    } 
    } 
} 

sottoclasse DynamicObject, questo si avvale di costume vincolante - dove si prende il processo di risoluzione di nomi dei membri da soli. In questo caso, associamo l'accesso get e set ai membri per recuperare o archiviare oggetti nella DataRow sottostante.

+0

Awsome! Quando l'ho visto, ho avuto l'idea di aggiungere un metodo di estensione .ExecuteSql che consente di utilizzare LinqPad direttamente con le connessioni del server SQL. [Guardalo fuori ...] (http://stackoverflow.com/a/24885293/1016343) – Matt

+0

Anche se ormai mi sono abituato e mi sento naturale, c'è in me questo purista che mi fa credere "AsDynamic". dovrebbe essere chiamato idealmente "ToDynamic". 'To' implica una conversione della modifica dell'identità mentre' As' implica in modo nativo una conversione che preserva la rappresentazione, come 'as' la parola chiave. Ma comunque, .NET ha molti 'As' simili, come' AsReadOnly'. – nawfal

2

La risposta di Joe è eccezionale. Ho un'idea di come semplificare l'utilizzo. Se si aggiunge questo alla classe estensione:

public static class Extensions 
{  

    public static IEnumerable<dynamic> ExecuteSql(this UserQuery uq, string sql) 
    { 
     var connStr="Provider=SQLOLEDB.1;"+uq.Connection.ConnectionString; 

     OleDbConnection connection = new OleDbConnection(connStr); 
     DataSet myDataSet = new DataSet(); 
     connection.Open(); 

     OleDbDataAdapter DBAdapter = new OleDbDataAdapter(); 
     DBAdapter.SelectCommand = new OleDbCommand(sql, connection); 
     DBAdapter.Fill(myDataSet); 

     var result = myDataSet.Tables[0].AsDynamic(); 
     return result; 
    } 
} 

Permette di utilizzare le query di questo tipo in LINQPad:

void Main() 
{ 
    var query1 = from cust in this.ExecuteSql("SELECT * from Customers") 
     where cust.ContactName.StartsWith ("C") 
     orderby cust.ContactName 
     select new { cust.CustomerID, cust.ContactName, cust.City };   
    query1.Dump();  
} 

NB: È necessario aggiungere i seguenti riferimenti:

  • Aggiungi System.Data.OleDb dal System.Data assemblea per le proprietà della query
  • Aggiungi System.Dynamic alle proprietà della query

Aggiornamento: Ho notato che Joe ha aggiunto una funzione ExecuteQueryDynamic nel latest Beta v4.53.03 of LinqPad, che possono essere utilizzati per raggiungere questo obiettivo, ad esempio:

void Main() 
{ 
    var q=this.ExecuteQueryDynamic("select * from Customers"); 
    q.Dump(); 
} 

Ciò restituirà la tabella Customers dal Database Northwind come IEnumerable<dynamic>, utilizzando una connessione Linq2Sql.

0

Quello che ho fatto mi dà il risultato è questo, ma penserei che c'è un modo migliore.

using (SqlConnection connection = new SqlConnection(this.Connection.ConnectionString)) 
{ 
    connection.Open(); 

    SqlCommand command = new SqlCommand(query, connection); 
    SqlDataReader reader = command.ExecuteReader(); 

    reader.Cast<IDataRecord>().AsQueryable().Dump();  
}