Way to set property depending on other property - c#

So, I have this code
Process[] processesManager = Process.GetProcesses();
List<ProcessInfo> temporaryProcessesList = new List<ProcessInfo>();
for (int i = 0; i < processesManager.Length; ++i)
{
temporaryProcessesList.Add(new ProcessInfo(processesManager[i].ProcessName, processesManager[i].Id, TimeSpan.Zero, "Group"));
}
processesList = temporaryProcessesList.GroupBy(d => new {d.Name}).Select(d => d.First()).ToList();
This code is used for getting current processes. Then I'm adding those procesess to temporaryProcessesList. And instead of simple string "Group" I want to set the property depending of the name of process. For example if process has name leagueoflegends.exe then I would like to set the group to "Games", if its devenv.exe I would like to set the group to "Software development".
And my question is, how to do it the simplest/best way? I was thinking about using Dictionary with string and enum. And comparing the ProcessName with string. But maybe there is better way to do it.
ProcessInfo is simple class with 4 properties and constructor.
public class ProcessInfo
{
private string Name { get; set; }
private int Id { get; set; }
private TimeSpan Time { get; set; }
private string Group { get; set; }
public ProcessInfo(string name, int id, TimeSpan time, string group)
{
Name = name;
Id = id;
Time = time;
Group = group;
}
}

Using dictionary is the best way to accomplish this:
var dictionary = new Dictionary<string, string>();
dictionary.Add("a.exe", "aGroup");
dictionary.Add("b.exe", "bGroup");
string val;
if (dictionary.TryGetValue(processName, out val))
processInfo.Group = val;
else
processInfo.Group = "Undefined Group";

Maybe this is what you are looking for:
public class ProcessInfo
{
private string _name;
private string Name
{
get { return _name; }
set
{
_name = value;
UpdateGroupName();
}
}
private int Id { get; set; }
private TimeSpan Time { get; set; }
private string Group { get; set; }
private void UpdateGroupName()
{
Group = ProcessNames::GetGroupFromProcessName(Name);
}
public ProcessInfo(string name, int id, TimeSpan time)
{
Name = name;
Id = id;
Time = time;
}
}
internal static class ProcessNames
{
private static Dictionary<string, string> _names;
public static string GetGroupNameFromProcessName(string name)
{
// Make sure to add locking if there is a possibility of using
// this from multiple threads.
if(_names == null)
{
// Load dictionary from JSON file
}
// Return the value from the Dictionary here, if it exists.
}
}
This design isn't perfect, but hopefully you see the idea. You could also move the update of the Group name to the constructor, but then it would not change the Group name if you set the property after construction.
Also, you could clean the interface up by using INotifyPropertyChanged and/or dependency injection. https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx

Related

How to serialize a Dictionary<object, guid>?

