How to select specific fields from dynamic list using LINQ - c#

I am trying to get the some specific fields from dynamic object with is actually a list of any class, this class contains various fields out of those fields I want to select some specific fields using LINQ, The fields which I want to select is also passing by the user. Below is the code that I have tried using System.Linq.Dynamic.
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Linq.Dynamic;
using System.Collections;
private void Test_Click(object sender, EventArgs e)
{
List<RateInfo> lst = new List<RateInfo>();
lst.Add(new RateInfo() { id_country = "IND", id_state = 1, rate = 2.3f });
lst.Add(new RateInfo() { id_country = "IND", id_state = 2, rate = 1.1f });
lst.Add(new RateInfo() { id_country = "IND", id_state = 3, rate = 5.2f });
lst.Add(new RateInfo() { id_country = "IND", id_state = 4, rate = 6.5f });
GetDynamicData(lst, new List<string>() { "id_country", "id_state" });
}
private void GetDynamicData(dynamic list, List<string> fetchFields)
{
var data = ((IEnumerable)list).Cast<dynamic>()
.Select(r => new { r }).AsQueryable();
StringBuilder s = new StringBuilder();
//This is for test only.
//It works, the value of "id_state" and "id_state" getting appended
foreach (var item in data)
{
s.Append(item.r.id_state);
s.Append(",");
s.Append(item.r.id_country);
}
//-----------------------------------------------------
//Select the specific field data from dynamic list
StringBuilder fields = new StringBuilder();
fields.Append("new (");
foreach (var fld in fetchFields)
{
fields.Append("r." + fld);
fields.Append(",");
}
fields.Remove(fields.Length - 1, 1);
fields.Append(")");
//This does not work throws error
//"No property or field 'id_country' exists in type 'Object'"
IQueryable iq = data.Select(fields.ToString());
//For test only to check the value of selected fields
foreach (dynamic item in iq)
{
s.Append(item.id_state);
s.Append(",");
s.Append(item.id_country);
}
}

you can hughly simplify your GetDynamicData method both specifying explicit list type (GetDynamicData(IList<RateInfo> list, ...)) and leaving the list item type generic, in order to reuse the method; with this last approach in mind, you can rewrite the GetDynamicData as follows, obtaining the desired output:
private void GetDynamicData<T>(IEnumerable<T> list, List<string> fetchFields)
{
var fields = $"new ({string.Join(",", fetchFields)})";
var res = list.AsQueryable().Select(fields);
//For test only to check the value of selected fields
foreach (dynamic item in res) {
Console.WriteLine(item.id_state);
Console.WriteLine(item.id_country);
}
}
OUTPUT
1
IND
2
IND
3
IND
4
IND
EXPLANATION
I think the difference is that specifying explicitly the type (through generic T or through RateInfo) you force LINQ to know list items'type; if you use dynamic the IQueryable.ElementType of the IQuqryable instance has value System.Object, so the query fails with the error you've experienced.

