How to convert DapperRow to Dictionary<string,string> - c#

I've a method that returns IEnumerable with Dapper Row.
But I'm trying to access the data without typecasting it to a particular class and I'm getting null value.

Assuming that you are connecting to an SQL database
public List<IDictionary<string, object>> DapperSelect(string connectionString, string query, object parameters)
{
using (var connection = new SqlConnection(connectionString))
{
var result = connection.Query(query, parameters).ToList();
return result.Select(x => (IDictionary<string, object>)x).ToList();
}
}
I don't think that you should be converting your result to IDictionary<string, string> I don't think that has the desired effect you want, not every item in the dictionary is going to be a string, it could be bool, int, double, etc...
But if you insist, You could try to do something like
result.Select(x => ((IDictionary<string, object>)x).ToDictionary(ks => ks.Key, vs => vs.ToString())).ToList();
but I don't recommend it.
Better than all of that is that with dapper you can always strongly type the result returned from SQL, so instead of
connection.Query(query, parameters).ToList();
you would write
connection.Query<YOURTYPE>(query, parameters).ToList();

Something like this:
var foo = db.Query(
"MySp",
new { parameters },
commandType: CommandType.StoredProcedure)
.ToDictionary(
row => (int) row.Id,
row => (string) row.Name);
With row. being the names of the columns and foo being of type Dictionary .

Related

Dynamically change Linq Select