I'm currently building an application where I have some tasks that are sharing a Dictionary<Object, Guid>.
To avoid conflicts, the actual Dictionaries are contained within a DictionaryHandler-class with three methods:
class DictionaryHandler
{
private object lockProcesses = new object();
private Dictionary<Process, Guid> processes = new Dictionary<Process, Guid>();
public Dictionary<Process, Guid> GetProcesses()
{
lock (lockProcesses)
{
// TODO
}
}
public void AddToProcesses(Process process, Guid guid)
{
lock (lockProcesses)
{
processes.Add(process, guid);
}
}
public void RemoveFromProcesses(Process process)
{
lock (lockProcesses)
{
processes.Remove(process);
}
}
}
For context, this is the Process-class:
public class Process
{
public string Name { get; }
public bool Enabled { get; }
public TimeSpan RuntimeWindowStart { get; }
public TimeSpan RuntimeWindowEnd { get; }
public TimeSpan Cooldown { get; }
public int Priority { get; }
public string Username { get; }
public string ExceptionDate { get; }
public string ExceptionDay { get; }
public string AllowedWorkdays { get; }
public string SpecificResource { get; }
public string Assigned { get; set; }
public DateTime? Timestamp { get; set; }
public Process(string name, bool enabled, TimeSpan runtimeWindowStart, TimeSpan runtimeWindowEnd, TimeSpan cooldown, int priority, string username, string exceptionDate, string exceptionDay, string allowedWorkdays, string specificResource, string assigned, DateTime? timestamp)
{
Name = name;
Enabled = enabled;
RuntimeWindowStart = runtimeWindowStart;
RuntimeWindowEnd = runtimeWindowEnd;
Cooldown = cooldown;
Priority = priority;
Username = username;
ExceptionDate = exceptionDate;
ExceptionDay = exceptionDay;
AllowedWorkdays = allowedWorkdays;
SpecificResource = specificResource;
Assigned = assigned;
Timestamp = timestamp;
}
}
My main issue is that I want to find a way to return a copy of the Dictionary through the GetProcesses()-method, without returning a reference to the "actual" dictionary.
As far as I can see, the optimal way to do this is to Serialize and Deserialize the Dictionary and return that. But I'm having a hard time doing this, as I'm unable to find an example that matches my case.
I've read this and this and tried to combine the two - unfortunately without luck.
My main issue is that I want to find a way to return a copy of the Dictionary through the GetProcesses()-method, without returning a reference to the "actual" dictionary.
If you don't need to clone the values, you can use the constructor overload to Dictionary which takes an existing IDictionary:
new Dictionary<Process, Guid>(processes);
If you do need to clone the values, you can use something like:
public static Dictionary<Process, Guid> DeepClone<TKey, TValue>(Dictionary<Process, Guid> source)
{
var ret = new Dictionary<Process, Guid>(source.Count, source.Comparer);
foreach (var entry in source)
{
ret.Add(entry.Key, entry.Value);
}
return ret;
}
If you do need create a copy of Process class instance while copying dictionary, you may use:
public class Process
{
// some properties here
public Process ShallowCopy()
{
return (Process) this.MemberwiseClone();
}
}
public static Dictionary<Process, Guid> DeepClone<TKey, TValue>(Dictionary<Process, Guid> source)
{
var ret = new Dictionary<Process, Guid>(source.Count, source.Comparer);
foreach (var entry in source)
{
ret.Add(entry.Key.ShallowCopy(), entry.Value);
}
return ret;
}
Use String instead of Guid.
Guid is structure type. Therefore serialize and deserialize methods might not work correctly.

CsvHelper Looking for Non-existent Columns

The propblem: There is no "Name" field in the object or csv file, yet CsVHelper keeps looking for "Name" in the header. So why is it tripping there and what are some fixes?
When trying to build objects from a csv file, the following error comes up:
CsvHelper.HeaderValidationException: Header with name 'Name' was not found. If you are expecting some headers to be missing and want to ignore this validation, set the configuration HeaderValidated to null. You can also change the functionality to do something else, like logging the issue.
at CsvHelper.Configuration.ConfigurationFunctions.HeaderValidated(Boolean isValid, String[] headerNames, Int32 headerNameIndex, ReadingContext context)
I have tried setting HeaderValidated to null, but got the same results.
The header of the csv:
Id|Title|Description|AssignedToUserId|SourceUserId|DateCreated|DateAssigned|DateCompleted|Notes
The parsing code:
private static IEnumerable<T> GetCSVData<T>(string fullFileName)
{
PrintMembers<T>();
using (var reader = new StreamReader(fullFileName))
{
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.IncludePrivateMembers = false;
csv.Parser.Configuration.Delimiter = "|";
var records = csv.GetRecords<T>().ToList();
return records;
}
}
}
A quick function for listing the public properties and fields of the class (T) being passed in outputs the following:
Properties...
Id
AssignedToUserId
SourceUserId
Title
Description
AssignedTo
Source
DateCreated
DateAssigned
DateCompleted
RelatedTasks
Notes
Fields...
[None]
They all have getters and setters.
EDIT
The IntermediateTask is the generic being fed into GetCSVData(). It has a default constructor. IntermediateTask is internal, but is in the same assembly as GetCSVData().
Code for the class(es) in question:
internal class IntermediateTask : Task
{
private int _Id;
new public int Id
{
get { return _Id; }
set { _Id = value; }
}
private int _AssignedToUserId;
public int AssignedToUserId
{
get { return _AssignedToUserId; }
set
{
_AssignedToUserId = value;
base.AssignedTo = userManager.Get(_AssignedToUserId);
}
}
private int _SourceUserId;
public int SourceUserId
{
get { return _SourceUserId; }
set
{
this._SourceUserId = value;
base.Source = userManager.Get(_SourceUserId);
}
}
public IntermediateTask() : base("", "", new IntermediateUser(), new IntermediateUser())
{
}
}
public class Task
{
public Task(string title, string description, User assignedTo, User source, DateTime? dateCreated = null, int id = 0)
{
this.RelatedTasks = new List<Task>();
this.Title = title;
this.Description = description;
this.AssignedTo = assignedTo;
this.Source = source;
this.DateCreated = dateCreated ?? DateTime.Now;
this.Id = id;
}
private int _Id;
public int Id
{
get { return _Id; }
protected set { _Id = value; }
}
public string Title { get; set; }
public string Description { get; set; }
public User AssignedTo { get; set; }
public User Source { get; set; }
public DateTime DateCreated { get; set; }
public DateTime? DateAssigned { get; set; }
public DateTime? DateCompleted { get; set; }
public IList<Task> RelatedTasks { get; set; }
public string Notes { get; set; }
override public string ToString()
{
return $"Id: {Id}; Title: {Title}";
}
}
In my case it complained about AssignedTo missing, but that is actually a property in the class that is not in the csv, so I had to add these two lines to make it work:
csv.Configuration.HeaderValidated = null;
csv.Configuration.MissingFieldFound = null;
I don't know why it would come up with 'Name' unless you have something different.

