2009-01-03 7 views
6

J'ai vu beaucoup de questions à ce sujet, mais je n'ai jamais vraiment eu la réponse dont j'avais besoin.Types anonymes LINQ + vues MVC

Je suis en train de convertir une application web assez importante de Web Forms en MVC et après un certain temps j'ai rencontré un problème avec le passage de données à la vue. Dans l'action que j'exécute le code:

//This is just an example ViewData["QProducts"] = from p in db.Products select new{Name = p.Name, Date = p.ToShortDateString() } ViewData["QUsers"] = from u in db.Users select u;

J'utilise une boucle foreach pour itérer sur les objets en html, comme ceci:

foreach(var q in (IEnumerable)ViewData["QEvents"]) 
{ 
    /*Print the data here*/ 
} 

Avant d'utiliser MVC Je viens d'utiliser un asp:Repeater, mais puisque c'est MVC, je ne peux pas utiliser les contrôles ASP.NET. Comment dois-je transmettre ces données à la vue? Je n'ai pas vraiment la possibilité de ne pas utiliser les types anonymes ici. <%#ViewData.Eval()%> ne fonctionnera évidemment pas.

Des idées?

Répondre

15

Au lieu d'un type anonyme, créez un type pour contenir le nom et la date:

public class NameDate 
{ 
    public string Name { get; set; } 
    public DateTime Date { get; set; } 
} 

Ensuite, utilisez que dans votre requête Linq:

from p in db.Products select new NameDate { Name = p.Name, Date = p.Date } 

typer vue pour être MyView<IEnumerable<NameDate>> et alors faites juste un foreach (var nameDate in ViewData.Model)...

+0

Merci pour une bonne réponse. – impClaw

+0

De rien, mais désolé j'ai oublié de citer le générique, donc ce n'est pas lisible. L'éditera ... –

+1

Il n'y a donc aucun moyen d'éviter de créer une nouvelle classe? J'aime vraiment les types anonymes dans ce cas, ils sont un moyen rapide et fortement typé d'obtenir un champ généré et je n'aime pas créer une nouvelle classe juste pour un cas particulier. Existe-t-il un moyen propre de transmettre le type anonyme à la vue? – emzero

1

explicitement envisager de convertir à une liste et la coulée du ViewData:

ViewData["QUsers"] = (from u in db.Users select u).ToList(); 

foreach(Users u in (List<Users>)ViewData["QUsers"]){ 

    /*Print the data here*/ 

} 

Vous pouvez transmettre des données de plusieurs façons, en utilisant ViewData que vous êtes au-dessus ou TempData pour passer entre les actions. Vous pouvez également utiliser ViewData.Model pour contenir un modèle fortement typé. Notez que vous devrez changer la définition de la vue d'être quelque chose comme

ViewPage<User> 

En ce qui concerne un essai de remplacement agréable répéteur http://www.codeplex.com/MVCContrib. Ils ont un assistant Grid Html qui peut aider.

1

Si vous voulez éviter de créer une classe séparée juste pour afficher votre projection, vous pouvez aussi utiliser un dictionnaire, comme ceci:

from person in personList select new Dictionary<string, string> 
{ 
    { "Name", person.Firstname + " " + person.Lastname }, 
    { "Id", person.Id.ToString() } 
}; 

Vous pouvez alors saisir votre ViewPage à

ViewPage<IEnumerable<Dictionary<string, string>>> 

Et itérer enfin sur la liste dans la vue comme ceci:

<% foreach (Dictionary<string, string> p in (IEnumerable)ViewData.Model) 
{ %> 
    <li> <%=p["Id"] %> - <%= p["Name"] %> </li> 
<% } %> 

Inutile de dire que, l'inconvénient est que votre code est maintenant plutôt plein de "chaînes magiques", ce qui le rend plus sujet aux erreurs en raison de l'absence de vérification de la compilation.

2

Si vous vous sentez un peu paresseux, vous pouvez utiliser ce code ici ... C'est un peu long, mais en gros c'est un emballage pour Reflection ...

var something = { Name = "Jim", Age = 25 }; 
AnonymousType type = AnonymousType.Create(something); 

