2010-11-07 12 views
17

Donc, je viens de recevoir une recommandation d'Amazon pour LINQ to Objects Using C# 4.0: Using and Extending LINQ to Objects and Parallel LINQ (PLINQ).De bons exemples d'utilisation du mot-clé dynamique .NET 4 avec Linq?

Il dit que le livre introduit en utilisant le mot-clé dynamic avec Linq, ce qui me fait penser:

Quel genre de génialité pourriez-vous faire avec le mot-clé dynamic que vous ne pouviez pas faire avec LINQ autrement?

+1

Si je fais une requête Linq sur un objet dynamique, j'obtiens 'erreur CS1979: Les expressions de requête sur le type de source « dynamique » ou avec une séquence de jointure de type « dynamique » ne sont pas autorisée de: S. –

+0

Un peu de lecture dans les limites actuelles de l'utilisation de LINQ avec la dynamique, et quelques façons de travailler autour d'eux: http://weblogs.asp.net/davidfowler/archive/2010/08/04/dynamic-linq-a-little- plus-dynamique.aspx – egoodberry

Répondre

20

Voici une idée: en combinant LINQ avec dynamic, vous pouvez interroger des jeux de données non typés comme s'ils étaient tapés.

Par exemple, supposons que myDataSet est un DataSet non typé. Avec typage dynamique et une méthode d'extension appelée AsDynamic(), ce qui suit est possible:

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 }; 

Voici comment définir la méthode d'extension AsDynamic. Remarquez comment il retourne IEnumerable de dynamique, ce qui le rend approprié pour les requêtes 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); 
    } 
    } 
} 

Par DynamicObject sous-classement, cela profite de liaison personnalisée - où vous prenez sur le processus de résolution des noms de membres vous-même. Dans ce cas, nous lions l'accès des membres get et set à la récupération ou au stockage des objets dans le DataRow sous-jacent.

+0

Awsome! Quand j'ai vu cela, j'ai eu l'idée d'ajouter une méthode d'extension .ExecuteSql qui permet d'utiliser LinqPad directement avec les connexions au serveur SQL. [Check it out ...] (http://stackoverflow.com/a/24885293/1016343) – Matt

+0

Bien que je sois habitué à cela maintenant et me sens naturel, il y a ce puriste en moi qui me fait croire 'AsDynamic' devrait être idéalement nommé 'ToDynamic'. 'To' implique une conversion d'identité tandis que' As' implique une représentation préservant la conversion, comme 'as' le mot-clé. Mais de toute façon, .NET a beaucoup de 'As' similaires, comme' AsReadOnly'. – nawfal

2

La réponse de Joe est cool. J'ai une idée comment simplifier l'utilisation. Si vous ajoutez cela à la classe d'extension:

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; 
    } 
} 

Il permet d'utiliser des requêtes comme celle-ci dans 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: Vous devez ajouter les références suivantes:

  • Ajouter System.Data.OleDb depuis leAssembléeaux propriétés de la requête
  • Ajouter System.Dynamic aux propriétés de la requête

Mise à jour: je remarquai que Joe a ajouté une fonction ExecuteQueryDynamic dans le latest Beta v4.53.03 of LinqPad, qui peuvent être utilisés pour atteindre cet objectif, par exemple:

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

Cela renverra la table Customers de la Base de données Northwind comme IEnumerable<dynamic>, en utilisant une connexion Linq2Sql.

0

Ce que j'ai fait c'est que le résultat est le suivant, mais je pense qu'il y a un meilleur moyen.

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();  
}