DropDownList and EditorTemplates - c#

I have an entity called Invoice that I am extending for data annotations
[MetadataType(typeof(InvoiceMetadata))]
public partial class Invoice
{
// Note this class has nothing in it. It's just here to add the class-level attribute.
}
public class InvoiceMetadata
{
// Name the field the same as EF named the property - "FirstName" for example.
// Also, the type needs to match. Basically just redeclare it.
// Note that this is a field. I think it can be a property too, but fields definitely should work.
[HiddenInput]
public int ID { get; set; }
[Required]
[UIHint("InvoiceType")]
[Display(Name = "Invoice Type")]
public string Status { get; set; }
[DisplayFormat(NullDisplayText = "(null value)")]
public Supplier Supplier { get; set; }
}
The Uhint[InvoiceType] causes the InvoiceType Editor Template to be loaded for this element.This templates is defined as
#model System.String
#{
IDictionary<string, string> myDictionary = new Dictionary<string, string> {
{ "N", "New" }
, { "A", "Approved" },
{"H","On Hold"}
};
SelectList projecttypes= new SelectList(myDictionary,"Key","Value");
#Html.DropDownListFor(model=>model,projecttypes)
}
i have many such hardcoded status lists in my program.I say hardcoded because they are not fetched from the database. Is there any other way to create templates for drop downs? How do I declare an enum in the model and have the drop down load the enum without having to pass it through a view model ?

Rather than "hard coding" your statuses I would either create an Enum or a Type Safe Enum. For your example I would use the latter.
For each of your required "status lists" create a separate class with your desired settings:
public sealed class Status
{
private readonly string _name;
private readonly string _value;
public static readonly Status New = new Status("N", "New");
public static readonly Status Approved = new Status("A", "Approved");
public static readonly Status OnHold = new Status("H", "On Hold");
private Status(string value, string name)
{
_name = name;
_value = value;
}
public string GetValue()
{
return _value;
}
public override string ToString()
{
return _name;
}
}
Utilizing reflection you can now get the fields of this class to create your required drop down lists. It would be beneficial to your project to either create an extension method or a helper class:
var type = typeof(Status);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
Dictionary<string, string> dictionary = fields.ToDictionary(
kvp => ((Status)kvp.GetValue(kvp)).GetValue(),
kvp => kvp.GetValue(kvp).ToString()
);
You can now create your select list much like you are currently doing:
var list = new SelectList(dictionary,"Key","Value");
Which will create a drop down list with the following html:
<select>
<option value="N">New</option>
<option value="A">Approved</option>
<option value="H">On Hold</option>
</select>

Related

How to implement reflection to get an object field based on a generic enum

I think it is a rather simple issue, but I can't really get my head around reflection things.
I'm looking for a way to initialize DropDownList based on enums with default value in Razor page
I have the following CustomModel class which have many enum properties:
public class CustomModel {
public EnumA A { get; set; }
public EnumB B { get; set; }
// Other properties based on EnumC, EnumD, etc.
}
And the view model where I want to populate each enum:
public class CustomViewModel
{
public CustomModel Custom { get; set; }
public SelectList As { get; set; }
public SelectList Bs { get; set; }
// Other SelectList properties for all the different enums
public CustomViewModel(CustomModel custom) // Will need to think about some dependency injection
{
Custom = custom;
As = InitializeDropDownList<A>();
Bs = InitializeDropDownList<B>();
// Idem for all other enums
}
// Probably move it to an extension method
private SelectList InitializeDropdownList<T>() where T : struct, Enum
{
// string value = ...
new SelectList(new Descriptions<T>(), value)
}
}
// Returns the string values corresponding to the generic enum
public class Descriptions<T> : List<string> where T : struct, Enum
{
public Descriptions()
{
// Enumerates the T values
foreach (T e in Enum.GetValues(typeof(T)))
{
// A bit simplified, because I actually use an other Description extension method
Add(e.ToString());
}
}
}
Is there a way to get the value in the InitializeDropdownList function using generics and reflection as follows :
T = EnumA => value = CustomModel.A
T = EnumB => value = CustomModel.B
...
I think I should use FieldInfo[] fields = Custom.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); and compare it to the actual name of T and then get the value of the field whih I don't know how to do. Does anyone have an idea of how to achieve it?
Thanks for any insights!

Serialize Store C# class in camelCase