//then used... 
string name = type.Get<string>("Name"); 
int age = type.Get<int>("Age", -1 /* optional default value */); 

Et voici le code ...

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Reflection; 

namespace Code { 

    /// <summary> 
    /// A convenient method of accessing the values of an 
    /// anonymous type without needing to define a separate class 
    /// </summary> 
    public class AnonymousType { 

     #region Constants 

     private const string EXCEPTION_MISSING_PARAMETER_INFORMATION = 
      "Unable to match the parameter '{0}' to a property in the AnonymousType. This could be due to a casting problem or a Property that does not exist."; 
     private const string EXCEPTION_COULD_NOT_ACCESS_FIELD = 
      "Unable to find a field named '{0}' (of type {1})"; 
     private const string EXCEPTION_COULD_NOT_ACCESS_FIELD_AT_INDEX = 
      "Unable to find a field named '{0}' at the requested index (of type {1})"; 

     #endregion 

     #region Constructors 

     /// <summary> 
     /// Creates a new AutoType for methods that return Anonymus types 
     /// </summary> 
     public AnonymousType(object type) { 
      this._Init(type, false); 
     } 

     /// <summary> 
     /// Creates a new AutoType for methods that return Anonymus types and 
     /// detetrmins if exceptions should be thrown if a type is missing 
     /// </summary> 
     public AnonymousType(object type, bool supressErrors) { 
      this._Init(type, supressErrors); 
     } 

     /// <summary> 
     /// Initalizes the data for the is type 
     /// </summary> 
     private void _Init(object type, bool supressErrors) { 
      this.SupressExceptions = supressErrors; 
      this.m_Type = type.GetType(); 
      this.m_TypeData = type; 
     } 

     #endregion 

     #region Static Routines 

     /// <summary> 
     /// Creates a new Anonymous Type from the provided object data 
     /// </summary> 
     public static AnonymousType Create(object data) { 
      return new AnonymousType(data); 
     } 

     /// <summary> 
     /// Creates a new Anonymous Type from the provided object data 
     /// </summary> 
     public static AnonymousType Create(object data, bool supressErrors) { 
      return new AnonymousType(data, supressErrors); 
     } 

     #endregion 

     #region Private Members 

     /// <summary> 
     /// The type that will be accessed via reflection 
     /// </summary> 
     private Type m_Type; 

     /// <summary> 
     /// The actual typs that is being used 
     /// </summary> 
     private object m_TypeData; 

     #endregion 

     #region Properties 

     /// <summary> 
     /// Determines if errors should be thrown if any casting errors take place 
     /// </summary> 
     public bool SupressExceptions { get; set; } 


     /// <summary> 
     /// Accessess a property by name and returns an object 
     /// </summary> 
     public object this[string property] { 
      get { 
       return this.Get<object>(property); 
      } 
     } 

     #endregion 

     #region Public Methods 

     /// <summary> 
     /// Checks if this Anonymous Type has the specified property 
     /// </summary> 
     public bool Has(string property) { 
      return ((m_Type.GetProperty(property) as PropertyInfo) != null); 
     } 

     /// <summary> 
     /// Returns if this Anonymous type has the specified property and that 
     /// the value matches the type specified 
     /// </summary> 
     public bool Has(string property, Type isType) { 

      //try and get the property 
      PropertyInfo prop = m_Type.GetProperty(property) as PropertyInfo; 

      //If this type doesn't exist at all, just return false 
      if (prop == null) { return false; } 

      //if it does exist, verify the type 
      if (prop.PropertyType.Equals(isType)) { return true; } 
      return false; 

     } 

     /// <summary> 
     /// Returns a type value using the specified type 
     /// </summary> 
     public T Get<T>(string property) { 

      //return this value if needed    
      PropertyInfo prop = m_Type.GetProperty(property) as PropertyInfo; 
      try { 
       return (T)prop.GetValue(this.m_TypeData, null); 
      } 
      catch (Exception ex) { 
       if (this.SupressExceptions) { return default(T); } 
       throw new Exception(
        string.Format(EXCEPTION_COULD_NOT_ACCESS_FIELD, property, typeof(T).Name), 
        ex 
        ); 
      } 
     } 