How to access a class member through a variable

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
}
}

Pointing to a property without reflection or databinding

I want to create a class with a property that "variably" points to some other property in another class.
Imagine a class (called "Limiter") with several integer properties (Limit1, Limit2, etc).
I now want a second class ("LimitWatcher") which can "watch" one of those limits. But I want to be able to set which particular limit it is watching in the constructor. I eventually want several instances of LimitWatcher, each one pointing to a separate Limit. The Limit values themselves may change after the Watchers have been instantiated, but the watcher must always see the current value of the Limit that it is watching. So basically, I want to store a reference to an integer.
I know I can accomplish this using reflection (see example below), but I feel as though there might be a simpler way.
using System;
namespace ConsoleApplication4
{
public class Limiter
{
public int limit1 { get; set; } = 10;
public int limit2 { get; set; } = 20;
public void Update()
{
limit1++;
limit2++;
}
}
public class LimitWatcher
{
public LimitWatcher(Limiter lim, string propName)
{
myLimiter = lim;
limitName = propName;
}
private Limiter myLimiter { get; }
public string limitName { get; set; }
//can I do this without reflection:
public int FooLimit { get { return (int)typeof(Limiter).GetProperty(limitName).GetValue(myLimiter); } }
}
class Program
{
static void Main(string[] args)
{
Limiter lim = new ConsoleApplication4.Limiter();
LimitWatcher w1 = new LimitWatcher(lim, nameof(lim.limit1));
LimitWatcher w2 = new LimitWatcher(lim, nameof(lim.limit2));
lim.Update();
Console.WriteLine($"1st watcher sees {w1.FooLimit}"); //11
Console.WriteLine($"2nd watcher sees {w2.FooLimit}"); //21
Console.ReadKey();
}
}
}
You could use a Func int the constructor, something like:
private Limiter limiter;
private Func<Limiter, int> propertyAccesor;
public LimitWatcher(Limiter lim, string propName, Func<Limiter, int> propertyAccesor)
{
this.propertyAccesor = propertyAccesor;
}
public bool LimitExceeded()
{
int propertyValue = propertyAccesor(limiter);
return propertyValue > 20;
}
You could use dynamic expressions:
using System.Linq.Expressions;
public class LimitWatcher
{
public LimitWatcher(Limiter lim, string propName)
{
myLimiter = lim;
limitName = propName;
var parameter = Expression.Parameter(typeof(Limiter), "x");
var member = Expression.Property(parameter, propName);
var finalExpression = Expression.Lambda<Func<Limiter, int>>(member, parameter);
getter = finalExpression.Compile();
}
private Func<Limiter, int> getter;
private Limiter myLimiter { get; }
public string limitName { get; set; }
public int FooLimit { get { return getter(myLimiter); } }
}
Inspired by this article

