How to access a class member through a variable - c#

I have a class that I've created
public class DataRecord
{
public string PayerAccount { get; set; }
public string GlobalEntityType { get; set; }
public string GlobalEntityNumber { get; set; }
}
I am now trying to access this DataRecord in a different method through the use of a variable
public List<DataTest> CountAndFrequency(IEnumerable records, string ColumnName, int numResults)
{
foreach (DataRecord record in records)
{
record.ColumnName = record.ColumnName.ToUpper();
}
}
I am getting the error that DataRecord does not contain a definition for ColumnName, which of course makes sense. The question is, how do I combat this issue? I've been scouring the internet to no avail and would appreciate any help.
Thanks in advance!

You can use reflection for this. Try this
public static List<DataTest> CountAndFrequency(IEnumerable<DataRecord> records, string ColumnName, int numResults)
{
foreach (DataRecord record in records)
{
var prop = typeof(DataRecord).GetProperty(ColumnName);
var value = prop.GetValue(record).ToString().ToUpper();
prop.SetValue(record, value);
}
}

If you want to access data via a string name, you store the data in a Dictionary<string,string>.
public class DataRecord
{
private readonly Dictionary<string, string> data;
public DataRecord()
{
this.data = new Dictionary<string, string>();
}
// Access data with names
public string this[string columnName]
{
get{ return data[columnName]; }
set{ data[columnName] = value;}
}
// Fake properties
public string PayerAccount
{
get => data[nameof(PayerAccount)];
set => data[nameof(PayerAccount)] = value;
}
public string GlobalEntityType
{
get => data[nameof(GlobalEntityType)];
set => data[nameof(GlobalEntityType)] = value;
}
public string GlobalEntityNumber
{
get => data[nameof(GlobalEntityNumber)];
set => data[nameof(GlobalEntityNumber)] = value;
}
}
class Program
{
static void Main(string[] args)
{
var record = new DataRecord
{
PayerAccount = "10024",
GlobalEntityType = "QXT",
GlobalEntityNumber = "509382"
};
var number = record["GlobalEntityNumber"];
// 509382
}
}

Related

Initializing fields through attribute?

I want the value to be initialized according to the attribute When Instance is created.
class FieldAttr : Attribute
{
public readonly string key;
public Key_FieldAttr(string key)
{
this.key = key;
}
}
class CtorAttr : Attribute
{
}
static string FromTable(string key)
{
// will return localized string of key from table.
}
...
class LocalizedMetadata
{
[FieldAttr("NAME")]
public string Name { get; }
[FieldAttr("DESC")]
public readonly string Description;
[CtorAttr]
public LocalizedMetadata(string header)
{
// I want "Key" attribute to do as below...
// this.Name = FromTable(header + "NAME");
// this.Description = FromTable(header + "DESC");
}
}
public static void Main(string[] args)
{
var foo = new LocalizedMetadata("UNITY_");
Console.WriteLine(foo.Name); // = FromTable("UNITY_NAME");
Console.WriteLine(foo.Description); // = FromTable("UNITY_DESC");
}
I don't know where to attach what kind of Attribute (Field, Construction, Class) will be possible.
I looked up documents about Attribute and Reflection, but I don't know if this is possible.
I think below solution will solve your problem.
class LocalizedMetadata
{
[FieldAttr("NAME")]
public string Name { get; set; }
[FieldAttr("DESC")]
public string Description { get; set; }
public LocalizedMetadata(string header)
{
foreach (var property in typeof(LocalizedMetadata).GetProperties())
{
foreach (var attr in property.GetCustomAttributes(false))
{
FieldAttr fieldAttr = (FieldAttr)attr;
property.SetValue(this, header + fieldAttr.Key);
}
}
}
}
public static void Main(string[] args)
{
var foo = new LocalizedMetadata("UNITY_");
Console.WriteLine(foo.Name);
Console.WriteLine(foo.Description);
}
Output:
UNITY_NAME
UNITY_DESC

c# Loop List<T> row and trim column values

