Iterate through Custom Object's Property Names and Values - asp.net

I'm trying to create an export Excel/CSV function that will iterate through a custom object and first output the property names and then output the values. I want to use reflection only where necessary so I'm attempting to save the property names when I output the headers and then reuse them to print the values.
Is this possible? I'm a little weary of using reflection in a loop but is there a better way?
Psuedo Code:
Dim Cust1 = New Customer("Tom", "123 Main Street")
Dim Cust2 = New Customer("Mike", "456 Main Street")
Dim Cust3 = New Customer("Joe", "789 Main Street")
Dim CustList As New Arraylist()
CustList.Add(Cust1)
CustList.Add(Cust2)
CustList.Add(Cust3)
CSVExport(CustList, New Customer())
Function CSVExport(List As ArrayList, CustomObject as Object) As StringWriter
Dim sw as Stringwriter
dim proplist as arraylist
'output header
Foreach CustProperty as System.Reflection.PropertyInfo CustomObject.GetType().GetProperties()
proplist.add(CustProperty.Name)
sw.write(CustProperty + ",")
EndFor
'output body
'??
'?? Here I'd like to loop through PropList and List instead of using reflection
'??
Return Sw
End Function

Its all reflection regardless of whether or not you have the names stored in a list.
Do you have a degree of control over the CustomObject. You could store the info within the CustomObject and query that info instead without using reflection. For instance, this is the code I use for my basic domain objects.
public class DomainObject
{
private HashTable _values = new HashTable();
public HashTable Properties
{
get
{
return _values;
}
}
protected void SetValue<T>(string property, T value)
{
if (_values.ContainsKey(property))
{
_values[property] = value;
}
else
{
_values.Add(property, value);
}
}
protected T GetValue<T>(string property)
{
if (_values.ContainsKey(property))
{
return (T)_values[property];
}
else
{
return default(T);
}
}
}
public class TootsieRoll : DomainObject
{
public string Size
{
get { return GetValue<string>("Size"); }
set { SetValue<string>("Size",value); }
}
public string Flavor
{
get { return GetValue<string>("Flavor"); }
set { SetVlaue<string>("Flavor", value); }
}
public int Ounces
{
get { return GetValue<int>("Ounces"); }
set { SetValue<int>("Ounces", value); }
}
}
Now your CSV code would only need to access and loop through the Key=>Value pairs within the "Properties" HashTable it inherited from the DomainObject to get the names and values. But obviously this only works if you have a level of control over your objects necessry to make them inherit from the DomainObject, and it wouldnt involve 30 years of drugery to rewrite all your property accessors. If that is the case, then reflection is your way to go.

In your Pseudo Code you're already populating an arraylist using reflection. If all you want to do is loop through the ArrayList, you can have a look at the ArrayList Class MSDN entry. It shows how to implement IEnumerable to iterate your array list, e.g:
Dim obj As [Object]
For Each obj In CType(myList, IENumberable)
Console.Write(" : {0}", obj)
Next obj
That's untested as is, I'm not sure if it should be CType(myList, IENumberable) or DirectCast(myList, IENumberable).
There is another option, using Object Serialization in VB.Net, a road far less traveled (at least around our offices).

Related

Bind repeater list with enum