in this piece of code:
protected async Task<IEnumerable<KeyValuePair<int, string>>> GetListAsync(string tableName, string key, string value, string orderBy = "Id")
{
var query = $"SELECT {value},{key} FROM {tableName} ORDER BY {orderBy}";
var result = await QueryAsync<dynamic>(query);
return result.Select(x => new KeyValuePair<int, string>(x.Id, x.RoleName));
}
I'm trying to Create a dynamic generic method to handle getting list of any of my tables
the problem is I cannot recognize how to send key and value overloads instead of x.Id, x.RoleName
Try using column aliases:
protected async Task<IEnumerable<KeyValuePair<int, string>>> GetListAsync(string tableName, string key, string value, string orderBy = "Id")
{
var query = $"SELECT [{key}] as [KEY] , [{value}] as [VALUE] FROM [{tableName}] ORDER BY [{orderBy}]";
var result = await QueryAsync<dynamic>(query);
return result.Select(x => new KeyValuePair<int, string>(x.KEY, x.VALUE));
}
Update: I added square brackets everywhere to make it work. Not only is KEY a reserved word, the column names that are passed in might also be keywords, or they could contain spaces/special characters. This will make it work for almost any case (as long as you don't have square brackets inside column names).
It's hard to retrieve properties by name from dynamic, especially if it is implementing IDynamicMetaObjectProvider
So, hack is to use serialization to JSON, and access it's properies by name then.
Return statement should look like this (used Newtonsoft JSON here):
return result.Select(x => {
var jo = (JObject)JToken.FromObject(d);
return new KeyValuePair<int, string>(jo[key], jo[value]);
});

Show only columns from an string array

I have a Table in my Entities with 370 columns ! Furthermore i have a string Array which is not known before runtime (comes from a website).
e.g.:
string [] columns = {"column1", "column2", "column3"}
How can i fire a linq to my entities which gives me only the result with the giving columns?
I searched for hours, but don`t suceed till now - any suggestions?
This is not something that you can do with Linq-to-Entities. You need to be able to declare the columns in your code.
A better approach would be to create the query in Sql using the column names in your array, and use something like Dapper to map the results to your objects.
You could instantiate an ExpandoObject in your Delegate and use Reflection to get the columns specified in the incoming array.
The following:
List<IDictionary<String, Object>> List = Context.Row_Type.Select(delegate(Row_Type Row) {
IDictionary<String, Object> Out = new ExpandoObject() as IDictionary<String, Object>;
PropertyInfo[] PIs = Row.GetType().GetProperties();
foreach(PropertyInfo PI in PIs) {
if(PI.GetIndexParameters().Length == 0) {
Out.Add(PI.Name, PI.GetValue(Row));
}
}
return Out;
}).ToList();
Will return a List < IDictionary< String, Object>> with all the properties, to return the desired columns just discriminate by PI.Name:
if(PI.Name == "Desired column"){ // Or Array index
// Add to Out:
Out.Add( PI.Name, PI.GetValue(Row) )
}
I'm guessing you don't really want to return a class with unknown column names. Would a dictionary of column names and values work for you? Your query would still have to retrieve all of the columns, but you can only return the ones you care about.
string [] columns = {"column1", "column2", "column3"}
var entity = GetEntity();
var dictionary = columns.ToDictionary(c => c, c => entity.GetType().GetProperty(c).GetValue(entity));
Or, if you have a collection...
var entities = GetEntities();
var results = entities
.Select(e => columns.ToDictionary(c => c, c => e.GetType().GetProperty(c).GetValue(e)));

Returning a Dictionary<string, string> from a linq query

I have a table with 2 columns defined as varchar(50): Column1 and Column2. I want to return a dictionary of <string, string> where each row is in the dictionary and where Column1 is the key and Column2 is the value. This is what I have:
public Dictionary<string, string> LoadAllTheDataFromDB()
{
using (MyDC TheDC = new MyDC())
{
return (from c in TheTable
select new Dictionary<string, string>()
{
//stuck here
}).FirstOrDefault();
}
}
How do I make it that the dictionary is filled?
Try this:
var dict = TheTable.Select( t => new { t.Col1, t.Col2} )
.ToDictionary( t => t.Col1, t => t);
Remember in select lambda you will perform projection and create some anonymous object. Then in ToDictionary you will pass two parameters: First Parameter is a lambda to specify the key; in code above we are choosing Col1 to be the key. Second parameter is a lambda to specify the value; in code above we are choosing the object itself to be the value.
If you want the value to be an anonymous type, change the 2nd lambda like this:
ToDictionary( t => t.Col1, t => new { t.Col2 });
If you want the value to be a type you have defined, change the 2nd lambda like this:
ToDictionary( t => t.Col1, t => new YourType { Prop1 = t.Col2 });
Since you just need value of one first row why not to do that first:
var row = TheTable.FirstOrDefault();
And than just construct that dictionary if you got the result:
return row == null ? null :
new Dictionary<string,string>{ {row.Column1, row.Column2 } };

Dapper: How to read into list of Dictionary from query?

Dapper provides lots of ways mapping data into list of dynamic objects. However in some case I'd like to read data to list of Dictionary.
The SQL may looks like:
"SELECT * FROM tb_User"
As tb_User may change outside, I don't know what columns will return in result. So I can write some code like this:
var listOfDict = conn.QueryAsDictionary(sql);
foreach (var dict in listOfDict) {
if (dict.Contains("anyColumn")) {
// do right thing...
}
}
Is there any built-in methods for Dapper to do this conversion?
You can cast each row as IDictionary:
var row = (IDictionary<string, object>)conn.Query("select foo = 1, bar = 'bar'").First();
Assert.That(row["foo"], Is.EqualTo(1));
Assert.That(row["bar"], Is.EqualTo("bar"));
You could use the Cast extension method from System.Linq
IEnumerable<IDictionary<string, object>> rows;
rows = connection.Query(sqlRequest).Cast<IDictionary<string, object>>();
foreach (var row in rows)
{
var columnValue = row['columnName']; // returns the value of the column name
}
You can just assign aliases to your query so that it matches the Key and Value properties of a KeyValuePair and then use the .ToDictionary method like this:
var dict = db.Query<KeyValuePair<string, int>>(#"
SELECT COMMUNITY_TYPE As Key, COUNT(*) AS Value
FROM SNCOMM.COMMUNITY
GROUP BY COMMUNITY_TYPE")
.ToDictionary(x => x.Key, x => x.Value);
Now you have a Dictionary<string, int> without any manual converting.

Pass Func<> to Select

I'm starting with this:
query
.Take(20)
.Select(item => new
{
id = item.SomeField,
value = item.AnotherField
})
.AsEnumerable()
.ToDictionary(item => item.id, item => item.value);
Now, I want to reuse everything except SomeField and AnotherField.
public static Dictionary<int, string> ReusableMethod<T>(
this IQueryable<T> query,
Func<T, int> key,
Func<T, string> value)
{
return query
.Take(20)
.Select(item => new
{
id = key(item),
value = value(item)
})
.AsEnumerable()
.ToDictionary(item => item.id, item => item.value);
}
query.ReusableMethod(item => item.SomeField, item => item.AnotherField);
This works, but the DB query selects more data than required, so I guess that means ReusableMethod is using linq-to-objects.
Is it possible to do this while only selecting the required data? I'll add that Func<> is still part magic for me, so I might be missing something obvious.
Clarification to avoid confusion: the Take(20) is fine, the Select() isn't.
Wrap your funcs with Expression and remove the AsEnumerable call.
public static Dictionary<int, string> ReusableMethod<T>(
this IQueryable<T> query,
Expression<Func<T, int>> key,
Expression<Func<T, string>> value)
An alternative would be to just return the whole row then. No need for Expression in this case.
return query
.Take(20)
.ToDictionary(key, value);
Recently I had the same problem and here is what I did:
You have some DbEntity (generated by LINQ to EF,SQL), but you want to query only some fields (I did this to save network bandwidth). You have to create class derived from DbEntity, beacuse you cant create anonyous types in Expression trees and you can not create new instance of DbEntity in select statement. (No need to add any fields, properties etc.)
public class LocalEntity : DbEntity {}
You need to define a method to generate your select expression tree. It should look like this. This will generate expression tree similar to this: .Select(db => new LocalEntity() { Property1 = db.Property1, Proeprty2 = db.Property2})
protected Expression<Func<DbEntity, LocalEntity>> getSelectExpression()
{
ParameterExpression paramExpr = Expression.Parameter(typeof(DbEntity), "dbRecord");
var selectLambda = Expression.Lambda<Func<DbEntity, LocalEntity>>(
Expression.MemberInit(
Expression.New(typeof(LocalEntity)),
Expression.Bind(typeof(LocalEntity).GetProperty("DbEntityFieldName"), Expression.Property(paramExpr, "DbEntityFieldName"),
....
))
),
paramExpr);
return selectLambda;
}
Use it like this:
query.Select(getSelectExpression()).ToDictionary();
Consider this more as pseudo-code than C# code, as I had to simplify it a lot and I canĀ“t test it, but if oyu make it work, it will transfer from DB only fields you define in getSelectedExpression, not the whole row.

Categories

Resources