Anonymous LINQ + MVC Types

I have seen many questions about this, but I have never had the answer I need.

I am converting a rather large web application from Web Forms to MVC, and after a while I ran into the problem of transferring data to a view. In 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 objects in html, for example:

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

Before using MVC, I just used asp:Repeater , but since it is MVC, I cannot use ASP.NET controls.

How should I pass this data to a view? I really have no way of not using Anonymous types here. <%#ViewData.Eval()%> will obviously not work.

Any ideas?

+6
linq anonymous-types asp.net-mvc
source share
6 answers

Instead of an anonymous type, create a type to store the name and date:

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

Then use this in your Linq query:

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

Strictly enter your view as MyView<IEnumerable<NameDate>> , and then just do foreach ( var nameDate in ViewData.Model )...

+15
source share

If you feel a little lazy, you can use this code here ... It's a little longer, 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 } } 
+2
source share

Consider explicitly listing and listing 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 transfer data in several ways, using ViewData, as you above, or TempData to transfer 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 as something like

 ViewPage<User> 

For a good repeater replacement, try http://www.codeplex.com/MVCContrib . They have a Grid Html helper that can help.

+1
source share

If you want to avoid creating a separate class just to display your single projection, you can also resort to using a dictionary, for example:

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

Then you can enter your watch page.

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

And finally, let's move on to the list in the view like this:

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

Needless to say, the disadvantage is that your code is currently pretty full of โ€œmagic linesโ€, which makes it more error prone due to the lack of compile time checking.

+1
source share

Can't you use RouteValueDictionary from MVC?

0
source share
0
source share

All Articles