I'm storing data in a Firestore DB using .net. I'm using FirestoreData and FirestoreProperty attributes to control how objects are serialized to the DB. C# propeties, by default, are in PascalCase and I'd like them to be serialized in camelCase. I know I can set the name a property will be serialized in the FirestoreProperty attribute, but it's a really tedious and error proner task. Is there any way to configure Firestore .net client to by default serialize properties in camelCase?
Thanks
The FirestorePropertyAttribute defines two constructors. One allows to add a name by providing the parameter name:
The name to use within the Firestore document.
So you can simply set it for a property like
[FireStoreProperty("anyCase")]
public string AnyCase{ get; set; }
Doing this a silent way is not possible without modifying the underlying type. A possible approach is to implement a reflection based Document converter, changing the property names at runtime. You only need to define the converter once for each data class. Here is a possible approach:
//Sample data class
[FirestoreData(ConverterType = typeof(CamelCaseConverter<CustomCity>))]
public class CustomCity
{
public string Name { get; set; }
public string Country { get; set; }
public long Population { get; set; }
}
//Sample data class
[FirestoreData(ConverterType = typeof(CamelCaseConverter<CustomPerson>))]
public class CustomPerson
{
public string Name { get; set; }
public uint Age { get; set; }
}
//Conversion of camelCase and PascalCase
public class CamelCaseConverter<T> : IFirestoreConverter<T> where T : new()
{
public object ToFirestore(T value)
{
dynamic camelCased = new ExpandoObject();
foreach (PropertyInfo property in typeof(T).GetProperties())
{
string camelCaseName =
char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1);
((IDictionary<string, object>)camelCased)[camelCaseName] = property.GetValue(value);
}
return camelCased;
}
public T FromFirestore(object value)
{
if (value is IDictionary<string, object> map)
{
T pascalCased = new T();
foreach (PropertyInfo property in typeof(T).GetProperties())
{
string camelCaseName =
char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1);
property.SetValue(pascalCased, map[camelCaseName]);
}
return pascalCased;
}
throw new ArgumentException($"Unexpected data: {value.GetType()}");
}

Public fields vs. properties as they relate to constructors

I'm pretty new to C# and scripting in Unity.
Up until now, when making a C# (Non-MB) class, I'd use the following pattern.
public class Item
{
public string itemName;
public Item(string newItemName)
{
itemName = newItemName;
}
}
and in some other script I could use Item rock = new Item("Rock"); or whatever.
Now, I'm using properties like the following.
public class Item
{
string itemName;
public string ItemName
{
get { return itemName; }
set { itemName = value; }
}
}
and in some other script I use Item rock = new Item(); rock.ItemName = "Rock";
Realistically, I use auto-implemented properties more often than not, and there are many more fields than just the item's name. When I used public fields, it was easy to set all these fields in the constructor. Now, I'm not sure how to, or if it's possible (if I want to keep them private). Therefore, I find myself having to use a default constructor and then settings all the fields with the properties like rock.ItemWeight = "1 lb" rock.ItemColor = "gray" rock.ItemType = "blunt instrument"...etc. for every item and field each time a new item is created. Is there a better way?
Never use public fields!
Set your brain to use auto-properties like this by default:
public string ItemName { get; } // without even the setter!
Only add the setter only when it is absolutely necessary for the class to be mutable.
Who says you can't set it in the constructor? Look:
public class Item
{
public string ItemName { get; }
public Item(string newItemName)
{
ItemName = newItemName;
}
}
You can also use an object initialisation block at the end:
public class Item
{
// setter is needed for this to work.
public string ItemName { get; set; }
}
var item = new Item() { ItemName = "" };
I just wanted to know how to set members within the constructor when those members are private.
Constructors are part of the class, so they can set private properties if they have a setter. It's just that outside the class, the properties are invisible.
You can use autoproperties and constructor like this:
public class Item
{
public string ItemName {get;set;}
public Item()
{
}
public Item(string itemName)
{
this.ItemName = itemName;
}
}
Then you can just do the following
Item item1 = new Item(value);
Or
Item item1 = new Item(){ItemName=value};
Your question actually boils down to wheather to set the members within the constructor or from the outside. As you´ve written in the comments you don´t wand an Item to exist without the members being set. So your decission depends on one single point: do you want to be able to modify the values which were set initially?
If you want this, the easiest way is to make those members publicily accessable and give them an initial value:
class Item
{
public string ItemName { get; set;} = "InitialValue";
}
or before C#6:
class Item
{
public string ItemName { get; set;}
public Item()
{
ItemName = "InitialValue";
}
}
If you don´t want to modify the members, you can use a private readonly backking-field instead:
class Item
{
private readonly string _itemName = "InitialValue";
public string ItemName { get { return _itemName; } }
}
Alternativly you can also create a default-constructor and set the initial value for ietmName there:
class Item
{
private readonly string _itemName;
public string ItemName { get { return _itemName; } }
public Item()
{
_itemName = "InitialValue";
}
}
You could also use a property which is readable from outside the class, but changable only within the class by using a private setter. However this is slightly different than using a private readonly backing-field, as in the latter case only the constructor is able to set the value, whilst in the former case evry code within your Item-class can change the value of the property.