     /// <summary> 
     /// Returns a type value using the specified type 
     /// </summary> 
     public T Get<T>(string property, object[] index) { 

      //return this value if needed 
      PropertyInfo prop = m_Type.GetProperty(property) as PropertyInfo; 
      try { 
       return (T)prop.GetValue(this.m_TypeData, index); 
      } 
      catch (Exception ex) { 
       if (this.SupressExceptions) { return default(T); } 
       throw new Exception(
        string.Format(EXCEPTION_COULD_NOT_ACCESS_FIELD_AT_INDEX, property, typeof(T).Name), 
        ex 
        ); 
      } 
     } 



     /// <summary> 
     /// Returns a type value using the specified type but includes a default value 
     /// if one it missing 
     /// </summary> 
     public T Get<T>(string property, T defaultValue) { 
      //return this value if needed 
      PropertyInfo prop = m_Type.GetProperty(property) as PropertyInfo; 
      if (prop == null) { return defaultValue; } 
      try { 
       return (T)prop.GetValue(this.m_TypeData, null); 
      } 
      catch (Exception ex) { 
       if (this.SupressExceptions) { return defaultValue; } 
       throw new Exception(
        string.Format(EXCEPTION_COULD_NOT_ACCESS_FIELD, prop, typeof(T).Name), 
        ex 
        ); 
      } 

     } 



     /// <summary> 
     /// Accepts a delegate that will use the names of the passed in 
     /// parameters as properties to map to. If the property does not 
     /// exist, then the method will fail. 
     /// </summary> 
     public void Use<T1>(Action<T1> with) { 

      //set a default for each of the params 
      T1 param1 = default(T1); 

      //get the parameters for this method 
      var paramList = with.Method.GetParameters(); 

      //update each of the parameters    
      string paramName = string.Empty; 
      try { 
       for (int i = 0; i < paramList.Length; i++) { 

        //find the correct matching property for this parameter 
        paramName = paramList[i].Name; 
        switch (i + 1) { 
         case 1: 
          param1 = this.Get<T1>(paramName); 
          break; 
        } 
       } 

      } 
      catch (Exception ex) { 
       throw new ArgumentException(
        string.Format(EXCEPTION_MISSING_PARAMETER_INFORMATION, paramName), 
        ex 
        ); 
      } 

      //otherwise, execute the method provided 
      with(param1); 

     } 



     /// <summary> 
     /// Accepts a delegate that will use the names of the passed in 
     /// parameters as properties to map to. If the property does not 
     /// exist, then the method will fail. 
     /// </summary> 
     public void Use<T1, T2>(Action<T1, T2> with) { 

      //set a default for each of the params 
      T1 param1 = default(T1); 
      T2 param2 = default(T2); 

      //get the parameters for this method 
      var paramList = with.Method.GetParameters(); 

      //update each of the parameters    
      string paramName = string.Empty; 
      try { 
       for (int i = 0; i < paramList.Length; i++) { 

        //find the correct matching property for this parameter 
        paramName = paramList[i].Name; 
        switch (i + 1) { 
         case 1: 
          param1 = this.Get<T1>(paramName); 
          break; 

         case 2: 
          param2 = this.Get<T2>(paramName); 
          break; 
        } 
       } 

      } 
      catch (Exception ex) { 
       throw new ArgumentException(
        string.Format(EXCEPTION_MISSING_PARAMETER_INFORMATION, paramName), 
        ex 
        ); 
      } 

      //otherwise, execute the method provided 
      with(param1, param2); 

     } 