You should try using generics:
private void GetDynamicData<T>(IEnumerable<T> list, List<string> fetchFields)
{
var data = list.AsQueryable();

Related

Initialize a List<T> with inline definition of <T>

You can always define a class like this:
public class item {
int id;
string name;
}
and then use it like this:
List<item> items = new List<item>();
Can we not do something like this:
var items = new List<{int id, string name}>();
Just a short way of initializing when underlying object definition is simple and predictable.
This is possible in JavaScript (I have seen examples in Angular).
Sorry if this is answered before, my quick search could not find an answer to this specific topic on Google or SO.
C# 7 introduces tuples, so you can do this:
var list = new List<(int id, string name)>();
list.Add((3, "Bob"));
var (id, name) = list[0];
var entry = list[0];
string s = $"{entry.name} has ID {entry.id}";
foreach (var (id, name) in list)
{
}
Before C# 7 you can use the old Tuple type, which is a bit more messy:
var list = new List<Tuple<int, string>>();
list.Add(Tuple.Create(3, "Bob"));
foreach (var item in list)
{
int id = item.Item1;
string name = item.Item2;
}
In c# 7.0 and higher you can use value tuples - the syntax is almost identical:
var items = new List<(int id, string name)>();
Also you can do it like this:
var list = new[] { new { Id = 1, Name = "name" } }.ToList();
list.Add(new { Id = 2, Name = "name2" });
foreach (var item in list)
{
int id = item.Id;
string name = item.Name;
}
References: Anonymous Types, Implicitly Typed Arrays, ToList Extension Method

How to get all the values from an SQL Query C#

I have used String Builder to generate a RAW SQL QUERY in C#.
List<string> columns = new List<string>();
columns.Add("id");
columns.Add("Temp");
StringBuilder SqlStatement = new StringBuilder();
SqlStatement.Append("Select ");
for (int i = 0; i < columns.Count; i++)
{
if (i == columns.Count - 1)
{
SqlStatement.Append(columns[i]);
}
else
{
SqlStatement.Append(columns[i]);
SqlStatement.Append(",");
}
}
SqlStatement.Append(" FROM graph_update");
var ctx = new graphDBContext();
var result = ctx.Database.SqlQuery<graphDBContext>(SqlStatement.ToString()).ToList();
This translates into SELECT id,Temp FROM graph_update
And the result gives me
id = 1, temp = 20
id = 2 temp = 30
How do I get all these values????
I'm too use to:
foreach(var item in result)
{
item.id = id;
item.temp = temp;
}
But it won't let me.
EDIT:
Sorry but I'm not sure what you mean. Here is my debugger
Try to use foreach like this if theres no error return
foreach(var v in result)
{
String v0 = v[0].ToString();
String v1 = v[1].ToString();
}
Assuming you have EF > 6, then the ctx.Database.SqlQuery, according to the method documentation:
Creates a raw SQL query that will return elements of the given generic type.
The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type.
With that in mind you can do something like this:
public class GraphUpdateResult
{
public int Id {get; set;}
public decimal Temp {get; set;}
}
Then in your current method:
var result = ctx.Database.SqlQuery<GraphUpdateResult>SqlStatement.ToString()).ToList();
foreach (var graphResult in result)
{
Console.WriteLine(graphResult.Id);
Console.WriteLine(graphResult.Temp);
}
You can add more columns to the GraphUpdateResult class for EF to bind to, even if in some queries you don't specify them in the select statement.
I hope this helps.
foreach(var item in result)
{
var id = item.id;
var temp = item.temp;
}
in your code above, you are trying to assign the values to the item, instead of retrieving.
You can use a ORM-Mapper like
https://stormy.codeplex.com/SourceControl/latest#Stormy.cs
It is a very light Mapper and you can look how it works.
It maps the reader Data to the Object data:
public class CatMapper : ISelectable<Cat>
{
public Cat ApplySelect(IDataReader reader)
{
return new Cat()
{
Name = reader["name"].ToString(),
Weight = (float)reader["weight"]
};
}
}

How to iterate through IEnumerable collection using for each or foreach?

I want to add one by one values but in for loop how can I iterate
through one by one values and add it inside dictionary.
IEnumerable<Customer> items = new Customer[]
{
new Customer { Name = "test1", Id = 111},
new Customer { Name = "test2", Id = 222}
};
I want to add { Name = "test1", Id = 111} when i=0
and want to add { Name = "test2", Id = 222} when i=1 n so on..
Right now i'm adding full collection in every key.(want to achieve this using foreach or forloop)
public async void Set(IEnumerable collection)
{
RedisDictionary<object,IEnumerable <T>> dictionary = new RedisDictionary>(Settings, typeof(T).Name);
// Add collection to dictionary;
for (int i = 0; i < collection.Count(); i++)
{
await dictionary.Set(new[] { new KeyValuePair<object,IEnumerable <T> ( i ,collection) });
}
}
If the count is need and the IEnumerable is to be maintained, then you can try this:
int count = 0;
var enumeratedCollection = collection.GetEnumerator();
while(enumeratedCollection.MoveNext())
{
count++;
await dictionary.Set(new[] { new KeyValuePair<object,T>( count,enumeratedCollection.Current) });
}
New version
var dictionary = items.Zip(Enumerable.Range(1, int.MaxValue - 1), (o, i) => new { Index = i, Customer = (object)o });
By the way, dictionary is a bad name for some variable.
I'm done using
string propertyName = "Id";
Type type = typeof(T);
var prop = type.GetProperty(propertyName);
foreach (var item in collection)
{
await dictionary.Set(new[] { new KeyValuePair<object, T>(prop.GetValue(item, null),item) });
}
So you want to a an item from the collection to the dictionary in the for loop?
If you cast your IEnumerable to a list or an array, you can easily access it via the index. For example like this:
Edit: Code at first created a list every time it looped, which should of course be avoided.
var list = collection.ToList(); //ToArray() also possible
for (int i = 0; i < list.Count(); i++)
{
dictionary.Add(i, list[i]);
}
I'm not 100% if that is what you need, though. More details to your question would be great.

