I don't even know if I am on the right track but I gave a shot to Dictionary to store big and static data (like multi language data) and use it in the whole application. This example goes for a WinForms app.
In this scenario, I have a table which holds various parameters, divided into groups. The table is made of following fields: GROUP_CODE, PARAMETER_KEY, PARAMETER_VALUE... It simply holds lots of values as in this example:
GROUP_CODE: 'MAILING_INFO'
PARAMETER_KEY: 'SendMailAfterProcessIsDone'
PARAMETER_VALUE: 'a#b.com'
There is no problem or anything to hold and get the data from the database. My only problem is that how exactly I am going to handle this data...
My ParameterValues class holds the following, same fields I select from the table...
public class ParameterValues
{
private string groupCode;
private string parameterKey;
private string parameterValue;
public string GroupCode
{
get { return groupCode; }
set { groupCode = value; }
}
public string ParameterKey
{
get { return parameterKey; }
set { parameterKey = value; }
}
public string ParameterValue
{
get { return parameterValue; }
set { parameterValue = value; }
}
}
In another class, called CacheHelper, I am trying to put this object with the GROUP_CODE value as key into a Dictionary. (Hope that made sense)
public class CacheHelper
{
public Dictionary<string, ParameterValues> LoadParameterCache2()
{
//Dictionary<string, ParameterValues> objList = new Dictionary<string, ParameterValues>();
Dictionary<string, List<ParameterValues>> objList = new Dictionary<string, List<ParameterValues>>();
//call the values from the database blah blah
while (rdr.Read())
{
ParameterValues cacheObj = new ParameterValues();
cacheObj.ParameterKey = rdr.GetString("PARAMETER_KEY");
cacheObj.ParameterValue = rdr.GetString("PARAMETER_VALUE");
objList.Add(rdr.GetString("GROUP_CODE"), /*List ParameterValues goes here blah blah*/);
}
return objList; //and finally return it to the main application thread
}
}
I have to group it by GROUP_CODE values as from the database since Dic's "key" has to be unique... I know....
Since I think it was not clear enough, I tried to demonstrate what I am actually trying to do in the following image...
I simply cannot put it there groupped. Please someone show me a way... And also I'd like to hear your ideas if this is a good way to use this as application caching for really large data (thousands of rows I mean).
Many thanks!
I am not going to talk about caching solutions, as there are some out there and I am not particularly experienced with any.
But If you want to try static Dictionary solution, you could try grouping your data using Linq:
public Dictionary<string, List<ParameterValues>> LoadParameterCache2()
{
List<KeyValuePair<string, ParameterValues>> dataValues = new List<KeyValuePair<string, ParameterValues>>();
// Your code to produce the DataReader
DataTableReader rdr = GetDataReader();
while (rdr.Read())
{
ParameterValues cacheObj = new ParameterValues();
cacheObj.ParameterKey = (string)rdr["PARAMETER_KEY"];
cacheObj.ParameterValue = (string)rdr["PARAMETER_VALUE"];
KeyValuePair<string, ParameterValues> dataValue = new KeyValuePair<string, ParameterValues>((string)rdr["GROUP_CODE"], cacheObj);
dataValues.Add(dataValue);
}
Dictionary<string, List<ParameterValues>> objList = dataValues.GroupBy(d => d.Key).ToDictionary(k => k.Key, v => v.Select(i => i.Value).ToList());
return objList;
}
I think I get where you are having a problem, please note the syntax may be not 100% as I am typing from memory.
public class CacheHelper
{
public Dictionary<string, List<ParameterValues>> LoadParameterCache2()
{
var objList = new Dictionary<string, List<ParameterValues>>();
//call the values from the database blah blah
while (rdr.Read())
{
var cacheObj = new ParameterValues();
cacheObj.ParameterKey = rdr.GetString("PARAMETER_KEY");
cacheObj.ParameterValue = rdr.GetString("PARAMETER_VALUE");
//here is where you need to change things.
var groupCode = rdr.GetString("GROUP_CODE");
if (objList.ContainsKey(groupCode) == false)
{
objList.Add(groupCode, new List<ParameterValues> {cacheObj});
}
else
{
objList[groupCode].Add(cacheObj);
}
}
return objList; //and finally return it to the main application thread
}
}
Related
I am a little bit confused about how to read data from Excel. I am trying to import Excel for updating product list, I create an Excel model; I added all basic properties like name, price, quantity, etc. into this model. I will read all Excel and map into this model. That's ok, then I will give this model to EF Core 5 to save to SQL Server.
public class ExcelModel
{
public string Name { get; set }
public int Price { get; set }
public int Quantity { get; set }
}
I have a problem with product options. According to my DB schema, I have one table of products, one for options, one for option values, one for productOptionRelation.
Can you suggest another solution way or just solve on my way?
My colleges did this created field corresponding to values. like option1 and optionValue1, option2 and optionValue2 many of them, because each product could have many options. Model look like that, 20 option and 20 value was declared here and they manually map all these
For a temporary solution, I limited this option up to 5 and I created an list. and encapsulate all of them into list
public class ExcelOptionViewModel
{
public string Option { get; set; }
public string Value { get; set; }
}
This is my temp model, I encapsulated like that.
public IList<ExcelOptionViewModel> OptionModels { get; set; } = new List<ExcelOptionViewModel>();
public string Option1
{
get { return OptionModels[0].Option; }
set
{
this.OptionModels.Insert(0, new ExcelOptionViewModel { Option = value });
}
}
public string Option1Value
{
get { return OptionModels[0].Value; }
set { this.OptionModels[0].Value = value; }
}
This would be unlimited, You should enter how much you want
I have 2 solutions still I am researching one is, creating a method inside the excelviewmodel, this method will add all options and values into a list or I will use reflection, I am looking something like underlying type I will all option and values this underlying base type or something, when property loop came here, checking the type and assign all option1,option2,option3 or name like that properties to List<string> options, and same for the option values. I will use reading like option[0] and optionvalue[0]
Excel column names must be different because I read excel and turn it into datatable. Datatable column names must be different, it's not valid for reading into datatable
I used basically excel to data table function I can't remember but probably I found it in StackOverflow. Also, I added a feature there If some cell is null it will miss.
public List<T> ConvertDataTableToList<T>(DataTable dt)
{
//datatable clomun names
var columnNames = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName.ToLower()).ToList();
//selection properties equals to columnnames because I dont want loop for all props
var properties = typeof(T).GetProperties().Where(prp => columnNames.Any(t => t.ToLower() == prp.Name.ToLower()));
return dt.AsEnumerable().Select(row =>
{
var objT = Activator.CreateInstance<T>();
foreach (var pro in properties)
{
try
{
if (row[pro.Name] != DBNull.Value)
pro.SetValue(objT, row[pro.Name], null);
else
pro.SetValue(objT, null, null);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
return objT;
}).ToList();
}
I am looking something here when option1 or option2 comes here it would put this into a list
Also in my dt to model converter I dont want to use If but if some data value is null It throws an error which cant convert from dbnull value. If you have a suggest for it I would like release if condition :)
When All done I will map this excelviewmodel to product model something like this
foreach (var prop in SideParams.columns)
{
var source = row.GetType().GetProperty(prop);
var destination = product.GetType().GetProperty(prop);
if (destination != null && source.GetValue(row) != null)
{
Type t = Nullable.GetUnderlyingType(destination.PropertyType) ?? destination.PropertyType;
object safeValue = Convert.ChangeType(source.GetValue(row), t);
destination.SetValue(product, safeValue);
}
}
I saw something here
https://learn.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=net-6.0
it about binding flangs when reflecting model. "Specifies flags that control binding and the way in which the search for members and types is conducted by reflection." If there is way I can redirect option(1-2-3-4-5-6...) to list options
thanks for the help I solved my problem. If you need something like that, my solution is;
As you know OptionModels is what I created before, AddOptipns function is a new one I use for add data to list,
The function work with the ref, otherwise it must be static, if I turn it static, option models also must be static, so I can't access the list.
public IList<ExcelOptionViewModel> OptionModels { get; set; } = new List<ExcelOptionViewModel>();
public void AddOptions(ref String option, ref String value)
{
OptionModels.Add(new ExcelOptionViewModel { Option = option.Trim(), Value = value.Trim() });
}
And also add some new parts to convert model function,
calling that AddOptions method with reflection, I got an example from here
https://learn.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=net-6.0
I was inspired by the swap example there.
public List<T> ConvertDataTableToList<T>(DataTable dt)
{
var columnNames = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName.ToLower()).ToList();
//selection properties equals to columnnames because I dont want loop for all props
var type = typeof(T);
var properties = type.GetProperties().Where(prp => columnNames.Any(t => t.ToLower() == prp.Name.ToLower())).ToList();
var productOptions = columnNames.Where(x => x.Contains("option")).ToList() ?? new List<string>();
return dt.AsEnumerable().Select(row =>
{
var objT = Activator.CreateInstance<T>();
foreach (var pro in properties)
{
try
{
if (row[pro.Name] != DBNull.Value)
pro.SetValue(objT, row[pro.Name], null);
else
pro.SetValue(objT, null, null);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
for (var i = 0; i < productOptions.Count(); i += 2)
{
object[] argValues = new object[] { row[productOptions[i]].ToString(), row[productOptions[i + 1]].ToString() };
String[] argNames = new String[] { "option", "value" } ;
var method = type.GetMethod("AddOptions");
method.Invoke(objT, argValues);
}
return objT;
}).ToList();
}
here is the added data :)
I have multiple dictionaries. Each of them contains the same keys, just with different values. I am using them for translations. They are dynamically created..
So, it looks something like this:
DictionaryEng:
first_page_name = "First page"
second_page_name = "Second page"
DictionaryRu:
first_page_name = "Первая страница"
second_page_name = "Вторая страница"
I would like this informations to be shown in DataGrid or, so user can change the values. It should look like this, end values to be editable.
What's the best practice to do this?
Basically you need to bind the DataGrid to a Collection
<DataGrid
ItemsSource="{Binding Lines}" AutoGenerateColumns="True"
I'll mock up the creation of the dictionary with stub functions called in the ViewModel c.tor
public class MyVM : ViewModelBase
{
public MyVM()
{
Line.DictionaryEng = Line.DictionaryEngStub();
Line.DictionaryRu = Line.DictionaryRuStub();
lines = new ObservableCollection<Line>(Line.DictionaryEng.Keys.Select(k => new Line() { KeyWord = k }));
}
private ObservableCollection<Line> lines;
public ObservableCollection<Line> Lines
{
get { return lines; }
set
{
lines = value;
OnPropertyChanged("Lines");
}
}
}
where the underlying class is defined as follows
public class Line : ViewModelBase
{
internal static Dictionary<string, string> DictionaryEngStub()
{
return new Dictionary<string, string>()
{
{ "first_page_name ","First page" },
{ "second_page_name ","Second page" }
};
}
internal static Dictionary<string, string> DictionaryRuStub()
{
return new Dictionary<string, string>()
{
{"first_page_name ","Первая страница" },
{"second_page_name ","Вторая страница" }
};
}
internal static Dictionary<string, string> DictionaryEng = new Dictionary<string, string>();
internal static Dictionary<string, string> DictionaryRu = new Dictionary<string, string>();
private string keyWord;
public string KeyWord
{
get { return keyWord; }
set
{
keyWord = value;
OnPropertyChanged("keyWord");
}
}
public string EnglishWord {
get
{
string english;
if (DictionaryEng.TryGetValue(keyWord ?? "", out english))
{
return english;
}
return null;
}
}
public string RussianhWord
{
get
{
string russian;
if (DictionaryRu.TryGetValue(keyWord ?? "", out russian))
{
return russian;
}
return null;
}
}
}
Notice that the translations have only a getter to retrieve the string from the dictionary.
You can easily make them editable by adding a setter to save the new translation into a persistence layer. Furthermore, English and Russian dictionaries are generic enough to be renamed as from/to dictionaries. Once the user selects a language in another combobox, you can reset the dictionary accordingly.
Since I have a stub here, the setter does not make much sense, but just to give you and idea...
private string englishSaved;
public string EnglishWord {
get
{
if (englishSaved != null)
{
return englishSaved;
}
string english;
if (DictionaryEng.TryGetValue(keyWord ?? "", out english))
{
return english;
}
return null;
}
set
{
englishSaved = value; //save the new translation into a persistence layer
}
}
I am unsure whether this is possible with an ArrayList or a Dictionary or whether it would be something else, if so I wonder where you could point me in the right direction...
Can you have an ArrayList with Multiple Values i.e.
ArrayList weather = new ArrayList();
weather.Add("Sunny", "img/sunny.jpg");
weather.Add("Rain", "img/Rain.jpg);
To then assign to controls like below.
if (WeatherValue = 0)
{
Label1.Text = weather[0].ToString;
Image1.ImageUrl = weather[0].ToString;
}
Or can I do this with a Dictionary
Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("Cloudy", "../img/icons/w0.png"); //[0]
dict.Add("Rain", "../img/icons/w1.png"); //[1]
Label1.Text = dict[0].VALUE1; //So this would get Cloudy
Image.ImageUrl = dict[0].VALUE2; //This would get ../img/w0.png
How do you call the values of a dictionary separately using [0], and [1] ? etc
There's no reason to still use ArrayList, use the System.Collections.Generic.List<T>-class. Then you keep compile time safety and you don't need to cast everything.
In this case you should create a custom class:
public class Weather
{
public double Degree { get; set; }
public string Name { get; set; }
public string IconPath { get; set; }
public override string ToString()
{
return Name;
}
}
Then you can use this readable and maintainable code:
List<Weather> weatherList = new List<Weather>();
weatherList.Add(new Weather { Name = "Sunny", IconPath = "img/sunny.jpg" });
weatherList.Add(new Weather { Name = "Rain", IconPath = "img/Rain.jpg" });
if (WeatherValue == 0) // whatever that is
{
Label1.Text = weatherList[0].Name;
Image1.ImageUrl = weatherList[0].IconPath;
}
Update: according to your edited question. A dictionary doesn't make much sense because you can't access it via index(it has no order) but only via key. Since that would be the weather-name you have to know it beforehand. But it seems that you don't have it.
So either loop all key-value pairs in the dictionary and use the key for the name and the value for the path or simply use a real class which would be much better.
If you don't want to create a class there's only one thing that comes to my mind, the Tuple:
List<Tuple<string, string>> weatherList = new List<string, string>();
weatherList.Add(Tuple.Create("Sunny", "img/sunny.jpg"));
weatherList.Add(Tuple.Create("Rain", "img/Rain.jpg"));
if (WeatherValue == 0) // whatever that is
{
Label1.Text = weatherList[0].Item1;
Image1.ImageUrl = weatherList[0].Item2;
}
You can use a Dictionary
Dictionary<string, string> weather = new Dictionary<string, string>();
values.Add("Sunny", "img/sunny.jpg");
values.Add("Rain", "img/Rain.jpg");
The simplest way to call element in a dictionnary is using foreach loop
foreach (var pair in weather )
{
Console.WriteLine("{0}, {1}",pair.Key,pair.Value);
}
I'm doing something wrong because after the loop executed myData still contains objects with blank ids. Why isn't the myData object being updated in the following foreach loop, and how do I fix it?
I thought it could be that I wasn't passing the object by reference, but added a ref keyword and also moved to the main method and I'm still showing the object not being updated.
Additional Information
The user object in the foreach loop is being updated, but the myData list does not reflect the updates I see being applied to the user object.
** Solution **
I was not creating a List but an Enumerable which was pulling the json each time I went through myData in a foreach list. Adding a ToList() fixed my issue.
public class MyData
{
public string ID { get; set; }
public Dictionary<string, string> Properties { get; set; }
}
int index = 0;
// Does not allow me to up, creates an IEnumerable
//IEnumerable<MyData> myData = JObject.Parse(json)["Users"]
// .Select(x => new MyData()
// {
// ID = x["id"].ToString(),
// Properties = x.OfType<JProperty>()
// .ToDictionary(y => y.Name, y => y.Value.ToString())
// });
//Works allows me to update the resulting list.
IEnumerable<MyData> myData = JObject.Parse(json)["Users"]
.Select(x => new MyData()
{
ID = x["id"].ToString(),
Properties = x.OfType<JProperty>()
.ToDictionary(y => y.Name, y => y.Value.ToString())
}).ToList();
foreach (var user in myData) // Also tried myData.ToList()
{
if (string.IsNullOrEmpty(user.ID))
{
user.ID = index.ToString();
user.Properties["id"] = index.ToString();
}
index++;
}
public class MyData
{
public MyData()
{
this.Properties = new Dictionary<string,string>();
}
public string ID { get; set; }
public Dictionary<string, string> Properties { get; set; }
}
public static void Main(string[] args)
{
IEnumerable<MyData> myDataList = new List<MyData>();
int index = 0; // Assuming your starting point is 0
foreach (var obj in myDataList)
{
if (obj != null && string.IsNullOrEmpty(obj.ID))
{
obj.ID = index.ToString();
// Checks if the Properties dictionary has the key "id"
if (obj.Properties.ContainsKey("id"))
{
// If it does, then update it
obj.Properties["id"] = obj.ID;
}
else
{
// Else add it to the dictionary
obj.Properties.Add("id", obj.ID);
}
}
index++;
}
I believe the reason why your objects are not updating because it's probably still referring to the memory block before your objects were changed. Perhaps. The easiest way (that I can think of, there are thousands of smarter programmers than me) is to create a new list and have it contain all of your updated objects.
Edit
I updated the code above with the code that I have. I created a method to set a small amount of objects to test:
private static IEnumerable<MyData> GetMyData()
{
return new List<MyData>()
{
new MyData(),
new MyData() {ID = "2"},
new MyData() {ID = "3"},
new MyData()
};
}
I was able to view my changes and then go through a foreach loop to view my changes. If the ID of the object is Null or Empty, then it steps into the if check and adds the current index to the ID as you know.
Now for my question: Which "id" is blank? The "id" in the dictionary or is it the ID of the model? Are all of your (Model).ID blank? As the updated code of yours, if your dictionary doesn't have "id" as a key, it's going to throw an exception saying it doesn't exist so you will need to do a check to make sure it does exist or add it if it doesn't.
How can I store data from 2 columns (from a database) in a List
List<string> _items = new List<string>();
Any help is appreciated
You create a class that will represent a row with 2 columns:
public class Foo
{
// obviously you find meaningful names of the 2 properties
public string Column1 { get; set; }
public string Column2 { get; set; }
}
and then you store in a List<Foo>:
List<Foo> _items = new List<Foo>();
_items.Add(new Foo { Column1 = "bar", Column2 = "baz" });
Use a tuple struct like KeyValuePair
List<KeyValuePair<string, string>> _items = new List<KeyValuePair<string, string>>();
_items.Add(new KeyValuePair<string, string>(foo, bar));
I would use a class
List<MyDataClass> _items = new List<MyDataClass>();
public class MyDataClass
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
You can either create a new class to hold the data, Or you could use the built in Tuple<> class. http://msdn.microsoft.com/en-us/library/system.tuple.aspx
Also if one of the columns contains a unique ID of some sort, you could also consider using a Dictionary<>.
It's about how to retrieve the data from the new two columns list
List<ListTwoColumns> JobIDAndJobName = new List<ListTwoColumns>();
for (int index = 0; index < JobIDAndJobName.Count;index++)
{
ListTwoColumns List = JobIDAndJobName[index];
if (List.Text == this.cbJob.Text)
{
JobID = List.ID;
}
}
I know this question is pretty old and by now you probably got your answer and have figured out what you need but I wanted to add something that might help someone in the future.
The best current answer is frankly from #csharptest.net but it has a serious performance drawback and so here is my approach a la his answer based on a suggestion to use Dictionary<TKey, TValue>
private Dictionary<string, string> _items = new Dictionary<string, string>();
// if you need to check to see if it exists already or not
private void AddToList(string one, string two)
{
if (!_items.ContainsKey(one))
_items.Add(one, two);
}
// you can simplify the add further
private void AddToList(string one, string two)
{
_items[one] = two;
// note if you try to add and it exists, it will throw exception,
// so alternatively you can wrap it in try/catch - dealer's choice
}
you can also make array of list
List<string> [] list= new List<String> [];
list[0]=new List<string>();
list[1]=new List<string>();
list[0].add("hello");
list[1].add("world");
You could do this:
List<IList<string>> cols = new List<IList<string>>();
You can set how many columns you want.
cols.Add(new List<string> { "", "", "","more","more","more","more","..." });