2010-11-17 6 views
0

Comment puis-je obtenir la valeur de l'une des nouvelles propriétés hors requête dans la fonction DoIt ci-dessous?Obtenir des valeurs à partir de la requête LINQ

public object GetData() 
{ 
     var table = GetDataTable(); 
     var view = table.DefaultView; 
     //..... more code 
     var query = from row in view.ToTable().AsEnumerable() 
        group row by row.Field<string>("ShortName") into grouping 
        select new 
         { 
          ShortName = grouping.Key, 
          SCount = grouping.Sum(count => count.Field<int>("ProfCount")), 
          DisplayText = string.Empty 
         }; 
     return query; 
} 

// this code doesn't work 
public void DoIt() 
{ 
    var result = GetData(); 
    string shortName = result.ShortName; 
} 

Merci!

Répondre

2

Vous êtes de retour d'un type anonyme (via select nouveau {}), qui est valable uniquement dans la portée locale. Vous devez créer un type concret et le renvoyer de votre fonction plutôt que d'un objet.

public SomeClass GetData() 
{ 
     var table = GetDataTable(); 
     var view = table.DefaultView; 
     //..... more code 
     var query = from row in view.ToTable().AsEnumerable() 
        group row by row.Field<string>("ShortName") into grouping 
        select new SomeClass 
         { 
          ShortName = grouping.Key, 
          SCount = grouping.Sum(count => count.Field<int>("ProfCount")), 
          DisplayText = string.Empty 
         }; 
     return query; 
} 

// this code doesn't work 
public void DoIt() 
{ 
    var result = GetData(); 
    string shortName = result.ShortName; 
} 

public class SomeClass 
{ 
    public string ShortName { get; set; } 
    public int SCount { get; set; } 
    public string DisplayText { get; set; } 
} 
+0

Peut-on utiliser un type existant? Je préfère utiliser un datarow ici. –

+1

Vous pouvez absolument utiliser un type existant. Vous ne pouvez pas utiliser un type anonyme en dehors de la portée locale. Vous avez juste besoin d'un type qui est publiquement visible parce que vous le renvoyez d'une méthode publique. (Le type explicite pourrait être une classe imbriquée privée si la méthode était privée.) –

+1

@James Kovacs: "Vous ne pouvez pas utiliser un type anonyme en dehors de la portée locale." C'est faux (oui, il y a des instructions incorrectes sur MSDN à cet effet). – jason

4

Les types anonymes ne sont pas appelés types anonymes pour rien. Alors:

définir un type avec un nom (6 lignes de code supplémentaires):

public class Foo 
{ 
    public string ShortName { get; set; } 
    public int SCount { get; set; } 
    public string DisplayText { get; set; } 
} 

maintenant modifier votre signature GetData à (0 lignes de code supplémentaires):

public IEnumerable<Foo> GetData() 

Et votre Requête LINQ à (3 caractères supplémentaires, ou un couple de plus si vous choisissez un nom plus significatif):

var query = 
    from row in view.ToTable().AsEnumerable() 
    group row by row.Field<string>("ShortName") into grouping 
    select new Foo 
    { 
     ShortName = grouping.Key, 
     SCount = grouping.Sum(count => count.Field<int>("ProfCount")), 
     DisplayText = string.Empty 
    }; 
+1

GetData retourneraient un IEnumerable, pas seulement Foo. –

+0

@ Etienne de Martel, m'a eu. Modification :-) –

0

Vous ne pouvez pas sans utiliser la réflexion. Comme il s'agit d'un type anonyme, vous ne pouvez pas utiliser la méthode DoIt() car le nom du type n'est pas connu au moment de la compilation.

1

Eh bien, DoIt n'a aucune idée que result a une propriété nommée ShortName car il est tapé comme object. Vous pouvez créer une classe concrète qui contient les résultats, utiliser la réflexion ou utiliser dynamic. Notez que de toute façon, GetData renvoie vraiment un IEnumerable<T>T est actuellement un type anonyme.

En utilisant une classe concrète:

public class Foo { 
    public string ShortName { get; set; } 
    public int SCount { get; set; } 
    public string DisplayText { get; set; } 
} 

public IEnumerable<Foo> GetData() { 
    var table = GetDataTable(); 
    var view = table.DefaultView; 
    //..... more code 
    var query = from row in view.ToTable().AsEnumerable() 
       group row by row.Field<string>("ShortName") into grouping 
       select new Foo 
        { 
         ShortName = grouping.Key, 
         SCount = grouping.Sum(count => count.Field<int>("ProfCount")), 
         DisplayText = string.Empty 
        }; 
    return query; 
} 

public void DoIt() { 
    var result = GetData(); 
    foreach(var item in result) { 
     Console.WriteLine(item.ShortName); 
    } 
} 

utilisant la réflexion:

public IEnumerable GetData() { 
    var table = GetDataTable(); 
    var view = table.DefaultView; 
    //..... more code 
    var query = from row in view.ToTable().AsEnumerable() 
       group row by row.Field<string>("ShortName") into grouping 
       select new Foo 
        { 
         ShortName = grouping.Key, 
         SCount = grouping.Sum(count => count.Field<int>("ProfCount")), 
         DisplayText = string.Empty 
        }; 
    return query; 
} 

public void DoIt() { 
    var result = GetData(); 
    PropertyInfo property = result.First().GetType().GetProperty("ShortName"); 
    foreach(var item in result) { 
     string shortName = property.GetValue(item, null); 
     Console.WriteLine(shortName); 
    } 
} 
0

Cela me donne ce que je dois:

public object GetData() 
{ 
     var table = GetDataTable(); 
     var view = table.DefaultView; 
     //..... more code 
      var query = from row in view.ToTable().AsEnumerable() 
        group row by row.Field<string>("ShortName") into grouping 
        select new Object[] 
         { 
          grouping.Key, 
          grouping.Sum(count => count.Field<int>("ProfCount")), 
          string.Empty 
         }; 
      return query; 
} 


public void DoIt() 
{ 
    // Note: Pretend that GetData returned only one result 
    object[] result = GetData() as object[]; 
    var shortName = result[0]; 
} 
Questions connexes