Is it possible to iterate on List<dynamic>? - c#

In the past few days I've been trying to find a way to iterate on a List<dynamic> without much success.
That's what I'm doing:
while (dr.Read())
{
dynamic e = new ExpandoObject();
var d = e as IDictionary<string, object>;
for (var i = 0; i < dr.FieldCount; i++)
d.Add(dr.GetName(i), DBNull.Value.Equals(dr[i]) ? null : dr[i]);
result.Add(e);
}
the above code is a method that returns an IEnumerable<dynamic> then in my controller I'm getting data back with:
dynamic irionErrorsExport = oracleDbManager.GetStrCtrlNDGWithErrors(tableName, queryParamsList, periodo, "", "", "");
and now I'm stuck since I need to iterate on irionErrorsExport and create a "concrete" object/s to use with EPPlus.
Can anyone please tell me if it is even possible and show a simple example?

Yes, you can iterate over dynamic object:
dynamic source = new List<int>() {1, 2, 3, 4, 5, 6};
foreach (var item in source)
{
Console.Write(item.ToString());
}
Prints 123456 into console.
However, it will cause runtime exception if iteration is not possible:
Consider following code:
dynamic source = 2;
foreach (var item in source)
{
Console.Write(item.ToString());
}
RuntimeBinderException is being thrown:
Cannot implicitly convert type 'int' to 'System.Collections.IEnumerable'
Edit: you should be aware of the differences between foreach on normal variables and dynamic. They are explained in another SO question: C# 4.0 'dynamic' and foreach statement

If you fill a DataTable like here, You can use Json.Net and get a concrete object easily
//Sample DataTable
DataTable dt = new DataTable();
dt.Columns.Add("IntCol");
dt.Columns.Add("StrCol");
dt.Rows.Add(new object[]{1,"1"});
dt.Rows.Add(new object[]{2,"2"});
var jsonstr = JsonConvert.SerializeObject(dt);
var list = JsonConvert.DeserializeObject<List<YourClass>>(jsonstr);
public class YourClass
{
public int IntCol { set; get; }
public string StrCol { set; get; }
}

while (dr.Read())
{
IDictionary<string, object> e = new ExpandoObject();
for (var i = 0; i < dr.FieldCount; i++)
e.Add(dr.GetName(i), DBNull.Value.Equals(dr[i]) ? null : dr[i]);
result.Add(e);
}
From the calling method you "cheat". You know that your dynamic collection is an ExpandoObject, so
foreach (IDictionary<string, object> row in result)
{
foreach (var kv in row)
{
Console.WriteLine("{0}: {1}", kv.Key, kv.Value);
}
}
In the end, it's better if your method simply returns a List<IDictionary<string, object>>, no dynamic necessary.
Reflection on dynamic types is hard. Unless you can use duck typing (duck typing is when you know that the object can Duck(), even if you don't know what exactly it's, so you can do dynamic x = something; x.Duck(); ), then it's only semi-hard. If you don't trust me on this, you can try reading How do I reflect over the members of dynamic object?

Related

How to Preform foreach in a IEnumerable object

