c# dynamically change tuple size - c#

Basically i want to export some lists. Theese lists contains different columns. For example
StudentID - StudentName - Assignment1 - Assignment2 - Final Exam
But sometimes there are different exams. I am using tuple class to add theese lists.
exportList.Add(new Tuple<string, string, string>(item.ID, item.Name + item.Surname, payList[i]));
this contains 3 columns. Is there a way to change tuple parameters dynamically?

In your case i would not use Tuples. I rather would use classes, which inherit from one another, to allow variating properties.
For example:
public class Student // only a sample
{
public string Name
{
get;
set;
}
}
public class StudentWithGrade : Student
{
public int Grade
{
get;
set;
}
}
Your list could than accept types of Student, which can be a Student or StudenWithGrade or anything that inherit from student.
This makes your code also more readable for others.
EDIT
If you really need some thing dynamic, use a list of dictionaries,
where one entry in the list represents a kind of row and the entries
in the Dictionary<string, object> represents the columns.

No, tuples are immutable. It seems like you're not using tuples to what they're meant to be used.
If you insist in not using classes to hold your data, your best bet are dictionaries:
exportList.Add(new Dictionary<string, string>() { { "ID", item.ID }, { "Name", item.Name }, { "Surname", item.Surname }, { "PayList", payList[i] } });
Now if you get the whole item, you'll be able to edit each item as follows:
exportList[0]["ID"] = 11;
exportList[0]["Name"] = "Matías";
exportList[0]["Surname"] = "Fidemraizer";
Another option is using a dynamic object like ExpandoObject:
dynamic person = new ExpandoObject();
person.ID = 11;
person.Name = "Matías";
person.Surname = "Fidemraizer";
exportList.Add(person);
Since ExpandoObject implements IDictionary<string, object>, you can access properties using regular dot syntax, or you can cast an ExpandoObject to IDictionary<string, object> and access properties as dictionary keys:
dynamic person = exportList[0];
string name = person.Name;
// or...
IDictionary<string, object> person = (IDictionary<string, object>)exportList[0];
string name = (string)person["Name"];
BTW, I still feel that you should go with designing and using an actual class to hold your persons' data either way...

Related

Saving Data of type (string, List<string>) in Viewstate

What collection to use to store Key, multiple values in ViewState. My data is of type string Key, List<string> Values, i.e. a key can have one or more string values
Say I have countries and cities that I need to save in ViewState.
I will have
USA - NY, DC, Chicago
Canada - Toronto, Vancouver, Montreal
etc.
I'd like to save this data to ViewState and was wondering how best to save this without creating custom serializable objects etc. They're just strings.
Should I save them as string key, comma separated List Values?
It seems that this simple data could be stored in a Dictionary<string, List<string>>. The dictionary keys will be the State/Region and the values will be the cities list.
You can initialize it like this
Dictionary<string, List<string>> locations = new Dictionary<string, List<string>>
{
{ "USA", new List<string> {"NY", "DC", "Chicago"}},
{ "Canada", new List<string> {"Toronto", "Vancouver", "Montreal"}},
};
Then set the ViewState entry with that data
ViewState["locs"] = locations;
You Could Use A Dictionary as mentioned in the comments, which would be the most efficient way when trying to access your data in that collection.You could also Use A Class Collection like so:
public class CollectingData
{
public Collection<Countries> CollectCountries ;
public CollectingData()
{
CollectCountries = new Collection<Countries>
}
}
public class Countries
{
public string country { get; set;}
public List<string> states { get; set;}
}
And in the class you are trying to write to the collection in, for Every Country you Call a new Country of class with the Countries name as your string and then add your states or territories to your list. Then you can add that instance of the class to your collection. like so:
Countries mycountry = new Countries();
mycountry.states = new List<string>();
mycountry.country = "United States";
mycountry.states.Add("thisstate", "thatState");
CollectCountries.Add(mycountry);
You can do something similar to this for each country you want to add. Hopefully this helps you out. the Dictionary route will definitely be faster and easier than this however.

how to get corresponding "Name"