Fetch value from string expression like "Add Watch" feature

public class UFMLine
{
public UFMTemplate elementTag;
public int posX1;
public int posY1;
public int posX2;
public int posY2;
public string property;
public string hierStr = string.Empty;
public List<UFMLine> ufmLines = new List<UFMLine>(); // the tricky nested class field
}
UFMLine ufmobj = new UFMLine();
This ufmobj is populated perfectly at the window load.
In my button click of wpf window xaml code behind...
string nthItem = "ufmobj.ufmLines[0].ufmLines[1].ufmLines[1].ufmLines[1].ufmLines[2].ufmLines[2].elementTag";
// Tried reflection method, but giving null exception
var result = typeof(UFMLine).GetField(nthItem).GetValue(ufmobj);
So when opening watch window and fetch the value for nthItem name it gives appropriate value.
How to get it in the code behind or am i not properly using the reflection?
Thanks.
The "name" of the variable (ufmobj) could be a problem since it could be lost in a release build, but the rest can be achieved with reflection if you don't mind implementing your own parser.
Split path (Syntax of your choice)
Resolve one path segment after another
Determine if path is Field/Property/Indexer/Method/etc.
Repeat until done
Here is a small snippet to get you going (far from complete, but works with your example):
Resolve a single Field or Property
private object GetFieldOrProperty(object obj, string name)
{
Type objType = obj.GetType();
if (objType.GetField(name) != null)
return objType.GetField(name).GetValue(obj);
if (objType.GetProperty(name) != null)
return objType.GetProperty(name).GetValue(obj, null);
return null;
}
Resolve the entire path:
private object Resolve(object parent, string path)
{
string[] paths = path.Split('.');
foreach (string p in paths)
{
if (p.EndsWith("]"))
{
int start = p.IndexOf("[");
string property = p.Substring(0, start);
string index = p.Substring(start + 1, p.Length - start - 2);
parent = GetFieldOrProperty(parent, property);
if (parent == null)
return null;
foreach (PropertyInfo info in parent.GetType().GetProperties())
{
if (info.GetIndexParameters().Length < 1) continue;
parent = info.GetValue(parent, new object[] {int.Parse(index)});
break;
}
}
else
{
parent = GetFieldOrProperty(parent, p);
if (parent == null)
return null;
}
}
return parent;
}
Test case:
UFMLine ufmobj = new UFMLine();
ufmobj.ufmLines.Add(new UFMLine());
ufmobj.ufmLines[0].ufmLines.Add(new UFMLine());
ufmobj.ufmLines[0].ufmLines[0].ufmLines.Add(new UFMLine{property = "Success"});
Debug.WriteLine(Resolve("ufmLines[0].ufmLines[0].ufmLines[0].property.Length", ufmobj));
7 (length of "Success")
You may change the fields of your UFMLine class to public properties:
public class UFMLine
{
public UFMTemplate elementTag { get; set; }
public int posX1 { get; set; }
public int posY1 { get; set; }
public int posX2 { get; set; }
public int posY2 { get; set; }
public string property { get; set; }
public string hierStr { get; set; } = string.Empty;
public List<UFMLine> ufmLines { get; set; } = new List<UFMLine>();
}
Now you could use the part of your nthItem string after "ufmobj." as the Path of a Binding, for which you would use ufmobj as Source:
var binding = new Binding
{
Source = ufmobj,
Path = new PropertyPath("ufmLines[0].ufmLines[1].ufmLines[1].ufmLines[1].ufmLines[2].ufmLines[2].elementTag")
};
To make use of this Binding, you would also need a target dependency property, which you might declare in a helper class like this:
public class BindingHelper : DependencyObject
{
public static readonly DependencyProperty ResultProperty =
DependencyProperty.Register("Result", typeof(object), typeof(MainWindow));
public object Result
{
get { return GetValue(ResultProperty); }
set { SetValue(ResultProperty, value); }
}
}
Finally you would assign the Binding to the Target property and then retrieve the resulting value like this:
var bindingHelper = new BindingHelper();
BindingOperations.SetBinding(bindingHelper, BindingHelper.ResultProperty, binding);
var result = bindingHelper.Result;

Categories

Resources