Convert object to a queryable

I used a modified version of this answer: How to dynamically create a class in C#? to create a dynamic object that represents a typed class.
public static object CreateNewObject(string[] columnNames)
{
var myType = CompileResultType(columnNames);
return Activator.CreateInstance(myType) as IQueryable;
}
Then in the main app:
var obj = MyTypeBuilder.CreateNewObject(rs.ColumnNames);
I need to somehow convert that to an IQueryable so I can do some Linq calls off it, such as .where(), .select() ect. Naturally, I am not currently able to because my app doesn't know what is exactly in that object, or what that object is.
So what I need is:
var obj = MyTypeBuilder.CreateNewObject(rs.ColumnNames);
List<obj> aListICanFill = new List<obj>();
..
aListICanFill.where(x => x.Equals("")).take(3);
I've blindly tried different casts, and even failed to try an iterate through the object - and now I'm completley stuck.
Is there any way to do this?
http://msdn.microsoft.com/en-us/library/bb882637.aspx seems to be something I should hook onto.
What my object looks like:
If you can use List<dynamic> you can use Where and Select IEnumerable<T> extension methods like below. This does not work with IQueryable because those methods require an Expression which cannot be dynamic.
using System;
using System.Collections.Generic;
using System.Linq;
namespace DynamicListTest
{
internal class Program
{
private static void Main(string[] args)
{
var dynamicObjects = GetDynamicObjects().Cast<dynamic>().AsEnumerable();
var itemsToPrint = dynamicObjects
.Where(item => item.Age > 30);
foreach (var item in itemsToPrint)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
private static IQueryable GetDynamicObjects()
{
return new List<dynamic>()
{
new { Name = "A", Age = 10 },
new { Name = "B", Age = 20 },
new { Name = "C", Age = 30 },
new { Name = "D", Age = 40 },
new { Name = "E", Age = 50 },
}.AsQueryable();
}
}
}
This prints
{ Name = D, Age = 40 }
{ Name = E, Age = 50 }
check out linq to objects
http://msdn.microsoft.com/en-us/library/bb397919.aspx
Hopefully your object contains an array?
Could you give a sample of how you want to query it? And also what CompileResultType does?
var myType = CompileResultType(columnNames);
EDIT
For future reference - as suggested by Shane - OP is trying out - Dynamic Linq dynamiclinq.codeplex.com

select value from array at desired index using LINQ

i have an array which have 5 values from index 0 to 4. i want to store the values in my 5 model properties.
public IEnumerable<fields> ConvertTList(List<string[]> rows)
{
var tList = new List<fields>();
foreach (var item in rows)
{
var ListReading = new fields
{
//model properties names are:
// date, user,campaign,adgroup,changes
};
tList.Add(ListReading);
}
return (tList);
}
this is my code when foreach is executed item get 5 values. i want to store the values in model. how i can i store them using linq
Maybe you mean something like this:
public IEnumerable<fields> ConvertTList(List<string[]> rows)
{
return rows.Select(x => StringsToField(x));
}
fields StringsToField(string[] source)
{
return new fields
{
date = source[0],
user = source[1],
campaign = source[2],
adgroup = source[3],
changes = source[4],
};
}

Categories