Dynamic query with linq to IDictionary<string, object>? - c#

This question is similar to this question but not quite the same.
Can I make a dynamic query on a linq-to-sql data context and have the result return as an IDictionary<string, object> where the key is the name of the column?
A bit like so (doesn't compile, but illustrates the intention)
IDictionary<string, object> data = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
data = db.ExecuteQuery<IDictionary<string, object>(queryString, params).ToDictionary(
k=>nameOfColumn,
k=>k
)
Obviously I'm totally off the map in the data = db.ExecuteQuery...
I have the feeling that the executeQuery is not what I'm ought to use as this one already tries to do the mapping to an object. For this particular use case I don't want that.

You'll need to include an object type to materialize; how about:
var data = db.ExecuteQuery<SomeType>(queryString, params)
.ToDictionary(st => st.SomeKey);

Related

Using a dictionary to perform basic CRUD operations and getting an sqlite syntax error

I'm learning SQL and decided to use a data structure I've never used before, a dictionary, to create some methods to perform basic CRUD operations. The first method that I am working on is a method to delete a person given a table name and a dictionary containing the strings to be used in the SQL statement. Here is the code.
class QueryBuilder
{
public void DeleteMemer(string tableName, Dictionary<string, string> columns)
{
var sqliteCommand = new SQLiteCommand($"delete from '{tableName}' where {columns.Keys} = '{columns.Values}'", connection);
sqliteCommand.ExecuteNonQuery();
}
}
class Program
{
static void Main(string[] args)
{
DBManagerSQLite memes = new DBManagerSQLite("SqliteDataBase.db");
QueryBuilder queryBuilder = new QueryBuilder(memes.connection);
Dictionary<string, string> dict = new Dictionary<string, string>();
//------------------DELETE TEST------------------
dict.Add("Memer", "Ryan");
queryBuilder.DeleteMemer("Memers", dict);
}
}
Edited for clarity. I get a run time error on the sqliteCommand.ExecuteNonQuery() line. I suspect this is because of the dictionary I am using. I previously was using all my CRUD operations with simple strings but decided to change it and use a dictionary. I think I am not understand how to use a dictionary in this way. If I remove the dictionary and just hard code strings, the methods work just fine.
This can lead to SQL injection - I'd suggest you sanitize input using SqlParameter
When injected like this, {columns.Keys} will transfrom to something like System...ICollection, which is definetely not the column name.
It would be good if you clarified the question. Assuming the problem is exception on syntax error:
You can iterate over key value pairs to create a WHERE clause for your query.
Example (without sanitizing!):
var whereClause = string.Join(" OR ", columns.Select(kvp => $"{kvp.Key} = '{kvp.Value}'")); // join with AND to create more restrictive query
var sqliteCommand = new SQLiteCommand($"delete from '{tableName}' where {whereClause}", connection);
UPD:
Apart from question, why do you try to use the db that way? It's fairly easy to implement code-first EF context, and you will get, along with simplicity, compile-time checks on your queries. And only once you will need more speed, you can switch to any micro-ORM and carefully optimize performance-critical queries

ExpandoObject removing multiple values dynamically

Originally I deserialize a JSON string into a dynamic type List, this worked fine until moving it to the server at which point the dynamic list stopped working. The only answer I could find is that this is not abnormal behavior for lists. The original question
So from that I changed my dynamic list to an ExpandoObject
dynamic root = JsonConvert.DeserializeObject<ExpandoObject>(jsonstring, new ExpandoObjectConverter());
Originally in my dynamic list I called
root.record.form_values.Remove("f161");
root.record.form_values.Remove("41df");
root.record.form_values.Remove("a228");
root.record.Remove("changeset_id");
Which worked as expected removing those objects from the List which I eventually turn back into JSON and send it back to an API.
After doing some reading on ExpandoObjects I found that to delete you need to throw it out to a IDictionary in order to have the .Remove functionality.
So I did this:
dynamic dict = (IDictionary<string, object>)root;
And then replaced my code with:
dict.record.form_values.Remove("f161");
dict.record.form_values.Remove("41df");
dict.record.form_values.Remove("a228");
dict.record.Remove("changeset_id");
This resulted in the error
'System.Dynamic.ExpandoObject' does not contain a definition for
'Remove'
So after a bit more reading I tried the following:
dict = (IDictionary<string, object>)root.record.form_values.Remove("f161");
dict = (IDictionary<string, object>)root.record.form_values.Remove("41df");
dict = (IDictionary<string, object>)root.record.form_values.Remove("a228");
dict = (IDictionary<string, object>)root.record.Remove("changeset_id");
Which resulted in the same error...
If anyone can give me a clue on where to go to from now, that would be great.
FYI The JSON Structure looks as such:
"{
\"record\":{
\"status\":\"somevalue\",
\"form_values\":
{
\"833b\":\"somevalue\",
\"683b\":\"somevalue\",
\"c9ca\":{\"other_values\":[],\"choice_values\":[\"somevalue\"]}
},
\"latitude\":somevalue,
\"longitude\":somevalue
}
}"
The original code with the DynamicList (Which worked) looks as such:
string jsonstring = data;
var root = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(jsonstring);
root.record.assigned_to = assignedto;
root.record.assigned_to_id = assignedtoid;
root.record.status = status.ToString();
root.record.bb42 = abudgetunit;
root.record.f694 = abudgetunitstr;
root.record.form_values.Remove("f161");
root.record.form_values.Remove("41df");
root.record.form_values.Remove("a228");
root.record.Remove("changeset_id");
You're very close. There are nested expando objects in the structure, and you've only converted the first one. If you find the nested objects and convert all the way down to the form_values then it will work.
dynamic root = JsonConvert.DeserializeObject<ExpandoObject>(jsonstring, new ExpandoObjectConverter());
var rootDict =(IDictionary<string, object>) root;
var recordDict = (IDictionary<string, object>) rootDict["record"];
var formValuesDict = (IDictionary<string, object>) recordDict ["form_values"];
formValuesDict.Remove("683b");