I have a requirement where when a user clicks on image a list should be shown with checkboxes and all the categories that is present in DB and user should be able to select the checkboxes. How can this be achieved using asp:repeater control? the caegory is a enum type and can have n number of values. In repeater i have added a checkbox and a label; the label should display the category text.
To start with, you should add the [Description] attribute to each value in your Enum. This allows you to set proper descriptive text for each value. This attribute is in System.ComponentModel, here's an example: -
public enum CalendarShowAsEnum
{
[Description("None")]
None = 10,
[Description("Busy")]
Busy = 20,
[Description("Out Of Office")]
OutOfOffice = 30,
[Description("On Holiday")]
OnHoliday = 40
}
You then need 2 functions: -
One function that takes an Enum type and a ListBox/DropDown as parameters, and adds an entry for each Enum into the list
A helper function that converts the enum into the descriptive title you gave them (example above)
The List function might look as follows (all this is taken from a project I worked on): -
public static void BindNamedEnumList(ListControl list,
Type enumerationType)
{
list.Items.Clear();
Array array = Enum.GetValues(enumerationType);
ListItem item;
string name;
var enumerator = array.GetEnumerator();
if (enumerator != null)
{
while (enumerator.MoveNext())
{
Enum value = enumerator.Current as Enum;
name = EnumHelper.GetEnumName(value);
item = new ListItem(name);
item.Value = Convert.ToInt32(value).ToString();
list.Items.Add(item);
}
}
}
This function takes a Type and a ListControl (which ListBox and DropDownList both inherit from). The Type is the .GetType() of the enum you want to add to the list. Note that it doesn't select any values and that it does depend on each enum value having a defined integer value. The latter part will help you with selecting individual items.
Note the loop calls EnumHelper.GetEnumName(value) - this is the helper function that uses the Description attribute I mentioned at the start. This function looks like: -
public static string GetEnumName(object value)
{
string retVal = string.Empty;
try
{
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
retVal = ((attributes.Length != 0) ? attributes[0].Description : value.ToString());
}
catch (System.NullReferenceException)
{
}
finally
{
if (string.IsNullOrEmpty(retVal))
{
retVal = "Unknown";
}
}
return retVal;
}
It uses reflection, so you'll need to add an Imports for System.Reflection
To use the list function to bind a set of Enum values to the list, simply call
{HelperClass}.BindNamedEnumList(myListBox, typeof({MyEnumType})

How do you make a class method modify itself?

asp.net C#4
I have a simple class to working with query strings.
A new instance is created like this:
public QueryString(string querystring)
{
try
{
_table = new Hashtable();
if (querystring.Length > 0)
{
foreach (string pair in querystring.Split('&'))
{
string[] item = pair.Split('=');
_table.Add(item[0].ToLower(), item[1]);
}
}
}
catch (Exception)
{
}
}
I want to add a method to this that will remove a key value pair. I don't want it to return a new querystring, I just want it to remove the pair from the current instance. Not sure how to do that since it says I can't assign a value to 'this'
public void Remove(string key)
{
String querystring = this.ToString();
try
{
_table = new Hashtable();
if (key.Length > 0)
{
foreach (string pair in querystring.Split('&'))
{
string[] item = pair.Split('=');
if (item[0] != key)
{
_table.Add(item[0].ToLower(), item[1]);
}
}
this = _table;
}
}
catch (Exception)
{
}
}
You're overcomplicating things. Since your class's state is made up of the _table field, all you need to do is remove the item with the given key from that field.
The following example replaces your untyped Hashtable wit a strongly-typed Dictionary. I also chose to initialize the dictionary with a LINQ statement, but you could keep your old code there if you prefer.
public class QueryString
{
private readonly Dictionary<string, string> _table;
public QueryString(string querystring)
{
if (querystring.Length > 0)
{
var pairs =
from pair in querystring.Split('&')
let item = pair.Split('=')
select new {key = item[0], value = item[1]};
_table = pairs.ToDictionary(p => p.key, p => p.value);
}
}
public void Remove(string key)
{
_table.Remove(key);
}
}
You cannot assign a value to this since it is a reference to the object itself.
However, if you remove the line this = _table; , isn't things working as they should then? I guess your ToString() is somewhat using the hashtable to generate a "printer friendly" QueryString, and if that is the case, the way I see it, your Remove() method should be working (since you are replacing the _table variable with a new HashTable not including the key-value pair you want to exclude).
you are passing a querystring into the class so the original querystring IS intact.
However you then break down the querystring into a a Hashtable of key/value pairs. If you want to keep THAT intact you need to clone the HashTable and perform the remove on the clone.
In any case it's probably a good idea to keep the querystring you are passing in as a constructor parameter in a member variable for safe keeping.

Elegant way to bind html radio buttons <=> Java enums <=> mysql enums in Play?

The Goal is to have a list of options (that a user can chose through radio buttons) in one place(for eg: a yaml config file). No other place should have this list hard-coded
I've done something similar to create select elements, and I think enums worked just fine. Doing radio buttons should be very similar. I've set it up so that the labels can be defined in the messages file. I'm going to try to excerpt the relevant portions from my larger auto-form-generation code (using FastTags) the best I can. It's a bit heavy for this one case but it makes sense in the larger system.
I use the tag like #{form.selector 'order.status' /}, which looks find the variable named order in the template, sees that status is declared as public Status status, and then goes to find all the values of the Status enum and generate options for them in the select element.
First, I use a FieldContext object which just contains a bunch of info that's used by the other code to determine what to generate along with some utility methods:
public class FieldContext {
public final Map<?,?> args;
public final ExecutableTemplate template;
public final int fromLine;
public Class clazz = null;
public Field field = null;
public Object object = null;
public Object value = null;
private Map<String,String> attrs = new HashMap<String,String>();
private Map<String,Boolean> printed = new HashMap<String,Boolean>();
private List<Option> options;
...
Then I have this in another helper class (its info gets added to the FieldContext):
public List<Option> determineOptions(FieldContext context) {
List<Option> options = new ArrayList<Option>();
if (context.field.getType().isEnum()) {
for (Object option : context.field.getType().getEnumConstants()) {
options.add(new Option(option.toString(), Message.get(option.toString())));
}
}
return options;
}
then the tag declaration is
public static void _selector(Map<?,?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) {
String field_name = args.get("arg").toString();
TagContext.current().data.put("name", field_name);
SelectHelper helper = HelperFactory.getHelper(SelectHelper.class);
try {
FieldContext context = new FieldContext(field_name, args, template, fromLine);
helper.autoconfigure(context);
TagContext.current().data.put("selected", helper.determineValue(context));
out.print("<div class=\"formutil-field formutil-selector\">");
out.print("<label for=\"" + context.getAttr("id") + "\">");
out.print(helper.findOrCreateLabel(context));
out.print("</label>");
out.print("<select");
context.printAttribute(out, "id", "name");
out.print(">");
if (context.hasOptions()) {
for (Option option : context.getOptions()) {
out.print("<option value=\"" + option.value + "\">" + option.label + "</option>");
}
}
out.print("</select>");
context.printErrorIfPresent(out);
context.printValidationHints(out);
out.println("</div>");
}
...
}

Pass property from controller to Model

I am trying to pass a variable from a method in my Controller to a method in a Model. Since the method in the Model takes one argument (which was designed earlier), I cannot pass my variable as an argument to the method in the Model. And also, the method in this Model is called by other controllers too, so if I change the argument, I have to change all the controllers too, which would be a tedious task.
What I have been trying so far is- I created one MyVariableClass and declared a property. Then I instantiated that class and set the property string to the variable that I wanted to pass. Now, in my Model's method, I instantiated the same MyVariableClass again, but when I did that, the value of the variable was set to null. The code I have right now is -
public ActionResult ItemInformation( string id)
{
//Pass a string to MyVariable
MyVariableVClass params = new MyVariableClass();
params.myVariable = "abc";
//This is what My Model is taking as an argument(id), and I don't want to
//pass mYvariable along with that argument because it will break other controllers
// too which calls this method
var itemInformation = _repository.GetItemInformation(id);
return View(itemInformation);
}
and MyVariableClass
public class MyVariableClass
{
public string myVariable { get; set; }
}
and the method in My Model
public IList<Items> GetItemInformation(string itemId)
{
MyVariableClass webType = new MyVariableClass();
var _params = webType.myVariable;
//Check this variable and perform database query
if (_params =="this")
{
var query = myFirstQuery;
}
else
{
var query = mySecondQuery;
}
//return ....
}
Anybody has solution to this? Thanks in Advance!
Any reason why subclassing your model and overriding the GetItemInformation method wouldn't work? Or, even easier, why not just overload the GetItemInformation method with one that takes two strings? Your other controllers can still use the one that only takes a single string.
public IList<Items> GetItemInformation(string itemId, MyVariableClass webType)
{
var _params = webType.myVariable;
//Check this variable and perform database query
if (_params == "this")
{
var query = myFirstQuery;
}
else
{
var query = mySecondQuery;
}
//return ....
}
public IList<Items> GetItemInformation(string itemId)
{
MyVariableClass fauxType = new MyVariableClass();
fauxType.myVariable = "not this";
return GetItemInformation(itemId, fauxType);
}
Try using session variable.

ASP.Net Custom Control

I am developing a custom control that needs to display a dropdownlist as a composite control.
The drop down list gets populated from a Rest web service. The problem I am facing is that the dropdownlist only has DataTextField and DataValueField but I need a way of storing more values in the control i.e. I have a couple of other properties I need to access for the selected item.
What is the best way of going about this?
Here is the code I have so far:
[ValidationProperty("SelectedValue")]
public class SelectSurveyControl : Panel
{
private DropDownList ddlSurveys;
public string SelectedSurveyId
{
get
{
return SelectedValue;
}
}
public string SelectedSurveyJavascriptEmbedCode
{
get
{
return this.ddlSurveys.SelectedItem.Attributes[""];
}
}
public string SelectedValue
{
get
{
return ddlSurveys.SelectedValue;
}
set
{
if (ddlSurveys == null)
{
ddlSurveys = new DropDownList();
}
ddlSurveys.SelectedValue = value;
}
}
protected override void OnLoad(EventArgs e)
{
base.OnInit(e);
if (ddlSurveys == null)
{
ddlSurveys = new DropDownList();
}
IList<Survey> surveys = GetSurveys();
this.ddlSurveys.DataSource = surveys;
this.ddlSurveys.DataTextField = "title";
this.ddlSurveys.DataValueField = "id";
this.ddlSurveys.DataBind();
ddlSurveys.SelectedValue = this.SelectedValue;
ddlSurveys.CssClass = "umbEditorTextFieldMultiple charlimit";
ddlSurveys.Attributes.Add("SurveyId", SelectedSurveyId);
ddlSurveys.Attributes.Add("JavascriptEmbedingCode", SelectedSurveyId);
this.Controls.Add(ddlSurveys);
}
public IList<Survey> GetSurveys()
{
...
}
}
Try using a string join/split to store and retrieve the various values, then you don't have to customize your dropdown list very much.
For Example:
Text: Some Title
Value: 1|testing test|2/12/2010
This will let you store as many values as you want, so long as you choose an appropriate character to join and split on. I usually use the bar, as in my example above.
Side Note: I was looking at your selected value set handler and it needs some tweaking. You shouldn't check for a null drop down list, instead you should call EnsureChildControls() before each get and set instead. Make sure you override the CreateChildControls() method and create your controls there.
You could use a hidden field and iterate thru a copy of the returned Surveys like this:
foreach(Survey s in Surveys){
string val = s.id + ":" + s.<property1> + ":" + s.<property2>;
hiddenField.Value += val +",";
}
When you need to read from the hidden field, you use String.Split to separate the values into arrays using ',' as the separator and in each array, you split again using ':'.
In the first split Array1[0] who be the survey id and Array1[n!=0] would be the properties of the Survey with the id = Array1[0]. Array[n!=0] would then be split into Array2.
I would suggest handling empty property values with an empty string or something or else you might end up with unequal lengths especially if you specify StringSplitOptions.RemoveEmptyEntries.
Agricfowl

Resources