I have an IEnumerable list with multi value in it, and I want to foreach over its values. I have this code
IEnumerable<object> Place = db.Places.Select(x => new { Id = x.Id, Nam1 = x.Name1, Name2 = x.Name2);
foreach(dynamic thisPlace in Place)
{
Response.Write (thisPlace.Id)
Response.Write (thisPlace.Name1)
Response.Write (thisPlace.Name2)
}
This code work fine and after it's done, it throws an error:
A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.Place_F6C785C74658C47ED4BFCF45D13FE7D754CCA2F688B6CBDD079244CE52B46291'."
Now the question is how to do foreach with IEnumerable the right way?
First of all, your code wont get executed. Because you do not have property named Name1 as you showed in the code.
Second, even if you have you should use var instead of using IEnumerable<object>
var Places = db.Places.Select(x => new { Id = x.Id, Nam1 = x.Name1, Name2 = x.Name2}).ToList();
foreach (var thisPlace in Places)
{
Response.Write(thisPlace.Id)
Response.Write(thisPlace.Name1)
Response.Write(thisPlace.Name2)
}

C# Linq, object definition does not contains a property

I need your help
I just wrote the following code
var anynomousObject = new { Amount = 10, weight = 20 };
List<object> ListOfAnynomous = new List<object> { anynomousObject };
var productQuery =
from prod in ListOfAnynomous
select new { prod.Amount, prod.weight }; // here it object on 'prod.Amount, prod.weight' that the object defenetion does not contains the "Amount" and "weight" properties
foreach (var v in productQuery)
{
Console.WriteLine(v.Amount, v.weight);
}
so please could you help me to solve this problem.
You need to make a class of your object definition, or using the dynamic keywork instead of boxing in object :
var anynomousObject = new { Amount = 10, weight = 20 };
List<dynamic> ListOfAnynomous = new List<dynamic> { anynomousObject };
var productQuery =
from prod in ListOfAnynomous
select new { prod.Amount, prod.weight };
foreach (var v in productQuery)
{
Console.WriteLine(v.Amount, v.weight);
}
this is because, when you box as object, the compiler doesn't know the definition of your anonymous var. Dynamic make it evaluate at runtime instead of compile-time.
The other option is to create a class or struct.
Your List<object> has a list of objects. The Linq query looks this list, and all it sees are regular objects.
Either use a class or a structure to store your objects, or use List<dynamic>

Convert NEST (Elasticsearch) Search Results to DataSet - C#

I am using NEST to search my Elasticsearch index:
var result = client.Search<MyObject>(s => s
.From(0)
.Size(10)
// Query here
);
This works and returns a Nest.SearchResponse object. The format returned by result.Hits.ToList() is List<Nest.IHit<MyObject>>().
How can I convert the results returned to a DataSet (or DataTable)?
Any help is appreciated.
You need to loop over your results
DataTable dt = new DataTable();
dt.Columns.Add("Field1", typeof(string));
dt.Columns.Add("Field2", typeof(string));
...
foreach (IHit<JObject> x in result.Hits)
{
dt.Rows.Add(
x.Fields.FieldValuesDictionary["Prop1"] as JArray,
x.Fields.FieldValuesDictionary["Prop2"] as JArray
...
);
}
Read
Retrieve data from elasticsearch results
How do you get search results returned in nest 1.x mapped to an object?
as well as
DocumentsWithMetaData
When you do a search with NEST 0.12, you'd get back a QueryResponse
with two ways to loop over your results. .Documents is an
IEnumerable and .DocumentsWithMetaData is and IEnumerable>
depending on your needs one of them might be easier to use.
Starting from NEST 1.0 .DocumentsWithMetaData is now called simply
.Hits.
http://nest.azurewebsites.net/breaking-changes.html
As proposed in this article, you can use an extension method to convert an IEnumerable<T> to a DataTable:
public static class IEnumerableExtensions
{
/*Converts IEnumerable To DataTable*/
public static DataTable ToDataTable<TSource>(this IEnumerable<TSource> data)
{
DataTable dataTable = new DataTable(typeof(TSource).Name);
PropertyInfo[] props = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in props)
{
dataTable.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ??
prop.PropertyType);
}
foreach (TSource item in data)
{
var values = new object[props.Length];
for (int i = 0; i < props.Length; i++)
{
values[i] = props[i].GetValue(item, null);
}
dataTable.Rows.Add(values);
}
return dataTable;
}
}
Then, after the search you could do something like this:
var dataTable = result.Documents.ToDataTable();
Where Documents is an IEnumerable<MyObject> that are the documents inside the hits that are returned. Another way to obtain those documents is:
var documents=result.Hits.Select(h => h.Source);

Serializing a list of dynamic objects to a CSV with ServiceStack.Text