Creating dictionaries with pre-defined keys C#

I'm looking for a way to define a dictionary for reuse. ie. I can create the dictionary object without having to populate it with the values I want.
Here is what I have currently (note code not tested, just example)
public Dictionary<string, string> NewEntryDictionary()
{
Dictionary<string, string> dic = new Dictionary<string, string>();
// populate key value pair
foreach(string name in Enum.GetNames(typeof(Suits))
{
dic.Add(name, "");
}
return dic;
}
The end result should be a new dictionary object with a predefined set of keys.
But I want to avoid doing it this way.
It's not really clear whether you're concerned about the amount of code you've written, or the efficiency of it. From an efficiency perspective, it's fine - it's O(N), but that's hard to avoid if you're populating a dictionary with N entries.
You can definitely make the source code shorter though, using LINQ:
public Dictionary<string, string> NewEntryDictionary()
{
return Enum.GetNames(typeof(Suits)).ToDictionary(name => name, name => "");
}
That won't be any more efficient, of course... it's just shorter code.
If you do ONLY want to save values according to your enum, use
Dictionary<Suits,String> instead of Dictionary<String,String>
Everything else, Jon already said. Use LinQ for a bit more "fancy" look. But that does not do better performance

Convert KeyValuePair to anonymous type in a LINQ query

I have an IEnumerable<KeyValuePair<string,string>>, from which I would like, ideally, an anonymous object which has the keys as property names and the values as property values.
I've tried various selection expressions (none of which even compiled...) and an approach using ExpandoObject (see below), but without success. Is there a good way to do this? If possible, I'd like to avoid an extra explicit iteration over the collection (i.e. do it all with a LINQ statement of some sort).
This is what I've tried so far. I hope it also clarifies what I'm trying to do:
var kvps = getProps(); // returns IEnumerable<KeyValuePair<string,string>>
dynamic o = new ExpandoObject();
foreach (var kvp in kvps)
{
o.Add(kvp);
}
This is OK at compile time, but at runtime I get a YSOD stating 'System.Dynamic.ExpandoObject' does not contain a definition for 'Add' - I guess it works at compile time because o is dynamic, so the compiler can't know if a method .Add() has been added to it since it was instantiated. The odd thing is, that on the MSDN documenation page for ExpandoObject .Add() is listed as one of several "explicitly implemented interface methods".
It is not necessary for me to get this into a dynamic object - I just need to get something that has property names and values according to the keys and values of the key-value pairs.
Update: Well, this is embarrassing. Turns out this was something of an XY-problem too.
I'm trying to render this to JSON using the built-in features of ASP.NET MVC, by simply returning Json(data) in my controller. The answers all worked very well to do what I first asked, but when I pass this object as data I still don't get what I want:
// What I get:
[
{ Key: 'firstkey', Value: 'FirstValue' },
{ Key: 'secondKey', Value: 'secondValue' }
]
// What I want:
{ firstKey: 'FirstValue', secondKey: 'secondValue' }
Apparently, an ExpandoObject with the relevant properties added didn't cut it - it was cast to a dictionary before rendering...
You need to use the ExpandoObject as an IDictionary<string, object> while populating it:
var kvps = getProps(); // returns IEnumerable<KeyValuePair<string,string>>
IDictionary<string, object> o = new ExpandoObject();
foreach (var kvp in kvps)
{
// Or use Add(kvp.Key, kvp.Value), if you want
o[kvp.Key] = kvp.Value;
}
dynamic d = o;
// Now you can use the properties
ExpandoObject explicitly implements IDictionary<string, object> - so you need to cast it to one first:
var kvps = getProps();
dynamic o = new ExpandoObject();
var dict = o as IDictionary<string, object>;
foreach (var kvp in kvps)
{
dict.Add(kvp.Key, kvp.Value);
}
Now you can use o as you would expect:
var example = o.YourKey;
I'm trying to render this to JSON using the built-in features of ASP.NET MVC, by simply returning Json(data) in my controller.
Interesting.
To do that, you serialize a dictionary, not an ExpandoObject. MVC 3's JSON serializer already does that with a dictionary. All you have to do is convert your IEnumerable<KeyValuePair<string, object>> to a dictionary:
var kvps = getProps();
var dictionary = kvps.ToDictionary(k => k.Key, v => v.Value);
return Json(dictionary, JsonRequestBehavior.AllowGet); //take out allow get if you don't need it.
No dynamics required.
I think you have to cast your expando object to IDictionary and call Add(string, object)

Using LINQ to find all keys from one collection that are not in another?

I'm trying to locate all the keys in one Dictionary that are not in another Dictionary. Obviously, I can do this using a nested loop, but I'm trying to learn LINQ at the moment and I was wondering if I might use it to accomplish this task?
Here's what I have so far:
Dictionary<string, List<string>> DBtables = this.CollectTableListings();
var generic = from Dictionary<string,List<string>> tab
in DBtables
where !_tables.ContainsKey(???)
select tab;
Any idea what should go in place of the question marks (or perhaps instead of the entire where clause)?
You can do:
var resultKeys = DBTables.Keys.Except( _tables.Keys );
The Except() method is essentially the same as the minus operations in SQL - it returns all items from the first collection excluding those in the second. Since dictionaries expose their keys, you can compute their difference that way.
The Except() operator uses the default equality for the type, but there is also an overload which allows you to specify your own IEqualityComparer to override the semantics of how to compare values. In your example, you probably don't need that - but it's nice to know it there.
Dictionary<string, List<string>> dictOne = ...
Dictionary<string, List<string>> dictTwo = ...
var missingKeys = dictOne.Keys.Where(x => !dictTwo.ContainsKey(x));
Dictionary<string, List<string>> dictionary = this.CollectTableListings();
Dictionary<string, List<string>> otherDictionary = getOtherTable();
var keys = from key in dictionary.Keys
where !otherDictionary.Keys.Contains(key)
select key;
(But LBuskin's answer is much better)
have a look at the Except extension method. HTH.
If you wanted to use query syntax I would do something akin to below:
var keys = from d1 in dictionary1
select d1.Key;
var items = from d2 in dictionary2
where d2.Key in keys
select d2;
foreach(var item in items)
{
}

Categories