Inheritance: Set Values of Abstract Dictionary

I am currently working on a small project that includes an abstract baseclass of sorts, which defines some methods that require a dictionary. Yet the dicitionary is individual for each derived class.
Therefore I defined the dictionary in my baseclass as follows:
abstract internal Dictionary<string, column> columnNames { get; }
and in each inheriting class I set that dicitonary like so:
internal override Dictionary<string, column> columnNames
{
get
{
return new Dictionary<string, column>{
{"partID", new column{ name = "ID", show = false, type = typeof(int)}},//false
{"partName", new column{ name = "Name", show = true, type = typeof(string)}}
};
}
}
Now if I want to change properties of the dicitonary I tried the following:
columnNames["partID"].show = true;
Which does not work. There is no exception, the value in the dicitionary simply does not chance.
I also wrote a set-Function within my base-class:
public void setDictEntry(string key, column value)
{
columnNames[key] = value;
}
But it still did not work. I was wondering: Is it because my dictionary does not have a set property?
For completion, here the definition of the column class:
class column
{
public string name { get; set; }
public bool show { get; set; }
public string format { get; set; }
public System.Type type { get; set; }
}
As always, sorry for my bad english and thanks for your help!
Of course it doesn't change - you're always returning a different Dictonary! You'll have to return an existing dictionary (for example, by storing the dictionary in a field) to have it (effectively) mutable.
Dictionary<string, column> _columnNames =
new Dictionary<string, column>{
{"partID", new column{ name = "ID", show = false, type = typeof(int)}},//false
{"partName", new column{ name = "Name", show = true, type = typeof(string)}}
};
internal override Dictionary<string, column> columnNames
{
get
{
return _columnNames;
}
}

Lambda Expression assign source object