All of my EF classes have a Projection() method that helps me choose what I want to project from the class to the SQL queries:
Example:
public static Expression<Func<Something, dynamic>> Projection()
{
return e => new
{
something = e.SomethingId,
name = e.Name,
requiredThingId = e.RequiredThingId,
requiredThing = new
{
requiredThingId = e.RequiredThing.RequiredThingId,
name = e.RequiredThing.Name,
...
},
optionalThingId = e.OptionalThingId,
optionalThing = e.OptionalThingId == null ? null : new
{
optionalThingId = e.OptionalThing.OptionalThingId,
name = e.OptionalThing.Name
}
...
};
}
I use these projection methods like this:
List<dynamic> projected = dbContext.Something.Select(Something.Projection()).ToList();
This way lets me reuse my projections all around my project.
I want to use ServiceStack.Text to serialize these lists to CSV.
I'm doing this:
string csvString = CsvSerializer.SerializeToCsv<dynamic>(Something.Select(Something.Projection()).ToList());
byte[] csvBytes = System.Text.Encoding.Unicode.GetBytes(csvString);
return File(csvBytes, "text/csv", "foo.csv");
But the result is an empty csv (csvString is full of "\r\n"'s and nothing more)
Questions:
Am I using correctly the SerializeToCsv() method?
Can I use <dynamic> in this library?
Suggestions for achieving my purpose?
For the example above the desired CSV would flatten all the properties, something like:
"somethingId";"name";"requiredThingId";"requiredThing.requiredThingId";"requiredThing.name";"optionalThingId";"optionalThing.optionalThingId";"optionalThing.name"
I will answer my own questions, but will not mark as accepted in hope of a new greater answer..
Am I using correctly the SerializeToCsv() method? Can I use dynamic
in this library?
Answer: Yes to both but ServiceStack.Text v4.0.36 is needed (which is available at MyGet, not Nuget)
Suggestions for achieving my purpose?
Answer: With ServiceStack.Text it is possible to serialize to CSV a List<dynamic>, but all the nested properties will be rendered as JSON and they will not be flattened out, eg:
List<dynamic> list = new List<dynamic>();
list.Add(new
{
name = "john",
pet = new
{
name = "doggy"
}
});
string csv = CsvSerializer.SerializeToCsv(list);
This list will be serialized to this csv:
name, pet
"john", { name = "doggy" }
And not to this csv, as I was expecting:
name, pet_name
"john", "doggy"
So... I finally ended up writing this code:
public class CsvHelper
{
public static string GetCSVString(List<dynamic> inputList)
{
var outputList = new List<Dictionary<string, object>>();
foreach (var item in inputList)
{
Dictionary<string, object> outputItem = new Dictionary<string, object>();
flatten(item, outputItem, "");
outputList.Add(outputItem);
}
List<string> headers = outputList.SelectMany(t => t.Keys).Distinct().ToList();
string csvString = ";" + string.Join(";", headers.ToArray()) + "\r\n";
foreach (var item in outputList)
{
foreach (string header in headers)
{
if (item.ContainsKey(header) && item[header] != null)
csvString = csvString + ";" + item[header].ToString();
else
csvString = csvString + ";";
}
csvString = csvString + "\r\n";
}
return csvString;
}
private static void flatten(dynamic item, Dictionary<string, object> outputItem, string prefix)
{
if (item == null)
return;
foreach (PropertyInfo propertyInfo in item.GetType().GetProperties())
{
if (!propertyInfo.PropertyType.Name.Contains("AnonymousType"))
outputItem.Add(prefix + "__" + propertyInfo.Name, propertyInfo.GetValue(item));
else
flatten(propertyInfo.GetValue(item), outputItem, (prefix.Equals("") ? propertyInfo.Name : prefix + "__" + propertyInfo.Name));
}
}
}
What this does is:
It flattens the List, so that all the properties of the objects in the list are primitives (eg: no nested properties)
It creates a CSV from that flattened list.
This algorithm is O(n*m), being
n: number of items in the list
m: number of properties inside each item (including nested properties)
Whilst the recommendation is to use clean POCO's for Serialization, you can serialize in-line anonymous types, e.g:
var somethings = dbContext.Something.Select(e => new {
something = e.SomethingId,
name = e.Name
});
somethings.ToCsv().Print();
Otherwise I've just added support for serializing IEnumerable<object> and IEnumerable<dynamic> in this commit.
This change is available from v4.0.36+ that's now available on MyGet.
I believe that part of what is going on is that the library is using properties in the object to determine what to serialize. I believe that a dynamic object does not construct properties like it's a POCO. If you don't setup a getter and setter on your object, it certainly won't work. Just for reference, I using version 4.5.6.0 of this library.

JSON Deserialization: How to get values out of a JSON array of objects

I've successfully deserialized this JSON string in C#, but can't extract the values from the objects nested in the array:
JavaScriptSerializer js = new JavaScriptSerializer();
string json =
{"key":"1234","status":"ok","members":
[{"id":7,"name":"Joe"},
{"id":2,"name":"Robert"},
{"id":18,"name":"Tim"}
]
}
var d = js.Deserialize < dynamic > (json);
string _key = d["key"]; // this works
Array _members = d["members"]; // this works, length = 3
But I'm having trouble extracting the values out of the objects by name, e.g, this isn't right, but essentially I want
_members[0]["name"] or, _members[0].name
I think the deserializer makes the objects inside the array dictionaries, but I think I'm clearing missing something...
I recommend using Json.NET to do what you're doing. The following code does what you want:
JObject jObject = JObject.Parse(json);
JToken memberName = jObject["members"].First["name"];
Console.WriteLine(memberName); // Joe
Via LINQ to Json.
Update:
var js = new JavaScriptSerializer();
var d = js.Deserialize<dynamic>(json);
Console.WriteLine(d["members"][0]["name"]); // Joe
Also works fine.
It's a bit late for an answer but I've been trying to figure this out and thought I should post somewhere what worked for me.
I wanted to use foreach so:
foreach (var member in json["members"])
{
Console.WriteLine(member["name"]);
}
and by the way, (for some reason like in my project) if you have nested arrays, e.g.
string json =
{"key":"1234","status":"ok",
"members":[
{"items"[
{"id":7,"name":"Joe"},
{"id":2,"name":"Robert"},
{"id":18,"name":"Tim"}
]}
]}
Then:
foreach (var member in json["members"])
{
foreach (var item in member["items"])
{
Console.WriteLine(item["name"]);
}
}
You were quite close in syntax. The key here is that d["members"] is of type Object[] / object[]. Instead of Array, you can use dynamic[] and everything works just fine.
Also note that even this declaration isn't explicitly necessary, as shown in DPeden's updated sample.
Here is the code for your updated snippet (I used a console app to test):
JavaScriptSerializer js = new JavaScriptSerializer();
dynamic d = js.Deserialize<dynamic>(json);
string key = d["key"];
string status = d["status"];
dynamic[] members = d["members"];
Console.WriteLine("key = {0}", key);
Console.WriteLine("status = {0}", status);
Console.WriteLine("members.length = {0}", members.Length);
Console.WriteLine("members type name = {0}", members.GetType().Name);
Console.WriteLine("d[\"members\"] type name = {0}", d["members"].GetType().Name);
And here is additional code showing array and member access.
Console.WriteLine("--");
for (int i = 0; i < members.Length; i++)
{
Console.WriteLine("members[{0}][\"id\"] = {1}", i, members[i]["id"]);
Console.WriteLine("members[{0}][\"name\"] = {1}", i, members[i]["name"]);
}
Console.WriteLine("--");
Console.WriteLine("{0}", d["members"][0]["id"]);
Console.WriteLine("{0}", d["members"][0]["name"]);
Console.ReadKey();

Categories