I have a C# dictionary in which I have a corresponding NAME against the ID.
Dictionary<string, List<object>> dict = new Dictionary<string, List<object>>
{
{ "ID", new List<object> { "Id1", "Id2" } },
{ "NAME", new List<object> { "True", "False" } }
};
foreach (var id in dict["ID"])
{
Console.WriteLine(id);
//how to get corresponding "Name". For "Id1" = "True" and for "Id2" = "False"
}
In above code I loop through ID, but how to get corresponding NAME?
I think a better design would be to create a class with the two properties and then iterate. If you find yourself having to sync different data structures for simple data representations then I'd suggest rethinking the design.
public class MyClass
{
public string Id { get; set; }
public bool Name { get; set; }
}
And then hold a List<MyClass> which when you iterate:
foreach (var item in list)
{
// Now access item.Id, item.Name
}
The use of dictionaries is good when you have some sort of natural key for your data and you want to access access an item by that key. As the items are accessed via a hash function accessing by key is done in O(1) whereas searching in a list is O(n). However in your case you are iterating all items in any case so no need for dictionary and arranging the data in a class is a better design.
A bit about the differences and some references:
what is the difference between list<> and dictionary<> in c#
List vs ArrayList vs Dictionary vs Hashtable vs Stack vs Queue?
If you do have control over dictionary data it's best to either use Gilad's answer and store everything in List<MyClass> or to use Dictionary<string, bool> :
Dictionary<string, bool> dict = new Dictionary<string, bool>()
{
{ "Id1", true }, { "Id2", false },
};
But if you do not have control over format of this data and get it as a dictionary from somewhere (for example web service) you could utilize .Zip method to convert this dictionary into one list of either anonymous objects/custom class or Tuples, where Item1 is Id and Item2 is value:
// anonymous object
var data = dict["ID"].Zip(dict["NAME"], (x, y) => new
{
ID = x,
NAME = y
}).ToList();
// tuple
// List<Tuple<object, object>> data = dict["ID"].Zip(dict["NAME"], Tuple.Create).ToList();
foreach (var obj in data)
{
Console.WriteLine(obj.ID + " " obj.NAME);
}
The other answers are probably what you should do to better structure your code. However, if you need to stick to your original use case, you could do something like this:
//Depending on what you're dealing with: Dictionary<string, List<string>>
Dictionary<string, List<object>> dict = new Dictionary<string, List<object>>{
{"ID", new List<object>{"Id1", "Id2"}},
{"NAME", new List<object>{"True", "False"}}
};
foreach(var v in dict.Keys){
Console.WriteLine($"{v} = {string.Join(",", dict[v])}");
}
//Output:
//ID = Id1,Id2
//NAME = True,False
Even if you have the just the mapping of ID and Name you can have very simple variable
Dictionary<string,string> lookup = new Dictionary<string,string>();
lookup.Add("ID1","True")
and if Name is Boolean type then replace string to bool in the
Dictionary<string,bool> lookup = new Dictionary<string,bool>();

How can I extend a field of a class?

Suppose I have a List of Person (which is a class). It contains about 20 field (Name, Surname, Age, DateOfBirthdate, and so on). So I got this list:
var listOfPersons= MyContext.Persons.Cast<Person>();
Now, I need to iterate through this List, and for each Person adding a new field (which it is not present in the class), called, let's say, CurrentDateTime.
I could create a new object, with the new field, and "copy & paste" values from Person to the new Class. Somethings like:
PersonNew newPerson = new PersonNew("Name", "Surname", "Age", "DateOfBirthdate", ... "CurrentDateTime");
But this is very bad if in the future I change the Person class. So, is there a strategy to "extending Person" with a new field? That takes the Person instance (whatever it is) and adds the new field?
You can create some static method that create PersonNew from Person using Automapper.
public class PersonNew : Person
{
public static PersonNew CreateFromPerson(Person person, DateTime currentDateTime)
{
var newPerson = Mapper.Map<PersonNew>(person);
newPerson.CurrentDateTime = currentDateTime;
}
}
I think that the solution you described works fine. If you want to keep track of each person's birthday without extending the Person class, you might use a Dictionary object
var listOfPersons = MyContext.Perons.Cast<Person>();
Dictionary<Person, DateTime> birthdays = new Dictionary<Person, DateTime>
foreach(Person person in listOfPersons)
{
birthdays.Add(person, getBirthday(person);
}
One solution is to make your class partial, and add your field in another partial definition of your class:
public partial class Person
{
public string Name { get; set; }
public string FirstName { get; set; }
...
}
...
public partial class Person
{
public DateTime CurrentDateTime { get; set; }
}
...
var listOfPersons = MyContext.Persons.Cast<Person>();
foreach (var person in listOfPersons)
{
person.CurrentDateTime = ....
}
Do note that you will use the same instance of your class.
First I would suggest using extension methods for projecting collections instead of iterating. Like that:
var newCollection = oldCollection.Select(entity => MakeNewType(entity))
Second, it's not completely clear what you mean by "extending Person" with a new field. Here are the couple of ways you can accomplish that.
1) Make another class with the new field and map it to the old one. This is a common scenario for asp.net mvc application where you map models to the appropriate viewmodels. Automapper is useful for these types of scenario (see Sławomir Rosiek anwser)
2) Take advantage of dlr in c# 4+. Yuo will lose the intellisense for dynamic objects, but they canned be passed around functions
var newPeople = people.Select(p =>
{
dynamic expando = new ExpandoObject();
expando.Id = p.Id;
expando.FirtName = p.FirtName;
/* ... */
expando.CurrentDateTime = DateTime.Now;
return expando;
});
3) Use Anonymous types. Anonymous types cannot be passed to another functions, so this approach is useful when you need to quickly project data inside a single method and calculate some result
var newPeople = people.Select(p => new
{
Id = p.Id,
FirtName = p.FirtName,
/* ... */
CurrentDateTime = DateTime.Now
});
in both cases you can now access newly "created" property:
foreach(var p in newPeople)
{
Console.WriteLine("CurrentDateTime: {0}", p.CurrentDateTime);
}
4) If you really need to create a fully featured .net class at runtime you can use Reflection.Emit. This scenario is typically used to create dynamic proxies - subclasses which implement some functionality only known at runtime. Entity framework does this.

