DynamicExpressionParser not working while concating two arrays - c#

I want to concat two array in LINQ dynamic parser.
I am sending dictionary input as below
object a= new object[]{1d,0d,1d};
object b = new object[]{};
var dict = new Dictionary<string, object>();
dict["KIStrike"] = a;
dict["NonKIStrike"] = b;
It throws {"')' or ',' expected"} exception, when I am executing below statement
var exp = "Enumerable.Concat(dict[\"KIStrike\"] as IEnumerable<object>, dict[\"NonKIStrike\"] as IEnumerable<object>)";
var param = Expression.Parameter(typeof(Dictionary<string, object>), "dict");
var lambda = DynamicExpressionParser.ParseLambda(new[] { param }, null, exp);
var result = lambda.Compile().DynamicInvoke(dict);
Whether my expression is incorrect. Please let me know if you have any solution. Thanks in advance!!!.

Related

Dynamic Linq Select from List<IDictionary<string, object>>

Consider I have this IQueryble database collection which is similar to this list List and linq:
var lstData = new List<IDictionary<string, object>>()
{
new Dictionary<string, object>() {
{ "Name" , "John"},
{ "Age", 20 },
{ "School", "Waterloo" }
},
new Dictionary<string, object>() {
{ "Name" , "Goli"},
{ "Age", 23 },
{ "School", "Mazandaran" }
},
};
var result = lstData.Select(x => new { Name= x["Name"], School= x["School"] });
However, I do not know the name of properties at compilation. How do I dynamically select specific columns in the Linq select at runtime? something like this:
var result= lstData .Select("Name,School");
It would be more efficient and more maintainable if you define a class that you could store your data for each person rather than storing it in an object which in that case it would need unnecessary boxing and unboxing every time you search through your list. having that said you could search in your data structure like this:
var result = lstData.Find(x => x["Name"] == (object)"John" && x["School"] == (object)"Waterloo");
var age = (int)result["Age"]; //20
Edit
If the goal is to get properties as an anonymous list without knowing the property names at compilation time it's not possible. the anonymous type in C# needs the property name first hand or at least property access when you define it. an alternative way to solve this problem would be to use another dictionary so you could map the original one into:
var summayList = lstData.Select(x => new Dictionary<string, object>()
{
{ "Name", x["Name"] },
{ "School", x["School"] }
});
var john = summayList.Single(x => x["Name"] == (object)"John");
if the number of properties is unknown in addition to their names, you could use a method that return a list of dictionaries given unknown number of property names:
public static IEnumerable<IDictionary<string, object>> GetProps(List<IDictionary<string, object>> list, params string[] props)
{
return list.Select(x => new Dictionary<string, object>(
props.Select(p => new KeyValuePair<string, object>(p, x[p]))));
}
Usage
var result = GetProps(lstData, "Name", "School");
var goli = result.Single(x => x["Name"] == (object)"Goli");
I have finished the code, try the following code.
Func<IDictionary<string, object>, dynamic> CreateDynamicFromDict(string fields)
{
// input parameter "x"
var xParameter = Expression.Parameter(typeof(IDictionary<string, object>), "x");
// output object
var result = Expression.Parameter(typeof(IDictionary<string, object>), "result");
var add = typeof(IDictionary<string, object>).GetMethod("Add");
var body = new List<Expression>();
//initial output object
body.Add(Expression.Assign(result, Expression.New(typeof(ExpandoObject))));
// set value "FieldN = x.FieldN"
var bindings = fields.Split(',').Select(o => o.Trim())
.Select(o =>
{
var key = Expression.Constant(o);
return Expression.Call(result, add, key, Expression.Property(xParameter, "Item", key));
});
body.AddRange(bindings);
// return value
body.Add(result);
var block = Expression.Block(new[] { result }, body);
var lambda = Expression.Lambda<Func<IDictionary<string, object>, dynamic>>(block, xParameter);
// compile to Func<IDictionary<string, object>, dynamic>
return lambda.Compile();
}
The usage is
var result = lstData.Select(CreateDynamicFromDict("Field1, Field2"));
References:
LINQ : Dynamic select
Expression tree create dictionary with property values for class
Expression Trees (C#)
Expression Tree

How to convert list to expandobject key in c#?

I have a function in which i am getting data(array of objects) from db and then adding those objects of array one by one into a lit of type ExpandoObject
public async Task<List<ExpandoObject>> GetGroupWithMaxTickets(){
List<ExpandoObject> topGroupsWithMaxTickets = new List<ExpandoObject>();
dynamic ticketDetails = new ExpandoObject();
var pipeline_tickets = new BsonDocument[]{
new BsonDocument("$match",
new BsonDocument
{
{ "nsp", "/sbtjapan.com" },
{ "datetime",
new BsonDocument
{
{ "$gte", "2019-12-03T00:00:34.417Z" },
{ "$lte", "2019-12-03T24:00:34.417Z" }
} }
}),
new BsonDocument("$group",
new BsonDocument
{
{ "_id", "$group" },
{ "totalTIckets",
new BsonDocument("$sum", 1) }
}),
new BsonDocument("$project",
new BsonDocument
{
{ "_id", 0 },
{ "group", "$_id" },
{ "totalTIckets", 1 }
}),
new BsonDocument("$sort",
new BsonDocument("totalTIckets", -1)),
new BsonDocument("$limit", 5)
};
var collection = await DbService.tickets.AggregateAsync<RawBsonDocument>(pipeline_tickets, new AggregateOptions {UseCursor = true, BatchSize = 500});
await collection.MoveNextAsync();
if(collection.Current.ToList().Count > 0){
// ticketDetails = JsonConvert.DeserializeObject(collection.Current.ToJson());
// ticketDetails.group = collection.Current.ToList()[0]["group"];
// ticketDetails.totalTickets = collection.Current.ToList()[0]["totalTIckets"];
Parallel.ForEach(collection.Current.ToList(), (ticket) => {
Console.WriteLine("Ticket----"+ticket);
dynamic groupWithTickets = new ExpandoObject();
groupWithTickets = ticket;
topGroupsWithMaxTickets.Add(groupWithTickets);
});
}
return topGroupsWithMaxTickets;
}
But it throws an error like this
System.AggregateException: One or more errors occurred. (The best overloaded method match for 'System.Collections.Generic.List<System.Dynamic.ExpandoObject>.Add(System.Dynamic.ExpandoObject)' has some invalid arguments)
I want that my function must return array of objects of type List<ExpandoObject>
How can i do this in c#?
Since you have changed the question, following is the answer that should resolve your matters.
How to NOT work with ExpandoObjects
I tested this on my system and got it to reproduce the same results as you are getting. Following is the failed try:
dynamic employee = new ExpandoObject();
List<ExpandoObject> listOfEmployees = new List<ExpandoObject>();
employee = "someStrangeString";
listOfEmployees.Add(employee); // ERROR !!!!
and just as expected, i get the following error on Add.
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
HResult=0x80131500
Message=The best overloaded method match for 'System.Collections.Generic.List.Add(System.Dynamic.ExpandoObject)' has some invalid arguments
Source=
StackTrace:
Corrected way of ExpandoObject use
Following is the method that will take care of the issues with Adding it to the list.
Parallel.ForEach(collection.Current.ToList(), (ticket) =>
{
Console.WriteLine("Ticket----" + ticket);
dynamic groupWithTickets = new ExpandoObject();
groupWithTickets.users = ticket; //<---- Assign ticket to users element.
topGroupsWithMaxTickets.Add(groupWithTickets);
});
What was done to fix it?
When you are working with ExpandoObjects, you have to think of dictionary type of a deal. When you declare ExpandoObject, you have to dynamically assign the value to an element (that you define).
Example from MS site: shows the proper use of ExpandoObject
dynamic employee, manager;
employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;
manager = new ExpandoObject();
manager.Name = "Allison Brown";
manager.Age = 42;
manager.TeamSize = 10;
Hopefully this resolves your issue.
It should be like this;
dynamic ticketDetails = new ExpandoObject();
ticketDetails.user = collection;
string json = Newtonsoft.Json.JsonConvert.SerializeObject(ticketDetails);
You can simply do this:
dynamic ticketDetails = new ExpandoObject();
ticketDetails = Json(new users = JsonConvert.DeserializeObject(collection.Current.ToJson()));
For testing purposes, i used an array arr that holds one of the elements. If you need that array to be part of ExtendoObject with first element being users, you can create a Json object and set the array as value to the "users" element.
dynamic ticketDetails = new ExpandoObject();
JArray arr = new JArray();
arr.Add(#"[{""name"": ""Alex"", ""age"": 21}]");
JObject o = new JObject();
o["users"] = arr.ToString();
ticketDetails = o;
// output: { "users" : [{"name" : "Alex", "age" : 21}]}

Linq exception on Provider.CreateQuery

Given this class:
class SomeClass
{
public int SomeValue { get; set; }
}
The following list:
var queryableData = new List<SomeClass>() {
new SomeClass{SomeValue=1 },
new SomeClass{SomeValue=2 },
new SomeClass{SomeValue=3 },
new SomeClass{SomeValue=4 },
new SomeClass{SomeValue=5 },
new SomeClass{SomeValue=6 },
new SomeClass{SomeValue=7 },
}.AsQueryable();
And this code where i try to dinamically query the list (note that the string query could contain anything such as Take,Select,OrderBy, etc).
var externals = new Dictionary<string, object>();
externals.Add("SomeClass", queryableData);
string query = "SomeClass.Where(o => o.SomeValue >= 3)"; // or any query
// here i use the code from System.Linq.Dynamic
var expression = DynamicExpression.Parse(typeof(IQueryable<SomeClass>), query, new[] { externals });
// result will have five values
var result = queryableData.Provider.CreateQuery<SomeClass>(expression);
// here i use the code from System.Linq.Dynamic.Core
var expression2 = DynamicExpressionParser.ParseLambda(typeof(IQueryable<SomeClass>), query, new[] { externals });
// will throw exception here: Argument expression is not valid
var result2 = queryableData.Provider.CreateQuery<SomeClass>(expression2).ToDynamicArray();
I want to know what am i doing wrong that the Provider.CreateQuery is thowing the "Argument expression is not valid" exception?
The difference with DynamicExpression.Parse (hence the cause of the problem) is that the DynamicExpressionParser.ParseLambda method returns LambdaExpression (basically Expression<Func<TResult>>) which is not a valid query expression.
But the Body of it is, so the simplest fix is to use
var result2 = queryableData.Provider.CreateQuery<SomeClass>(expression2.Body);
Alternatively you could use System.Linq.Dynamic.Core.Parser.ExpressionParser class directly:
var expression2 = new ExpressionParser(null, query, new[] { externals }, null)
.Parse(typeof(IQueryable<SomeClass>));
var result2 = queryableData.Provider.CreateQuery<SomeClass>(expression2);

Json.Net, how to create json dynamically by path?

I'm new to Json.net, so I'm not sure if I'm doing this the right / best way, but I can't figure this out:
I'm creating an output JObject:
JObject joOutput = new JObject();
Now, I have a list of paths to values:
a.b.c = 5
a.b.d = 7
a.c.q = 8
So I want to populate the joOutput object in the obvious way using that hierarchy.
I can't seem to find a way to set the value by path, obviously creating the path along the way if it doesn't exist.
I'm trying to split the path on the .'s and create the nodes, but I can't even get that far as the api is very confusing.
Can you point me in the right direction?
Thanks!
string a = #"{""b"": {""c"":5, ""d"":7}, ""c"": {""q"":8}}";
JObject joObject = JObject.Parse(a);
UPDATE:
var B = new JObject();
B.Add("c", 5);
B.Add("d", 7);
var C = new JObject();
C.Add("q", 8);
JObject A = new JObject();
A.Add("b", B);
A.Add("c", C);
UPDATE2, for completeness, here is Vladimir's method:
var B = new Dictionary<string, int> { ["c"] = 5, ["d"] = 7 };
var C = new Dictionary<string, int> { ["q"] = 5, ["8"] = 7 };
var A = new Dictionary<string, object> { ["b"] = B, ["c"] = C };
var AA = JObject.FromObject(A);
Try to solve your problem by using Dictionary with string key and object value.
Dictionary<string,object> myJsonObj = new Dictionary<string, object>;
Each element inside can be also same dictionary.

C# using ExpressionTree to map DataTable to List<T>

I have written a ToList(); extension Method to convert a DataTable to List. This just works under some circumstances but we have much old code which uses DataTables and sometimes it's needed. My Problem is that this method works with reflection what is ok but not that performant. I need about 1,2sek for 100.000 DataRows.
So i decided to build this with Expression Trees. At first i want to replace the Setter Call of Properties. Up to this time i could easily get the value:
var exactType = Nullable.GetUnderlyingType(propType) ?? propType;
var wert = Convert.ChangeType(zeile[spaltenname], exactType);
and set it:
propertyInfo.SetValue(tempObjekt, wert, null);
Now i searched StackOverflow and found this:
var zielExp = Expression.Parameter(typeof(T));
var wertExp = Expression.Parameter(propType);
var propertyExp = Expression.Property(zielExp, matchProp);
var zuweisungExp = Expression.Assign(propertyExp, wertExp);
var setter = Expression.Lambda<Action<T, int>>(zuweisungExp, zielExp, wertExp).Compile();
setter(tempObjekt, wert);
My big Problem is that the Lambda Action expects an integer. But i need this expecting the type of my Property. I have the Type of my Property via PropertyInfo. But can't get this to work. Thought i can easily make:
Action<T, object>
but this results in following excepion:
ArgumentException The ParameterExpression from Type "System.Int32"
cannot be used as Delegateparameter from Type "System.Object".
Someone out there knows a possible solution?
Instead of the generic Expression.Lambda method you can use this overload which takes a type:
public static LambdaExpression Lambda(
Type delegateType,
Expression body,
params ParameterExpression[] parameters
)
Then you can use the Type.MakeGenericType method to create the type for your action:
var actionType = typeof(Action<,>).MakeGenericType(typeof(T), proptype);
var setter = Expression.Lambda(actionType, zuweisungExp, zielExp, wertExp).Compile();
Edit following the comments regarding performance:
You can also just build the expression runtime to map the DataTable to your class of type T with a select, so there's only need to use reflection once, which should greatly improve performance. I wrote the following extension method to convert a DataTable to List<T> (note that this method will throw a runtime exception if you don't plan to map all datacolumns to a property in the class, so be sure to take care of that if that might happen):
public static class LocalExtensions
{
public static List<T> DataTableToList<T>(this DataTable table) where T : class
{
//Map the properties in a dictionary by name for easy access
var propertiesByName = typeof(T)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(p => p.Name);
var columnNames = table.Columns.Cast<DataColumn>().Select(dc => dc.ColumnName);
//The indexer property to access DataRow["columnName"] is called "Item"
var property = typeof(DataRow).GetProperties().First(p => p.Name == "Item"
&& p.GetIndexParameters().Length == 1
&& p.GetIndexParameters()[0].ParameterType == typeof(string));
var paramExpr = Expression.Parameter(typeof(DataRow), "r");
var newExpr = Expression.New(typeof(T));
//Create the expressions to map properties from your class to the corresponding
//value in the datarow. This will throw a runtime exception if your class
//doesn't contain properties for all columnnames!
var memberBindings = columnNames.Select(columnName =>
{
var pi = propertiesByName[columnName];
var indexExpr = Expression.MakeIndex(paramExpr, property,
new[] { Expression.Constant(columnName) });
//Datarow["columnName"] is of type object, cast to the right type
var convert = Expression.Convert(indexExpr, pi.PropertyType);
return Expression.Bind(pi, convert);
});
var initExpr = Expression.MemberInit(newExpr, memberBindings);
var func = Expression.Lambda<Func<DataRow, T>>(initExpr,paramExpr).Compile();
return table.Rows.Cast<DataRow>().Select(func).ToList();
}
}
Then I wrote a small testclass and some code which creates a datatable of 1,000,000 rows that get mapped to a list. Building the expression + converting to a list now only takes 486ms on my pc (granted it is a very small class of course):
class Test
{
public string TestString { get; set; }
public int TestInt { get; set; }
}
class Program
{
static void Main()
{
DataTable table = new DataTable();
table.Columns.Add(new DataColumn("TestString", typeof(string)));
table.Columns.Add(new DataColumn("TestInt", typeof(int)));
for(int i = 0; i < 1000000; i++)
{
var row = table.NewRow();
row["TestString"] = $"String number: {i}";
row["TestInt"] = i;
table.Rows.Add(row);
}
var stopwatch = Stopwatch.StartNew();
var myList = table.DataTableToList<Test>();
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
}
}
I think I've understood you correctly. I cannot translate your variables so I'm taking my best guess here based on what I'm seeing in your question:
For an Action<object,object> where the first parameter is the Entity itself and the second is the type of the property you can use
var instance = Expression.Parameter(typeof(object), "i");
var argument = Expression.Parameter(typeof(object), "a");
var convertObj = Expression.TypeAs(instance, propertyInfo.DeclaringType);
var convert = Expression.Convert(argument, propertyInfo.PropertyType);
var setterCall = Expression.Call(convertObj, propertyInfo.GetSetMethod(), convert);
var compiled = ((Expression<Action<object, object>>) Expression.Lambda(setterCall, instance, argument)).Compile();
If you know T (ie, the type of the Entity), you can do this instead:
var instance = Expression.Parameter(typeof(T), "i");
var argument = Expression.Parameter(typeof(object), "a");
var convert = Expression.Convert(argument, propertyInfo.PropertyType);
var setterCall = Expression.Call(instance , propertyInfo.GetSetMethod(), convert);
var compiled = ((Expression<Action<T, object>>) Expression.Lambda(setterCall, instance, argument)).Compile();
I comment here because I do not have the necessary reputation to comment on the response of #Alexander Derek
var memberBindings = columnNames.Select(columnName =>
{
var pi = propertiesByName[columnName];
var indexExpr = Expression.MakeIndex(paramExpr, property,
new[] { Expression.Constant(columnName) });
//Datarow["columnName"] is of type object, cast to the right type
var convert = Expression.Convert(indexExpr, pi.PropertyType);
return Expression.Bind(pi, convert);
});
in order to avoid runtime exception i added a try-catch and .where()
var memberBindings = columnNames.Select(columnName =>
{
try
{
var pi = propertiesByName[columnName];
var indexExpr = Expression.MakeIndex(paramExpr, property,
new[] { Expression.Constant(columnName) });
var convert = Expression.Convert(indexExpr, pi.PropertyType);
return Expression.Bind(pi, convert);
}
catch(Exception e)
{
return null;
}
});
var initExpr = Expression.MemberInit(newExpr, memberBindings.Where(obj => obj != null));

Categories

Resources