views:

2129

answers:

5

I've seen many questions about this, but i've never really got the answer that I need.

I'm converting a fairly large web application from Web Forms to MVC and after a while I encountred a problem with passing data to the view. In the Action I execute the 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;
I use a foreach loop to iterate over the objects in html, like this:
foreach(var q in (IEnumerable)ViewData["QEvents"]){ 
    /*Print the data here*/
}

Before using MVC I just used a asp:Repeater, but sinec this is MVC I can't use asp.net controls.

How am I supposed to pass this data to the View? I don't really have the option of not using Anonymous Types here. <%#ViewData.Eval()%> obviously won't work.

Any Ideas?

+1  A: 

Consider explicitly converting to a list and casting the ViewData:

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

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

    /*Print the data here*/

}

You can pass data in a few ways, using ViewData as you are above, or TempData to pass between Actions. You can also use ViewData.Model to contain a strongly typed model. Note that you will have to change the definition of the view to be something like

ViewPage<User>

As for a nice repeater replacement try http://www.codeplex.com/MVCContrib. They have a Grid Html Helper that may help.

ccook
+7  A: 

Rather than an anonymous type, create a type to hold the name and date:

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

Then use that in your Linq query:

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

Strongly type your view to be MyView<IEnumerable<NameDate>> and then just do a foreach ( var nameDate in ViewData.Model )...

Mike Scott
Thanks for a good reply.
impClaw
You're welcome - but sorry I forgot to quote the generic, so it's not legible. Will edit it...
Mike Scott
So there's no way to avoid having to create a new class? I really like Anonymous types in this cases, they are a quick and strongly-typed way of getting some generated field and I don't like creating a whole new class just for 1 particular case. Is there a clean way to pass the anonymous type to the view?
emzero
emzero, you can still use an anonymous type but you'll only be able to access its properties via reflection. Creating your own class gives you strong type checking.
Mike Scott
+1  A: 

If you want to avoid creating a separate class just for displaying your one projection, you could also resort to using a dictionary, like so:

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

You can then type your viewpage to

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

And finally iterate over the list in the view like so:

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

Needless to say, the drawback is that your code is now rather full of "magic strings", making it more error prone because of the absence of compile time checking.

Pascal Lindelauf
A: 

If you're feeling a little lazy, you can use this code here... It's a little long, but basically it's a wrapper for 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 */);

And here is the 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

 }
}
Hugoware
A: 

Can't you just use the RouteValueDictionary from MVC?

MRAH