Is there a way I can generate a class with all the properties obtained from a List<string>?

I have a List object. In the code, I want to declare a new class , and its attributes, which are dynamically generated, are all obtained from the List object. Is there a way I can do this?
For example: I have a List<string> lstStr= "apple, pear, banana"; and I want to generate a new class in the code:
Class Foo{
string apple;
string pear;
string banana;
}
I would use another collection like Dictionary instead:
Dictionary<String, String> Foo = lstStr
.Distinct()
.ToDictionary(f => f, f => default(String));
// now you can get/set the elements in the following way:
String banana = Foo["banana"];
Foo["apple"] = "some apple";
If you really need a class then this might be something you can do using codedom - either parse the string into a class definition string and generate from there, or use the list of strings to create the properties as you loop through them.
Theres a guide to the basics here.
If you are using c# 4 or higher you can do this with dynamics and specifically the ExpandoObject.
var x = new ExpandoObject() as IDictionary<string, Object>;
foreach (var prop in lstStr)
{
x.Add(prop, "");
}
Since you have said in comments that your end goal here is to bind the data to a DataGrid I'd suggest that you use a DataTable. You can create a blank slate, add columns (of any primitive type) and then populate any number of rows all with that same column schema. Additionally, you can trivially bind the DataTable to a DataGrid; they were designed to work together.
You could use
public static List<Foo> GetFooList() { .... }
or extend from the List Object but many methods are virtual so you may not end up with all the methods.
public class FooList:List<Foo> { .... //add more }
and using it
public static FooList GetFooList() { .... }
link:
C# Generics : List<Object> or new class extends List <Object>

Separating columnName and Value in C#

I have a employee object as shown below
class emp
{
public int EmpID { get; set; }
public string EmpName { get; set; }
public int deptID { get; set; }
}
I need to create a mapping either in this class or a different class to map the properties with column name of my SQL
for eg. EmpdID="employeeID"
EmpName="EmployeeName"
deptID="DepartmentID"
When from my asp.net page when I create the employee class and pass it to a function:
for eg: emp e=new emp();
e.EmpID=1;
e.EmpName="tommy";
e.deptID=10;
When the emp object is populated and passed to the buildValues function it should return array of ComumnName(e.g.employeeID):Value(e.g.1),EmployeeName:tommy,DepartmentID:10)
string[] values=buildValues(emp);
public string[] buildValues(emp e)
{
string[] values=null;
return values;
}
I have 2 questions:
1. Where do I specify the mappings
2. How do I use the mappings in my buildValues function shown above and build the values string array.
I would really appreciate if you can help me with this
You need to use Reflection.
Specifically, you need to loop over typeof(Employee).GetProperties().
This is a solved problem. Do some research on ORM's and have a look at this SO question: .Net ORM that works well with MySQL
First of all, (as it was already said) it's best to leave this kind of mappings to an ORM tool, and just forget about them. However, ORM tools tend to be too "maternal" in protecting you from the gory details of data access and such, so it can be complicated to extend them, or change their behaviour.
That said, you could create a special class (Mappings) that would hold all mapping code. The mappings themselves are best kept in a dictionary, something like this:
static class Mappings
{
private static Dictionary<Type, Dictionary<string, string>> TypeMappings;
private static Dictionary<string, string> EmployeeMapping;
//... other mapped classes
static Mappings()
{
TypeMappings = new Dictionary<Type, Dictionary<string, string>>();
EmployeeMapping = new Dictionary<string, string>();
EmployeeMapping.Add("EmpID", "EmployeeID");
EmployeeMapping.Add("EmpName", "EmployeeName");
EmployeeMapping.Add("DeptID", "DepartmentID");
TypeMappings.Add(typeof(Employee),EmployeeMapping);
//... other mapped classes
}
public static string[] BuildValues<T>(T item)
{
if (!TypeMappings.ContainsKey(typeof(T)))
throw new Exception("wrong call");
Dictionary<string, string> mapping = TypeMappings[typeof(T)];
List<string> results = new List<string>();
foreach (var keyValuePair in mapping)
{
string propName = keyValuePair.Key;
string dbName = keyValuePair.Value;
PropertyInfo pi = typeof(T).GetProperty(propName);
object propValue = pi.GetValue(item, null);
results.Add(string.Format("{0}:{1}", dbName, propValue));
}
return results.ToArray();
}
}
Here, the TypeMappings is a dictionary of all mapped classes, whose mappings in turn are in propertyName - databaseName dictionaries.
The BuildValues method, takes those names, reflects the values, and build a results string.

Categories