     /// <summary> 
     /// Accepts a delegate that will use the names of the passed in 
     /// parameters as properties to map to. If the property does not 
     /// exist, then the method will fail. 
     /// </summary> 
     public void Use<T1, T2, T3>(Action<T1, T2, T3> with) { 

      //set a default for each of the params 
      T1 param1 = default(T1); 
      T2 param2 = default(T2); 
      T3 param3 = default(T3); 

      //get the parameters for this method 
      var paramList = with.Method.GetParameters(); 

      //update each of the parameters    
      string paramName = string.Empty; 
      try { 
       for (int i = 0; i < paramList.Length; i++) { 

        //find the correct matching property for this parameter 
        paramName = paramList[i].Name; 
        switch (i + 1) { 
         case 1: 
          param1 = this.Get<T1>(paramName); 
          break; 

         case 2: 
          param2 = this.Get<T2>(paramName); 
          break; 

         case 3: 
          param3 = this.Get<T3>(paramName); 
          break; 
        } 
       } 

      } 
      catch (Exception ex) { 
       throw new ArgumentException(
        string.Format(EXCEPTION_MISSING_PARAMETER_INFORMATION, paramName), 
        ex 
        ); 
      } 

      //otherwise, execute the method provided 
      with(param1, param2, param3); 

     } 



     /// <summary> 
     /// Accepts a delegate that will use the names of the passed in 
     /// parameters as properties to map to. If the property does not 
     /// exist, then the method will fail. 
     /// </summary> 
     public void Use<T1, T2, T3, T4>(Action<T1, T2, T3, T4> with) { 

      //set a default for each of the params 
      T1 param1 = default(T1); 
      T2 param2 = default(T2); 
      T3 param3 = default(T3); 
      T4 param4 = default(T4); 

      //get the parameters for this method 
      var paramList = with.Method.GetParameters(); 

      //update each of the parameters    
      string paramName = string.Empty; 
      try { 
       for (int i = 0; i < paramList.Length; i++) { 

        //find the correct matching property for this parameter 
        paramName = paramList[i].Name; 
        switch (i + 1) { 
         case 1: 
          param1 = this.Get<T1>(paramName); 
          break; 

         case 2: 
          param2 = this.Get<T2>(paramName); 
          break; 

         case 3: 
          param3 = this.Get<T3>(paramName); 
          break; 

         case 4: 
          param4 = this.Get<T4>(paramName); 
          break; 
        } 
       } 

      } 
      catch (Exception ex) { 
       throw new ArgumentException(
        string.Format(EXCEPTION_MISSING_PARAMETER_INFORMATION, paramName), 
        ex 
        ); 
      } 

      //otherwise, execute the method provided 
      with(param1, param2, param3, param4); 

     } 

     #endregion 

     #region Working With Arrays 

     /// <summary> 
     /// Returns the specified property as an array of AnonymousTypes 
     /// </summary> 
     public AnonymousType[] AsArray(string property) { 
      object[] values = this.Get<object[]>(property); 
      return values.Select(o => { 
       if (o is AnonymousType) { return (AnonymousType)o; } 
       return new AnonymousType(o); 
      }).ToArray(); 
     } 

     /// <summary> 
     /// Performs the specified action on each value in an array of AnonymousTypes 
     /// </summary> 
     public void WithEach(string property, Action<AnonymousType> action) { 
      foreach (AnonymousType value in this.AsArray(property)) { 
       action(value); 
      } 
     } 

     #endregion 

     #region Static Methods 

     /// <summary> 
     /// Returns the type of data for the provided object 
     /// </summary> 
     public static T Get<T>(object data, string property) { 
      return new AnonymousType(data).Get<T>(property); 
     } 

     /// <summary> 
     /// Returns the type of data for the provided object 
     /// </summary> 
     public static T Get<T>(object data, string property, T defaultValue) { 
      return new AnonymousType(data).Get<T>(property, defaultValue); 
     } 

     /// <summary> 
     /// Returns the type of data for the provided object 
     /// </summary> 
     public static AnonymousType[] AsArray(object data, string property) { 
      return new AnonymousType(data).AsArray(property); 
     } 

     /// <summary> 
     /// Performs the following action on each of the values in the specified 
     /// property value for a user 
     /// </summary> 
     public static void WithEach(object data, string property, Action<AnonymousType> action) { 
      new AnonymousType(data).WithEach(property, action); 
     } 

     #endregion 

    } 
} 
0

tu ne peux pas utiliser le RouteValueDictionary de MVC?