I want to implement a Dictionary cache in my program. How can I store the database result seen in the image below in a Dictionary Collection ?
I want to iterate over the Database table and store the content of LanguageName and IsoCode columns in a Dictionary like this Dictionary<LanguageName,IsoCode>.
My database (ctlang) looks like this:
Here is my code:
private string GetLanguageForIsoCode(string isoCode)
{
//check the isocode column and return the corresponding language
using (var unitOfWork = dataAccessUnitOfWorkFactory.Create())
{
//need to call every time the sql query
string query = "SELECT languagename FROM ctlang WHERE isocode='" + isoCode + "'";
List<string> result = unitOfWork.OwEntities.Database.SqlQuery<string>(query).ToList();
if (result.FirstOrDefault() != null)
{
return result.FirstOrDefault();
}
//if language not available in Database, fallback to German as default language
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("de");
//displayName = Deutsch
return CultureInfo.GetCultureInfo("de").NativeName;
}
}
Bonus question: How can I search for a key in a dictionary using the value ?
It is. There is no problem in returning a key by its value, the only issue would be that there could theoretically be more than one key assigned to that value, since the key is what matters. But in your particular case that should be no problem, since only one isocode represents one language. So there would be no problem to make it like that, with dictionary:
public Dictionary<string, string> languagesAndKeys = new Dictionary<string, string>(); //Create it
Then you can search for keys/values like that:
string myValueByKey = languagesAndKeys["myKey"]; //getting value by key is easy
string myKeyByValue = languagesAndKeys.FirstOrDefault(item => item.Value == "myValue").Key; //getting the key of the FIRST matching value/or returning the default type. You'll need a check to be sure.
Afterwards, you can easily load the data from the sql table into the dictionary. For this goal, you can either use a temporary datatable with dataadapter, which will work well as long as the table is not that big, or you can use a DataReader to loop trough rows in the sql table one by one. I'm gonna be using a temp DataTable:
string cmdText = "SELECT * FROM ctlang"; //As far as I saw your cmd text in the code example, you may still want to take a look tho
string connectionString = ""; //fill the connection string according to your SQL server data
SqlDataAdapter dataAdapter = new SqlDataAdapter(cmdText, connectionString);
DataTable dTable = new DataTable();
dataAdapter.Fill(dTable);
foreach(DataRow row in dTable.Rows)
languagesAndKeys.Add(row[1].ToString(), row[0].ToString());//second column as a key, first column as a value - just like the structure of your table.
This is how I solved the problem. In my Database the Table with the languages is named CTLANG and the columns are LANGUAGENAME and ISOCODE. I wanted to map these to into a Dictionary collection. So that at the end the dictionary looks like this: ["LanguageName","IsoCode"].
private static Dictionary<string, string> languageToIsoCode; //dictionary cache
private void InitializeLanguageCacheDictionary()
{
using (var unitOfWork = dataAccessUnitOfWorkFactory.Create())
{
languageToIsoCode = (from p in unitOfWork.OwEntities.CTLANG
select new {p.LANGUAGENAME, p.ISOCODE}).ToDictionary(p => p.LANGUAGENAME, p => p.ISOCODE);
}
}
Thanks to #D.Petrov I also found a way to search for the key in a dictionary and give its value back.
And this is how I optimized my method to use the dictionary cache.
private string GetLanguageForIsoCode(string isoCode)
{
if (languageToIsoCode == null)//if cache empty initialize it
{
InitializeLanguageCacheDictionary();
}
//searches inside the dictionary, look for value and then return key
//might be bad if there are more than one value asigned to a key
//because value does not habe to be unique
string languageFromIsoCodeFromCache = languageToIsoCode.FirstOrDefault(item => item.Value == isoCode).Key;
if (languageFromIsoCodeFromCache == null)
{
//fallback and use the German language as default
return CultureInfo.GetCultureInfo("de").NativeName;
}
return languageFromIsoCodeFromCache;
}
Related
I am looking for a way to obtain my property names in a SSIS data flow task Script Component. I have been searching high and low only finding this. I have been trying to get this code to work, but I am too novice to understand what is happening here and I don't feel it is explained very well(no offense).
The source before this component is using a SQL query joining two tables. Inside the component, I would like to compare column to column. Then call an update method I created to use SqlConnection to perform the update.
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
if (Row.TableALastName != Row.TableBLastName)
// Call the update method if the last name did not match.
this.UpdateRecord("TableBLastName", Row.TableALastName.ToString(), Row.TableAAssociateId.ToString());
}
}
private void UpdateRecord(string columnName, string change, string associateId)
{
SqlConnection sqlConnection;
sqlConnection = new SqlConnection(this.Variables.Connection);
string updateQuery = "UPDATE [SomeDataBase].[dbo].[TableB] SET " + columnName + " = " + change + " WHERE [Associate_ID] = " + associateId;
using (SqlCommand cmd2 = new SqlCommand(updateQuery, sqlConnection))
{
sqlConnection.Open();
cmd2.ExecuteNonQuery();
sqlConnection.Close();
}
}
I would like to somehow get the PropertyName of Row.TableBLastName instead of having to hard code "TableBLastName" for each test I am doing, which will be a lot.
The problem is that the input buffer class does not have Property.GetName() This also means I can't add a method to the class to get the property names, as it is regenerated each run.
public Input0_ProcessInputRow(Input0Buffer Row)
{
Dictionary<string, List<string>> list = new Dictionary<string, List<string>>();
List<string> propertyList = new List<string>();
Type myType = typeof(Input0Buffer);
PropertyInfo[] allPropInfo = myType.GetProperties();
List<PropertyInfo> SqlPropInfo = allPropInfo.Where(x => !x.Name.Contains("AM_")).ToList();
// Loop through all the Sql Property Info so those without AM_
for (int i = 0; i < SqlPropInfo.Count(); i++)
{
List<string> group = new List<string>();
foreach (var propInfo in allPropInfo)
{
if (propInfo.Name.Contains(SqlPropInfo[i].Name))
{
// Group the values based on the property
// ex. All last names are grouped.
group.Add(propInfo.GetValue(Row, null).ToString());
}
}
// The Key is the Sql's Property Name.
list.Add(SqlPropInfo[i].Name, group);
}
foreach (var item in list)
{
// Do a check if there are two values in both SQL and Oracle.
if (item.Value.Count >= 2)
{
if (item.Value.Count() != item.Value.Distinct().Count())
{
// Duplicates exist do nothing.
}
else
{
// The values are different so update the value[0]. which is the SQL Value.
UpdateRecord(item.Key, item.Value[0], Row.AssociateId);
}
}
}
}
I separated the values from the two tables so there are two lists values from TableA and TableB. You can prefix the values from TableA with "AM_" or something distinct so you can use reflection to to get the properties with and without the prefix and find out which values belong to which table. Then I just loop through the properties and group the values with the properties from the target value (so those without the prefix "AM_") I then loop through the grouped list and compare the two values and if it's different, update TableA with the TableB values to match them
You are already in SSIS so I will propose using that (no matter how quick I usually jump to C# to solve problems)
This is a classic conditional split scenario:
Do your test then run the results into a SQL Update statement.
I have a List<> of say People, and people have an instance of a Thought object, I display People in a gridview, there is one column for each variable of the People Class and one for Thoughts, which displays the Thought's class Tostring Override, when I delete a thought, if it was used by any given "People" It would set that People's thought to "null" but when I try to retrieve that "null" from the db (ADO.NET) I get an error (which I think doesn't matter atm), how can I display a "NULL" on the gridview of People in which thoughts have been deleted?
EDIT:
Here is the method I use to save the DATASET:
public List<People> PeopleList()
{
ControllerClass cont = new ControllerClass();
List<People> list = new List<People>();
string strSQL = "SELECT * FROM People";
DataSet data = Select(strSQL);
if (data.Tables[0].Rows.Count > 0)
{
foreach (DataRow Row in data.Tables[0].Rows)
{
People p = new People();
p.IdPeople= Convert.ToInt32(Row.ItemArray[0].ToString());
p.NamePeople= Row.ItemArray[1].ToString();
p.ThoughtPeople= cont.SearchThought(Convert.ToInt32(Row.ItemArray[3].ToString()));
list.Add(p);
}
}
return list;
}
I use a method to search for thoughts because I save the thought's ID as a foreign key
Handle it like
p.NamePeople= Row.ItemArray[1] == DBNull.Value ? "NULL" : Row.ItemArray[1].ToString():
So I have been creating a library that uses dapper and allows user to manipulate a database.
I need some help with finding the best way to achieve the following.
Lets say I have an "order" table and I have a "transaction" table and an "order_line" table.
I want to take the Increment Id of table "order" when inserting and use it to store it in a column in "transaction" and "order_line" table and I want all of this done in a SQL transaction so that I can roll back in case of any issue.
Now since my library is dynamic to any type and action, I am not sure on how to approach something like this.
Here is the code on how you would insert:
I have 2 global variables
private string connectionString { get; set; }
public void newConnection(string connection)
{
if (string.IsNullOrWhiteSpace(connectionString))
{
connectionString = connection;
}
}
private List<KeyValuePair<string, object>> transactions = new List<KeyValuePair<string, object>>();
Here is how you call to have a class to be saved to the database:
public void Add(object item)
{
string propertyNames = "";
string propertyParamaters = "";
Type itemType = item.GetType();
System.Reflection.PropertyInfo[] properties = itemType.GetProperties();
for (int I = 0; I < properties.Count(); I++)
{
if (properties[I].Name.Equals("Id", StringComparison.CurrentCultureIgnoreCase) || properties[I].Name.Equals("AutoId", StringComparison.CurrentCultureIgnoreCase))
{
continue;
}
if (I == properties.Count() - 1)
{
propertyNames += "[" + properties[I].Name + "]";
propertyParamaters += "#" + properties[I].Name;
}
else
{
propertyNames += "[" + properties[I].Name + "],";
propertyParamaters += "#" + properties[I].Name + ",";
}
}
string itemName = itemType.Name;
KeyValuePair<string, object> command = new KeyValuePair<string, object>($"Insert Into[{ itemName}] ({ propertyNames}) Values({ propertyParamaters})", item);
transactions.Add(command);
}
There are more methods and like edit, remove, edit list, remove list etc. but are not relevant in this case.
When you want to commit changes to the database you call:
public void SaveChanges()
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (SqlTransaction sqlTransaction = sqlConnection.BeginTransaction())
{
try
{
foreach (KeyValuePair<string, object> command in transactions)
{
sqlConnection.Execute(command.Key, command.Value, sqlTransaction);
}
sqlTransaction.Commit();
}
catch
{
sqlTransaction.Rollback();
throw;
}
finally
{
sqlConnection.Close();
transactions.Clear();
}
}
sqlConnection.Close();
}
transactions.Clear();
}
You can find my library at github.com
https://github.com/pietercdevries/Bamboo.Net
Can it be done... yes... should we be trying to do this ourselves... I wouldn't :) but lets try it any way.
Some ideas that can make this code simpler:
Define helper interfaces and force the data classes to implement them or use attribute declarations to specify id fields and foreign key references
Investigate Injection or code generation techniques so that you can get some of this 'dynamic' coding and lookup executed at compile time, not runtime.
I don't use Dapper and your SqlConnection.Execute() is an extension method I am not familiar with but I assume that it generates DbParameters from the passed in object and applies them to the SqlCommand when it gets executed. Hopefully dapper has some functions to extract the parameters, so that they can be used in this code example, or perhaps you can use some of these concepts and adapt them to your dapper code. I just want to acknowledge that upfront and that I have omitted any code example here that parameterises the objects when executing the commands.
This is the journey that the following snippets will go down
Prepare the generated SQL to capture the Id field
Output the Id value when we save changes
Iterate over all remaining objects in the array and set the foreign key values
Note: these code changes are not tested or exception handled for production, nor would I call this "best practice" its just to prove the concept and help out a fellow coder :)
You have already devised a convention for Id field tracking, lets extend that idea by preparing the sql statement to set the value of an output parameter:
NOTE: in MS SQL, please use SCOPE_IDENTITY() in preference to ##Identity.
What is the difference between Scope_Identity(), Identity(), ##Identity, and Ident_Current?
NOTE: because the generated statements are using parameters, and we are not yet reading the parameter values, we will not need to regenerate the saved SQL statements later after we have found an Id value to insert to other objects... phew...
public void Add(object item)
{
List<string> propertyNames = new List<string>();
Type itemType = item.GetType();
System.Reflection.PropertyInfo[] properties = itemType.GetProperties();
for (int I = 0; I < properties.Count(); I++)
{
if (properties[I].Name.Equals("Id", StringComparison.CurrentCultureIgnoreCase) || properties[I].Name.Equals("AutoId", StringComparison.CurrentCultureIgnoreCase))
{
continue;
}
propertyNames.Add(properties[I].Name);
}
string itemName = itemType.Name;
KeyValuePair<string, object> command = new KeyValuePair<string, object>
($"Insert Into[{itemName}] ({String.Join(",", propertyNames.Select(p => $"[{p}]"))}) Values({String.Join(",", propertyNames.Select(p => $"#{p}"))}); SET #OutId = SCOPE_IDENTITY();", item);
transactions.Add(command);
// Simply append your statement with a set command on an #id parameter we will add in SaveChanges()
}
In Save Changes, implement output parameter to capture the created Id, and if the Id was captured, save it back into the object that the command is associated to.
NOTE: this code snippet shows the references to the solution in item 3.
And the foreach was replaced with a for so we could do forward iterations from the current index
public void SaveChanges()
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (SqlTransaction sqlTransaction = sqlConnection.BeginTransaction())
{
try
{
for (int i = 0; i < transactions.Count; i++)
{
KeyValuePair<string, object> command = transactions[i];
// 1. Execute the command, but use an output parameter to capture the generated id
var cmd = sqlConnection.CreateCommand();
cmd.Transaction = sqlTransaction;
cmd.CommandText = command.Key;
SqlParameter p = new SqlParameter()
{
ParameterName = "#OutId",
Size = 4,
Direction = ParameterDirection.Output
};
cmd.Parameters.Add(p);
cmd.ExecuteNonQuery();
// Check if the value was set, non insert operations wil not set this parameter
// Could optimise by not preparing for the parameter at all if this is not an
// insert operation.
if (p.Value != DBNull.Value)
{
int idOut = (int)p.Value;
// 2. Stuff the value of Id back into the Id field.
string foreignKeyName = null;
SetIdValue(command.Value, idOut, out foreignKeyName);
// 3. Update foreign keys, but only in commands that we haven't execcuted yet
UpdateForeignKeys(foreignKeyName, idOut, transactions.Skip(i + 1));
}
}
sqlTransaction.Commit();
}
catch
{
sqlTransaction.Rollback();
throw;
}
finally
{
sqlConnection.Close();
transactions.Clear();
}
}
sqlConnection.Close();
}
transactions.Clear();
}
/// <summary>
/// Update the Id field of the specified object with the provided value
/// </summary>
/// <param name="item">Object that we want to set the Id for</param>
/// <param name="idValue">Value of the Id that we want to push into the item</param>
/// <param name="foreignKeyName">Name of the expected foreign key fields</param>
private void SetIdValue(object item, int idValue, out string foreignKeyName)
{
// NOTE: There are better ways of doing this, including using interfaces to define the key field expectations.
// This logic is consistant with existing code so that you are familiar with the concepts
Type itemType = item.GetType();
foreignKeyName = null;
System.Reflection.PropertyInfo[] properties = itemType.GetProperties();
for (int I = 0; I < properties.Count(); I++)
{
if (properties[I].Name.Equals("Id", StringComparison.CurrentCultureIgnoreCase) || properties[I].Name.Equals("AutoId", StringComparison.CurrentCultureIgnoreCase))
{
properties[I].SetValue(item, idValue);
foreignKeyName = $"{item.GetType().Name}_{properties[I].Name}";
break;
}
}
}
So now your objects have their Id's updated as they are inserted.
Now for the fun part... After updating the Id, you should now iterate through the other objects and update their foreign key fields.
How you go about this in reality depends a lot on what kind of assumptions/conventions you are ready enforce over the data that you are updating. For simplicity sake, lets say that all of the foreign keys that we need to update are named with the convention {ParentClassName}_{Id}.
That means that if in our example we just inserted a new 'Widget', then we can try to forcibly update all other objects in this transaction scope that have a field 'Widget_Id' (or 'Widget_AutoId')
private void UpdateForeignKeys(string foreignKeyName, int idValue, IEnumerable<KeyValuePair<string, object>> commands)
{
foreach(var command in commands)
{
Type itemType = command.Value.GetType();
var keyProp = itemType.GetProperty(foreignKeyName);
if(keyProp != null)
{
keyProp.SetValue(command.Value, idValue);
}
}
}
This is a very simplistic example of how you could go about updating foreign (or reference) keys in OPs data persistence library.
You have probably observed in reality that relational key fields are rarely consistently named using any convention, but even when conventions are followed, my simple convention would not support a table that had multiple references to parents of the same type, for example a Manifest in one of my client's apps has 3 links back to a user table:
public class Manifest
{
...
Driver_UserId { get; set; }
Sender_UserId { get; set; }
Receiver_UserId { get; set; }
...
}
You would need to evolve some pretty advanced logic to tackle all possible linkage combinations.
Some ORMs do this by setting the values as negative numbers, and decrementing the numbers each type a new type is added to the command collection. Then after an insert you only need to update key fields that held the faked negative number with the updated number. You still need to know which fields are key fields, but atleast you don't need to track the precise fields that form the ends of each relationship, we can track with the values.
I like how Entity Framework goes about it though, try inject this linkage information about the fields using attributes on the properties. You may have to invent your own, but it's a clean declarative concept that forces you to describe these relationships up front in the data model classes in a way that all sorts of logic can later take advantage of, not just for generating SQL statements.
I don't want tobe too critical of Dapper, but once you start to go down this path or manually managing referential integrity like this there is a point where you should consider a more enterprise ready ORM like Entity Framework or nHibernate. Sure they come with some baggage but those ORMs have really evolved into mature products that have been optimised by the community. I now have very little manually written or scripted code to customise any interactions with the RDBMS at all, which means much less code to test or maintain. (= less bugs)
It doesn't say which database you are using. If it is MSSQL you can do
var id = connection.Query<int?>("SELECT ##IDENTITY").SingleOrDefault();
after executing the Insert. That gives you the id of the last insert.
So I am building a CSV Importer tool, now I have a dictionary that has a key value pair for the columns in the data gridview for comparison purposes.
var events = row.Cells[2].Value; // class DataGridViewCell.Value
That has the value and I want to compare it to the dictionary that I have and return the key.
How can I do this without using Linq, if I have to use linq I need to be able to convert the key into a string like the following:
ID = "3";
You should use LINQ, try this:
var x = yourDictionary.FirstOrDefault(entry=>object.Equals(entry.Value,events));
if(!x.Equals(default(KeyValuePair<long,string>))) {
var id = x.Key.ToString();
//...
}
NOTE: if you do the same task many times, you should use another Dictionary whose Keys is the Values of your first Dictionary and whose Values is the Keys of your first Dictionary like this:
var inverseDictionary = yourDictionary.Select(key=>key.Value, value=>value.Key)
.ToDictionary();
//Then use it like this:
long x;
if(inverseDictionary.TryGetValue((string)events,out x)){
var id = x.Value.ToString();
//...
}
I have a (static) table which I need to access very frequently. Therefore I cache it at run time.
For now I'm using a DataTable to store the information and the Select() to retrieve it (the filtering is usually spread over 2 or 3 columns). But this appears to be very (!) slow and is a major bottleneck.
Therefore: What is the best way of caching a DB table (in .Net 2.0) and how to access it the fastest?
[edit 1]
DataTable cache = new DataTable();
cache.Columns.Add("id", typeof(int));
cache.Columns.Add("data_id", typeof(int));
cache.Columns.Add("data", typeof(string));
cache.Columns.Add("value", typeof(float));
Eg:
DataRow[] result1 = cache.Select("id = 1 AND data_id = 2 AND data = 'some data'");
DataRow[] result2 = cache.Select("data = 'some other data'", "data desc");
[edit 2]
Having in mind the suggestion from #MichaelPerrenoud, I ended up implementing a Dictionary. Please have in mind that this design is with respect to the structure of the DB table that I needed cached.
Dictionary<string, Dictionary<NewObject, float>> cache;
class NewObject {int id; int data_id;}
This way the cache has as the [key] the data and as the [value] a new Dictionary. And this latter has as the [key] the id + data-id and as the [value] the value.
In accessing the data, this Dictionary is ~%90 faster than the DataTable.
So I think a good solution for you would be something like this. I'm making the assumption that you have the DataTable after you initially get the data.
DISCLAIMER: I have not compiled this code - it's a concept - you may need to fix some syntax.
Dictionary<string, List<DataRow>> CacheData(DataTable t, params string[] fields)
{
Dictionary<string, List<DataRow>> cache = new Dictionary<string, List<DataRow>>();
StringBuilder key = new StringBuilder();
foreach (DataRow r in t.Rows)
{
key.Clear();
foreach (string f in fields)
{
if (key.Length > 0) { key.Append("_"); }
key.Append(Convert.ToString(r[f]);
}
if (!cache.ContainsKey(key.ToString())) { cache.Add(key.ToString(), new List<DataRow>()); }
cache[key.ToString()].Add(r);
}
return cache;
}
Now, you can store those dictionaries any way you see fit and then pull data out of them when you need to, so for example if you needed to handle the first example it might look something like this to create the cache:
CacheData(table, "id", "data_id", "data");
and then to get data out you would build a string like this:
string key = "1_2_some data";
and pull the list of rows out of the dictionary like this:
dictionary[key];
and that would return a List<DataRow> for you to use.
But, you'll only have to do this one time and then leverage those dictionaries over and over.