public class Person {
string Name
string Address
int Age
.. 100+ more columns
}
var result = new List<Person>();
foreach (var item in result )
{
//loop column and trim the values.
}
I want the simplest way to loop the columns (assuming 100+ columns) where datatype is string then trim the value.
To rephrase in more C# terms: I want to update all properties and fields of an object that are of type string with trimmed value as item.StringProp = item.StringProp.Trim(). I don't want to manually write update for each property.
You could use reflection and Linq for filtering the properties of type string. From the OP, it looks like you are using Fields instead of properties. Please note it is unclear whether the Properties/Fields are public from OP, if you need to use public fields/properties, please use BindingFlags.Public
public List<T> TrimList<T>(List<T> source)
{
foreach(var property in typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(x=>x.FieldType== typeof(string)))
{
foreach(var personItem in source)
property.SetValue(personItem,Convert.ToString(property.GetValue(personItem)).Trim());
}
return source;
}
If properties, you could use
public List<T> TrimList<T>(List<T> source)
{
foreach(var property in typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance).Where(x=>x.PropertyType== typeof(string)))
{
foreach(var personItem in source)
property.SetValue(personItem,Convert.ToString(property.GetValue(personItem)).Trim());
}
return source;
}
Demo Code
Note: prior to .NET 4.5 you need to pass null as a second argument:
public List<T> TrimList<T>(List<T> source)
{
foreach(var property in typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance).Where(x=>x.PropertyType== typeof(string)))
{
foreach(var personItem in source)
property.SetValue(personItem,Convert.ToString(property.GetValue(personItem,null)).Trim());
}
return source;
}
As well as reflection, another way is to make it the responsibility of the Person class.
public class Person {
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
.. 100+ more columns
public void DoTrim()
{
this.Name = this.Name.Trim();
this.Address = this.Address.Trim();
... still need to code 100+ properties
}
}
The advantage is that you can call it like this
var result = new List<Person>();
...
for(int i=0; i < result.Count(); i++)
{
result[i].DoTrim();
}
Or you can control your data in the Person class when you set it and use local private variables.
public class Person {
private string name;
public string Name
{
get { return name; }
set { name = value.Trim(); }
}
private string address;
public string Address
{
get { return address; }
set { address= value.Trim(); }
}
....
This is how I would implement it:
class Program
{
static void Main(string[] args)
{
var obj = new Person
{
MyProperty = " A",
MyProperty1 = " A ",
MyProperty2 = "A ",
MyProperty3 = "A A A",
};
TrimStrings(obj);
}
public static void TrimStrings(object obj)
{
Type stringType = typeof(string);
var properties = obj.GetType().GetProperties().Where(x => x.PropertyType == stringType);
foreach(var property in properties)
{
string value = (string)property.GetValue(obj);
property.SetValue(obj, value?.Trim());
}
}
}
public class Person
{
public string MyProperty { get; set; }
public string MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
public string MyProperty3 { get; set; }
}
Output:
{"MyProperty":"A","MyProperty1":"A","MyProperty2":"A","MyProperty3":"A
A A"}
You can use This Nuget Package
.After Install use it as bellow:
result.ForEach(x => x.AdjustString());

c# how to add a reference for a struct to an array or list?

New to c# and object oriented
I am creating predefined data held in structs (that will not change).
and I want to add a reference to those structs in either an array or a list
The thought process is to then loop through an array of these structs and if the filter criteria is met, to then add an element of the struct "Displayname" to a listbox
Example I am using here is for an array
public struct ListboxData
{
public string Category;
public string Displayname;
public string ID;
public List<string> IDTags;
public string FaultTxt;
public string Suggestion;
public string Comments;
public List<string> MoreQuestions;
}
public ListboxData COOLING = new ListboxData
{
Category = "CATEGORY",
Displayname = "COOLING",
ID = "CAT_COOL",
IDTags = { ""},
FaultTxt = "",
Suggestion = "",
Comments = "",
MoreQuestions = { ""}
};
Having now created multiple instances of the ListboxData structs I then want to add a selection of some of these structs to an array. In this case the Struct reference COOLING
Example of what I am trying to achieve
public ListboxData[] ARRAYofCATEGORIES =
{
COOLING
};
How can I achieve this ?
I re-wrote your example, working. You can find a live test here
Below is the code. I used a List() since you mentioned in your question that it was acceptable, and I am more used to it.
I don't know what you were doing wrong since you didn't show us a full example. You can read the code bellow, tell me if there is something you don't understand/want and i'll update my answer to fit your needs :-)
Here is the code
public struct ListboxData
{
private readonly string _category;
private readonly string _displayname;
private readonly string _id;
private readonly List<string> _idTags;
private readonly string _faultTxt;
private readonly string _suggestion;
private readonly string _comments;
private readonly List<string> _moreQuestions;
public ListboxData(string category, string displayname, string id, List<string> idTags, string faultTxt, string suggestion, string comments, List<string> moreQuestions)
{
_category = category;
_displayname = displayname;
_id = id;
_idTags = idTags;
_faultTxt = faultTxt;
_suggestion = suggestion;
_comments = comments;
_moreQuestions = moreQuestions;
}
public string Category
{
get { return _category; }
}
public string Displayname
{
get { return _displayname; }
}
public string Id
{
get { return _id; }
}
public List<string> IdTags
{
get { return _idTags; }
}
public string FaultTxt
{
get { return _faultTxt; }
}
public string Suggestion
{
get { return _suggestion; }
}
public string Comments
{
get { return _comments; }
}
public List<string> MoreQuestions
{
get { return _moreQuestions; }
}
}
public class Test
{
public List<ListboxData> Categories = new List<ListboxData>();
public CoolingListTest()
{
Categories.Add(new ListboxData(
category: "CATEGORY",
displayname: "COOLING",
id: "CAT_COOL",
idTags: new List<String> { "" },
faultTxt: "",
suggestion: "",
comments: "",
moreQuestions: new List<String> { "" }
));
}
}
public class Program
{
public static void Main(string[] args)
{
var test = new Test();
Console.WriteLine(test.Categories.First().Displayname);
}
}
1st rule to go by is that you have to design immutability into struct. Especially if the fields are reference types (string, array, user classes).
I will demonstrate below a simplified example of populating a struct with various values, then creating a collection (array or list) of the struct and then finally slicing one of the values as an array.
See live code here this code:
public struct ListboxData
{
public ListboxData(
int id,
string category,
string name,
params string[] tags) : this()
{
this.ID = id;
this.Category = category;
this.Name = name;
this.Tags = new List<string>();
this.Tags.AddRange(tags);
}
public int ID { get; private set; }
public string Category { get; private set;}
public string Name { get; private set;}
public List<string> Tags { get; private set;}
public string[] ToStringArray()
{
return new[] {
ID.ToString(),
Category,
Name,
string.Join(";", Tags)
};
}
}
public class Program
{
public static void Main(string[] args)
{
//Your code goes here
Console.WriteLine("Hello, world!");
var list = new List<ListboxData>();
list.Add(new ListboxData(1001, "COOLING", "Cooling Intro"));
list.Add(new ListboxData(1002, "COOLING", "Cooling Adanced"));
list.Add(new ListboxData(1003, "COOLING", "Cooling Tests"));
list.Add(new ListboxData(1004, "HEATING", "Heating Intro", "tag1", "tag2"));
// Get all cooling ID's
int[] ids = list.Where((item)=> item.Category=="COOLING").Select( (item)=> item.ID).ToArray();
// { 1001, 1002, 1003 }
foreach(var item in ids)
{
Console.WriteLine(item);
}
// Get all items that contain "Intro" in the name
ListboxData[] intro = list.Where((item)=>item.Name.Contains("Intro")).ToArray();
// { list[0], list[3] }
foreach(var item in intro)
{
Console.WriteLine(item.Name);
}
}
}
Newer versions of c# have simplifications for the above code. You can omit the private set; statements in the properties.

linq update not working

I have tried a lot but all in vain.
I have written a LINQ code but not able to save changes in database.
It is giving no error neither it is updating record.
class Program
{
[Table(Name = "mainframe_replication")]
public class mainframe_replication
{
private string _REPL_GUID;
[Column(IsPrimaryKey = true, Storage = "_REPL_GUID")]
public string REPL_GUID
{
get { return this._REPL_GUID; }
set { this._REPL_GUID = value; }
}
private string _REPL_TYPE;
[Column(Storage = "_REPL_TYPE")]
public string REPL_TYPE
{
get { return this._REPL_TYPE; }
set { this._REPL_TYPE = value; }
}
private string _RPT_ID;
[Column(Storage = "_RPT_ID")]
public string RPT_ID
{
get { return this._RPT_ID; }
set { this._RPT_ID = value; }
}
private string _RPT_VERS;
[Column(Storage = "_RPT_VERS")]
public string RPT_VERS
{
get { return this._RPT_VERS; }
set { this._RPT_VERS = value; }
}
private string _RPT_BYTES;
[Column(Storage = "_RPT_BYTES")]
public string RPT_BYTES
{
get { return this._RPT_BYTES; }
set { this._RPT_BYTES = value; }
}
private string _REPL_DTM;
[Column(Storage = "_REPL_DTM")]
public string REPL_DTM
{
get { return this._REPL_DTM; }
set { this._REPL_DTM = value; }
}
private string _NOTIF_ID;
[Column(Storage = "_NOTIF_ID")]
public string NOTIF_ID
{
get { return this._NOTIF_ID; }
set { this._NOTIF_ID = value; }
}
}
public class MyPoco
{
public string ReportId { get; set; }
public string Reportversion { get; set; }
public string ReportBytes { get; set; }
public string ReportDate { get; set; }
public string NotifId { get; set; }
public string RecipAdd { get; set; }
}
public static string loglocation;
static void Main(string[] args)
{
try
{
using (DataClasses1DataContext db = new DataClasses1DataContext())
{
Table<NOTIF_RECIP> NOTIF_RECIP_alias = db.GetTable<NOTIF_RECIP>();
Table<NOTIF_SCHED> NOTIF_SCHED_alias = db.GetTable<NOTIF_SCHED>();
Table<mainframe_replication> mainframe_replication_alias = db.GetTable<mainframe_replication>();
var ids = NOTIF_SCHED_alias.Select(x => x.NOTIF_RPT_ID).ToArray();
foreach (string notif_sched_data in ids)
{
var repljoinmf = mainframe_replication_alias
.Join(NOTIF_RECIP_alias, mfr => mfr.RPT_ID, nr => nr.NOTIF_RECIP_ID, (mfr, nr)
=> new MyPoco { ReportId = mfr.RPT_ID, Reportversion = mfr.RPT_VERS, ReportBytes = mfr.RPT_BYTES.ToString(), ReportDate = mfr.REPL_DTM.ToString(), NotifId = mfr.NOTIF_ID, RecipAdd = nr.NOTIF_RECIP_ADDR });
foreach (var repljoinmf_data in repljoinmf)
{
repljoinmf_data.NotifId = "abc";
//DO STUFF
// repljoinmf_data.NotifId = "Changedxyz";
}
db.SubmitChanges();
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
It is not giving any error while submitting changes.
What I need to change?
Any suggestion will be helpful.
If you want to save your changes back to the original data source, you need to be working with the actual entities instead of projections of those entities. Since you are joining two tables, one option is to put those instances into an anonymous type and update them:
foreach (string notif_sched_data in ids)
{
var repljoinmf = mainframe_replication_alias
.Join(NOTIF_RECIP_alias,
mfr => mfr.RPT_ID,
nr => nr.NOTIF_RECIP_ID,
(mfr, nr) => new {mfr, nr});
foreach (var repljoinmf_data in repljoinmf)
{
//DO STUFF
repljoinmf_data.mfr.NotifId = "Changedxyz";
}
db.SubmitChanges();
In your previous question you were told that anonymous types cannot be uptated, but in this case you're modifying instances that are referenced by the anonymous type. So you're not updating the anonymous type itself, just the objects that the anonymous type references.
You are modifying the property of your MyPoco object. This is just a representation of your table. That's why the database is not updated.
You can send your MyPoco to your client. It will perform some changes. Then you can recreate the entity and copy the properties from the Poco object. Then, you need to attach the modified entity to your table and then save the changes.
If you modify directly the entity, there is no need to attach, since it will have kept the links to the database (assuming you do that with the same Databasecontext).

Object serializing/deserializing doesn't work

I have an extension method for System.Object to serialize and deserialize objects using Json.Net. this is my Extension methods:
public static void SaveToFile(this object data, string FileName)
{
using (StreamWriter writer = new StreamWriter(FileName))
{
string encode = WpfApplication.Helper.Encrypt(JsonConvert.SerializeObject(data));
writer.Write(encode);
writer.Close();
}
}
public static void LoadFromFile<t>(this object data, string FileName)
{
using (StreamReader reader = new StreamReader(FileName))
{
data = JsonConvert.DeserializeObject<t>(WpfApplication.Helper.Decrypt(reader.ReadToEnd()));
reader.Close();
}
}
and It's the class that I want to deserialize:
public class CardPack
{
#region Properties
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private List<FlashCard> cards;
public List<FlashCard> Cards
{
get { return cards; }
set { cards = value; }
}
private bool registered;
public bool Registered
{
get { return registered; }
set { registered = value; }
}
private int currentCardIndex;
public int CurrentCardIndex
{
get { return currentCardIndex; }
set { currentCardIndex = value; }
}
public string RegisterKey { get; set; }
public string ViewName { get; set; }
public List<FlashCard> TodayCards { get; set; }
#endregion
~CardPack()
{
foreach (FlashCard card in cards)
{
card.Check();
}
currentCardIndex = 0;
TodayCards = null;
this.SaveToFile(string.Format(System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase).Split(#"file:\\")[1] + #"\Packs\{0}.json", name));
}
but whenever I deserialize the class cards is empty and I don't know how to resolve the problem. Can anybody help me?
Update
I find the error when I had this code:
public CardPack(string Name)
{
this.name = Name;
this.LoadFromFile<CardPack>(string.Format(System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase).Split(#"file:\\")[1] + #"\Packs\{0}.json", name));
foreach (var item in cards)
{
if (item.NextTime == null)
{
int a = 0;
}
}
TodayCards = cards.Where(c => c.NextTime.Date == DateTime.Today).ToList();
}
because the application closed when it tries to run foreach (var item in cards)!
I asked here and found out that cards is empty!
update2 I serialized the CardPack object with a little different structure. in previous structure Cards property was read-only.
I found that data = JsonConvert.DeserializeObject<t>(WpfApplication.Helper.Decrypt(reader.ReadToEnd())); in extension method doesn't change to 'data' class then Cards in CardPack is always null. I'll ask a question to find out why I cant set the class from it's extension method later.

Categories

Resources