Context :
Since we are developing in C# MVC3, we wanted to have some classes designed to handle the tables on a web page. (Pagination / search / etc...).
So we finally found that it could be the best to have the following classes :
The table object that will hold all other object and knows the current page / current search etc... (misc informations)
public class Table<T> where T : IPrivateObject
{
...
public ICollection<Column<T>> Columns { get; set; }
public ICollection<Row<T>> Rows { get; set; }
public ICollection<RowMenu<T>> Menus { get; set; }
public ICollection<T> Items { get; set; }
public Table(
ICollection<T> inputItems,
ICollection<Column<T>> columns,
ICollection<RowMenuItem<T>> rowMenuItems,
...)
{
...
this.Columns = columns;
}
The column object that knows which property should be displayed and and a header value
public class Column<T> where T : IPrivateObject
{
public string Value { get; set; }
public Expression<Func<T, object>> Property { get; set; }
public Column(Expression<Func<T, object>> property, string value)
{
this.Property = property;
this.Value = value;
}
}
The other classes are not really interesting so i won't post them here.
In the controller, we use these classes like that :
public ActionResult Index(string search = null, string sort = null, int order = 1, int take = 10, int page = 1)
{
ICollection<Person> people = prismaManager.PersonManager.Search(search);
ICollection<Column<Person>> columns= new List<Column<Person>>();
columns.Add(new Column<Person>(Person => Person, "Person"));
columns.Add(new Column<Person>(Person => Person.LastMembershipApproval, "Last Membership approval"));
Table<Person> table = people.ToTable(columns);
}
We are now writing a helper that will display the table correctly.
It works well for the header but we face a problem with the Expressions when we want to use the #Html.DisplayFor() helper.
This is what we currently have for the content :
private static string TableRows<T>(HtmlHelper<Table<T>> helper, Table<T> table) where T : IPrivateObject
{
StringBuilder sb = new StringBuilder();
foreach (var item in table.Items)
{
sb.AppendLine("<tr>");
foreach (var column in table.Columns)
{
sb.AppendLine("<td>");
sb.AppendLine(helper.DisplayFor(obj => ??? ).ToString()); // How should I use the Expression that is stored in the column but for the current element ?
sb.AppendLine("</td>");
}
sb.AppendLine("</tr>");
}
return sb.ToString();
}
For this to work, we should set the value of the "Person" parameter from the Expression stored in the column to the current item.
new Column<Person>(Person => Person, "Person"));
How are we supposed to do that ?
Should we (if it is possible) modify the expression to set the value ?
Should we recreate a new Expression using the old one as a basic expression ?
I've been searching for 3 days now and I can't find any answers.
Thanks for your help.
UPDATE :
The problem is (as #Groo & #Darin Dimitrov said) that the Helper is of type HtmlHelper> and not HtmlHelper.
Any idea how I could get an HtmlHelper from a HtmlHelper> ?
UPDATE :
Person class is as following :
public class Person : IPrivateObject
{
public int Id { get; set; }
public int? AddrId { get; set; }
[DisplayName("First Name")]
[StringLength(100)]
[Required]
public string FirstName { get; set; }
[DisplayName("Last Name")]
[StringLength(100)]
[Required]
public string LastName { get; set; }
[DisplayName("Initials")]
[StringLength(6)]
public string Initials { get; set; }
[DisplayName("Last membership approval")]
public Nullable<DateTime> LastMembershipApproval { get; set; }
[DisplayName("Full name")]
public string FullName
{
get
{
return FirstName + " " + LastName;
}
}
public override string ToString()
{
return FullName;
}
}
Here's how you could proceed. Start by writing a custom view data container implementation which could be as simple as:
public class ViewDataContainer : IViewDataContainer
{
public ViewDataContainer(ViewDataDictionary viewData)
{
ViewData = viewData;
}
public ViewDataDictionary ViewData { get; set; }
}
and then just instantiate a HtmlHelper<T> which is what you need:
private static string TableRows<T>(HtmlHelper<Table<T>> helper, Table<T> table) where T : IPrivateObject
{
var sb = new StringBuilder();
sb.AppendLine("<table>");
foreach (var item in table.Items)
{
sb.AppendLine("<tr>");
foreach (var column in table.Columns)
{
var viewData = new ViewDataDictionary<T>(item);
var viewContext = new ViewContext(
helper.ViewContext.Controller.ControllerContext,
helper.ViewContext.View,
new ViewDataDictionary<T>(item),
helper.ViewContext.Controller.TempData,
helper.ViewContext.Writer
);
var viewDataContainer = new ViewDataContainer(viewData);
var itemHelper = new HtmlHelper<T>(viewContext, viewDataContainer);
sb.AppendLine("<td>");
sb.AppendLine(itemHelper.DisplayFor(column.Property));
sb.AppendLine("</td>");
}
sb.AppendLine("</tr>");
}
sb.AppendLine("</table>");
return sb.ToString();
}
UPDATE:
The previous example doesn't handle value types because the expression in the column is of type Expression<Func<T, object>> and when you are pointing to a value type property the value will be boxed and ASP.NET MVC doesn't allow such expressions to be used with the template helpers. To remedy this problem one possibility is to test whether the value was boxed and extract the actual type:
sb.AppendLine("<td>");
var unary = column.Property.Body as UnaryExpression;
if (unary != null && unary.NodeType == ExpressionType.Convert)
{
var lambda = Expression.Lambda(unary.Operand, column.Property.Parameters[0]);
sb.AppendLine(itemHelper.Display(ExpressionHelper.GetExpressionText(lambda)).ToHtmlString());
}
else
{
sb.AppendLine(itemHelper.DisplayFor(column.Property).ToHtmlString());
}
sb.AppendLine("</td>");
There are several things you should change.
The thing that first surprised me is that your table both has a list of Columns and Rows. You should change the design to something like: a Table has a list of Rows, and each Row has a list of Columns (or vice versa).
But this remark is less relevant. I guess a "Column" is something like a "Column definition" and contains no data, but in that case I see no point in having ICollection<Row<T>> instead of just ICollection<T>.
Next, you probably want to store a delegate, like Func<T, object>, instead of an Expression<Func<T, object>>.
Property should at least have private setters (or, even better, readonly backing fields). This is not something that you would want other parts of your code to change.
Naming is very confusing IMHO. I would choose better property names. If I got you right, Value and Property should actually be called HeaderName and GetValue, respectively.
Having said all that, I would change Column to something like this:
public class Column<T> where T : IPrivateObject
{
private readonly string _name;
private readonly Func<T, object> _valueGetter;
/// <summary>
/// Gets the column name.
/// </summary>
public string Name
{
get { return _name; }
}
/// <summary>
/// Gets the value of this column from the
/// specified object.
/// </summary>
/// <param name="obj">The object.</param>
/// <returns></returns>
public object GetValueFrom(T obj)
{
return _valueGetter(obj);
}
public Column(string columnName, Func<T, object> valueGetter)
{
_name = columnName;
_valueGetter = valueGetter;
}
}
And then simply use this in your loop:
sb.AppendLine(column.GetValueFrom(item).ToString());
You need to compile the expression using expression.Compile() (you have the property expression in your column.Property). This will give you a delegate. You can pass the object there and get the value. You also will need to pass the person, or the T, to the helper method.

Categories

Resources