SelectMany Expression Tree Expression.Call typeArguments - expression-trees

Well, I have a problem with creating expression tree for SelectMany.. especially at the typeArguments part..
So, I have a database with tables like below:
[Group] (one to many) [GroupDetail] (many to one) [Item] (one to many) [ItemDetail]
GroupDetail.group is a Group
GroupDetail.item is an Item
ItemDetail.item is an Item
Item.itemDetail is a collection of ItemDetail
Group.groupDetail is a collection of GroupDetail
so you can see that group detail is simply a many to many link for Group and Item
and (one to many) is a one to many relation..
For example, the data is like below:
Group, GroupDetail, Item, ItemDetail
------------------------------------
gr1, grDt1, ItemA, PartsAA
gr1, grDt1, ItemA, PartsAB
gr1, grDt2, ItemB, PartsBA
gr1, grDt2, ItemB, PartsBB
gr2, grDt3, ItemC, PartsCA
gr2, grDt4, ItemA, PartsAA
gr2, grDt4, ItemA, PartsAB
gr3, grDt4, ItemD, PartsDA
gr3, grDt5, ItemE, PartsEA
I want to select items and each of it's detail by a group search
and return it as a collection of some sort of view class..
Similar like this function below:
public IQueryable<ItemGroupDetailView> getViewQ(IQueryable<GroupDetail> myQ)
{
return myQ.SelectMany(
m => m.item.itemDetail,
(m, n) => new ItemGroupDetailView
{
groupName = m.group.name,
groupDetailCount = m.group.groupDetail.Count,
item = new ItemView
{
itemName = n.item.name,
itemDetailCount = n.item.itemDetail.Count
},
itemDetail = new ItemDetailView
{
itemDetailName = n.name
}
}
);
}
simply like that above BUT I want it to be a dynamic exp tree instead, so maybe I can just use it like:
Filter filter = new Filter("gr1","ItemA"); // just a filter
var myQ = getSearchQ(filters); // it gets all the where etc, everything is fine here..
var viewQ = getViewQ(myQ); // simply to convert the data to the view,.. where all the errors are
var finalQ = ApplyLimit(ApplyGrouping(ApplySorting(ApplySelect(myQ))); // paging, sorting, grouping, etc..
// run the select.. get the count etc..
now I want to make it dynamic but I seems to get it wrong on the SelectMany part
This is roughly how I do the SelectMany things:
step 1: I bind the property/field assignment.. it came from some sort of list-string-configuration-kinda-thing that map the assignment
PropertyInfo pInfo;
MemberExpression mExp;
// parse getproperty reflect etc...
List<MemberAssignment> memberAssginments = new List<MemberAssignment>();
memberAssginments.Add(Expression.Bind(pInfo, mExp);
step 2: then the usual member init
MemberInitExpression mie =
Expression.MemberInit(Expression.New
(typeof(ItemGroupDetailView)), memberAssginments);
so I get this:
new ItemGroupDetailView
{
groupName = m.group.name,
groupDetailCount = m.group.groupDetail.Count,
item = new ItemView
{
itemName = n.item.name,
itemDetailCount = n.item.itemDetail.Count
},
itemDetail = new ItemDetailView
{
itemDetailName = n.name
}
}
step 3: then get the expression collectionSelector & resultSelector
ParamterExpression m = Expression.Parameter(typeof(GroupDetail),"m");
ParamterExpression n = Expression.Parameter(typeof(ItemDetail),"n");
Expression<Func<GroupDetail, ItemDetail, ItemGroupDetailView>> exp2 =
Expression.Lambda<Func<GroupDetail, ItemDetail, ItemGroupDetailView>>
(mie, new ParameterExpression[] { m, n });
I think I get what I need, exp2 (resultSelector):
(m, n) => new ItemGroupDetailView
{
groupName = m.group.name,
groupDetailCount = m.group.groupDetail.Count,
item = new ItemView
{
itemName = n.item.name,
itemDetailCount = n.item.itemDetail.Count
},
itemDetail = new ItemDetailView
{
itemDetailName = n.name
}
}
and with similar way I get the other clause, exp1 (collectionSelector)
MemberExpression mEx = .... reflect get property/field etc..
Expression<Func<GroupDetail, IEnumerable<ItemDetail>>> exp1 =
Expression.Lambda<Func<GroupDetail, IEnumerable<ItemDetail>>>(mEx, m);
so I get this:
m => m.item.itemDetail
step 4: then get the selectMany MethodCallExpression itself
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(Expression<Func<GroupDetail, IEnumerable<ItemDetail>>>),
typeof(Expression<Func<GroupDetail, ItemDetail, ItemGroupDetailView>>)
},
new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
);
It doesnt work at all..
(No generic method 'SelectMany' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.)
so I think the main question here is:
How do I build an expression tree for such selectMany query
How do I build an expression query that has resultSelector & collectionSelector and multiple parameters..
and why does the code below works, but the Expression.Call always error..
myQ.SelectMany(exp1, exp2);
I guess I dont understand how SelectMany or Expression Tree works.. :(
but, I need this to be dynamic because the property/field assignment binding and source, selector and result is dynamic
public IQueryable<TView> getViewQ(IQueryable<T> myQ)
{
// some code..
}
EDIT 1 :
Switching the exp1 and exp2.. now exp1 is the collectionSelector and exp2 is the resultSelector..
EDIT 2 :
Furthermore I tried several things:
first, I change the type argument like what Mike said below, but the error still the same
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(ItemDetail),
typeof(ItemGroupDetailView)
},
new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
);
then I try some reflection this and that to check
System.Reflection.MethodInfo sminfo = null;
System.Reflection.MethodInfo sminfo2 = null;
IEnumerable<System.Reflection.MethodInfo> sminfos = typeof(Queryable)
.GetMethods(System.Reflection.BindingFlags.Static
| System.Reflection.BindingFlags.Public)
.Where(xxx => xxx.Name.Equals("SelectMany"));
foreach (System.Reflection.MethodInfo mi in sminfos)
{
if (mi.GetParameters().Count() == 3)
{
sminfo = mi;
}
}
/*
I ran this step by step to make sure that the method I get in sminfo is:
public static IQueryable<TResult> SelectMany<TSource, TCollection, TResult>(
this IQueryable<TSource> source,
Expression<Func<TSource, IEnumerable<TCollection>>> collectionSelector,
Expression<Func<TSource, TCollection, TResult>> resultSelector
);
*/
sminfo2 = sminfo.MakeGenericMethod(
new Type[] {
typeof(GroupDetail), typeof(ItemDetail), typeof(ItemGroupDetailView)
});
MethodCallExpression selectManyExp =
Expression.Call(sminfo2, new Expression[] { exp1, exp2 });
and I get different error:
(Incorrect number of arguments supplied for call to method ..)
and it tells me that the method required 3 parameters instead of 2, and the one I miss is the IQueryable<GroupDetail> source
so I get back to the Expression.Call and add the source parameter
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(ItemDetail),
typeof(ItemGroupDetailView)
},
new Expression[] { myQ.Expression, exp1, exp2 }
);
return (IQueryable<ItemGroupDetailView>)myQ.Provider.CreateQuery(selectManyExp);
and it works.. :D
Sorry for the messy and long post,.. my English is bad.. :(

It looks like you've conflated the type parameters with the formal parameters. I believe your type arguments should look like below:
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(ItemDetail),
typeof(ItemGroupDetailView)
},
new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
);

Related

Number of records in grid AX 2012

I tried to count num of rows in grid in runtime with this code
FormRun caller;
FormDataSource fds;
QueryRun queryRun;
int64 rows;
fds = caller.dataSource();
query = fds.query();
queryRun = new QueryRun(query);
rows = SysQuery::countTotal(queryRun); //this returns -1587322268
rows = SysQuery::countLoops(queryRun); //this returs 54057
The last line of code is closest to what i need because there are 54057 lines but if i add filters it still returns 54057.
I want logic to get the number rows that grid has in the moment of calling the method.
Your query has more than one datasource.
The best way to explain your observation is to look at the implementation of countTotal and countLoops.
public client server static Integer countTotal(QueryRun _queryRun)
{
container c = SysQuery::countPrim(_queryRun.pack(false));
return conpeek(c,1);
}
public client server static Integer countLoops(QueryRun _queryRun)
{
container c = SysQuery::countPrim(_queryRun.pack(false));
return conpeek(c,2);
}
private server static container countPrim(container _queryPack)
{
...
if (countQuery.dataSourceCount() == 1)
qbds.addSelectionField(fieldnum(Common,RecId),SelectionField::Count);
countQueryRun = new QueryRun(countQuery);
while (countQueryRun.next())
{
common = countQueryRun.get(countQuery.dataSourceNo(1).table());
counter += common.RecId;
loops++;
}
return [counter,loops];
}
If your datasource contains one datasource it adds count(RecId).
countTotal returns the number of records.
countLoops returns 1.
Pretty fast, as fast as the SQL allows.
If your datasource contains more than one datasource it does not add count(RecId).
countTotal returns the sum of recIds (makes no sense).
countLoops returns the number of records.
Also countLoops is slow if there are many records as they are counted one by one.
If you have two datasources and want a fast count, you are on your own:
fds = caller.dataSource();
queryRun = new QueryRun(fds.queryRun().query());
queryRun.query().dataSourceNo(2).joinMode(JoinMode::ExistsJoin);
queryRun.query().dataSourceNo(1).clearFields();
queryRun.query().dataSourceNo(1).addSelectionField(fieldnum(Common,RecId),SelectionField::Count);
queryRun.next();
rows = queryRun.getNo(1).RecId;
The reason your count did not respect the filters was because you used datasource.query() rather than datasource.queryRun().query(). The former is the static query, the latter is the dynamic query with user filters included.
Update, found some old code with a more general approach:
static int tableCount(QueryRun _qr)
{
QueryRun qr;
Query q = new Query(_qr.query());
int dsN = _qr.query().dataSourceCount();
int ds;
for (ds = 2; ds <= dsN; ++ds)
{
if (q.dataSourceNo(ds).joinMode() == JoinMode::OuterJoin)
q.dataSourceNo(ds).enabled(false);
else if (q.dataSourceNo(ds).joinMode() == JoinMode::InnerJoin)
{
q.dataSourceNo(ds).joinMode(JoinMode::ExistsJoin);
q.dataSourceNo(ds).fields().clearFieldList();
}
}
q.dataSourceNo(1).fields().clearFieldList();
q.dataSourceNo(1).addSelectionField(fieldNum(Common,RecId), SelectionField::Count);
qr = new QueryRun(q);
qr.next();
return any2int(qr.getNo(1).RecId);
}

How should I structure my data so it works with firebase?

I want to update some data in the forms of
wordBank = {
{word:"aprobi", translation:"to approve", count:2},
{word:"bati", translation:"to hit, to beat, to strike", count:1},
{word:"da", translation:"of", count:1}
}
the goal is to able to extract and display all the values of all the keys in each JSON object. How do I create this format on firebase? do I use .update? or something else?
currently I could only get firebase .update() to work with an array but it gives me data like this
wordBank = [
{word:"aprobi", translation:"to approve", count:2},
{word:"bati", translation:"to hit, to beat, to strike", count:1},
{word:"da", translation:"of", count:1}
];
where each word-object is an index in the array.
Here's how I construct my wordObjects:
function getWords() {
if (document.getElementsByClassName("vortarobobelo").length != 0){
var words;
words = document.getElementsByClassName("vortarobobelo")[0].children[0].children;
for (var i =0; i < words.length; i++) {
var localBank = {} //creating the local variable to store the word
var newWord = words[i].children[0].innerText; // getting the word from the DOM
var newTranslation = words[i].children[1].innerText; // getting the translation from the DOM
localBank.word = newWord;
localBank.translation = newTranslation;
localBank.count = 0 //assuming this is the first time the user has clicked on the word
console.log(localBank);
wordBank[localBank.word] = localBank;
fireBank.update(localBank);
}
}
}
If you want to store the items within an object, you need to pick keys to store them against.
You can't store unkeyed values inside an object in Javascript. This would result in a syntax error:
wordBank = {
{word:"aprobi", translation:"to approve", count:2},
{word:"bati", translation:"to hit, to beat, to strike", count:1},
{word:"da", translation:"of", count:1}
}
The other option is to store them in an array, in which case the keys will be automatically assigned as array indices. Just like your second example.
Maybe you want to store the word objects, using the word itself as a key?
wordBank = {
aprobi: {word:"aprobi", translation:"to approve", count:2},
bati: {word:"bati", translation:"to hit, to beat, to strike", count:1},
da: {word:"da", translation:"of", count:1}
}
This would be easy to do with Firebase. Let's say you have all of your word objects as a list.
var ref = new Firebase("your-firebase-url");
wordObjects.forEach(function(wordObject) {
ref.child(wordObject.word).set(wordObject);
});
Or you could create the object with Javascript, then add it to Firebase using .update.
var wordMap = {};
wordObjects.forEach(function(wordObject) {
wordMap[wordObject.word] = wordObject;
});
ref.update(wordMap);

Clone a List, Map or Set in Dart

Coming from a Java background: what is the recommended way to "clone" a Dart List, Map and Set?
Use of clone() in Java is tricky and questionable1,2. Effectively, clone() is a copy constructor and for that, the Dart List, Map and Set types each have a named constructor named .from() that perform a shallow copy; e.g. given these declarations
Map<String, int> numMoons, moreMoons;
numMoons = const <String,int>{ 'Mars' : 2, 'Jupiter' : 27 };
List<String> planets, morePlanets;
you can use .from() like this:
moreMoons = new Map<String,int>.from(numMoons)
..addAll({'Saturn' : 53 });
planets = new List<String>.from(numMoons.keys);
morePlanets = new List<String>.from(planets)
..add('Pluto');
Note that List.from() more generally accepts an iterator rather than just a List.
For sake of completeness, I should mention that the dart:html Node class defines a clone() method.
1 J. Bloch, "Effective Java" 2nd Ed., Item 11.
2 B. Venners, "Josh Bloch on Design: Copy Constructor versus Cloning", 2002. Referenced from here3. Quote from the article:
If you've read the item about cloning in my book, especially if you read between the lines, you will know that I think clone is deeply broken. ---J.Bloch
3 Dart Issue #6459, clone instance(object).
With the new version of dart cloning of a Map or List become quite easy.
You can try this method for making a deep clone of List and Map.
For List
List a = ['x','y', 'z'];
List b = [...a];
For Maps
Map mapA = {"a":"b"};
Map mapB = {...mapA};
For Sets
Set setA = {1,2,3,};
Set setB = {...setA};
I hope someone find this helpful.
If you are using dart > 2.3.0, You can use spread operator something like:
List<int> a = [1,2,3];
List<int> b = [...a]; // copy of a
For lists and sets, I typically use
List<String> clone = []..addAll(originalList);
The caveat, as #kzhdev mentions, is that addAll() and from()
[Do] not really make a clone. They add a reference in the new Map/List/Set.
That's usually ok with me, but I would keep it in mind.
For deep copy (clone), you can use :
Map<String, dynamic> src = {'a': 123, 'b': 456};
Map<String, dynamic> copy = json.decode(json.encode(src));
but there may be some concerns about the performance.
This solution should work:
List list1 = [1,2,3,4];
List list2 = list1.map((element)=>element).toList();
It's for a list but should work the same for a map etc, remember to add to list if its a list at the end
Map.from() only works for 1D map.
To copy multi dimensional map without reference in dart use following method
Map<keyType, valueType> copyDeepMap( Map<keyType, valueType> map )
{
Map<keyType, valueType> newMap = {};
map.forEach
(
(key, value)
{
newMap[key] =( value is Map ) ? copyDeepMap(value) : value ;
}
);
return newMap;
}
Method-1: Recommended
For cloning a multi-dimensional (nested) List or Map, use the
json.decode() and json.encode()
List newList = json.decode(json.encode(oldList));
Map newMap = json.decode(json.encode(oldList));
Method-2:
List newList = [...oldList];
Map newMap = {...oldMap}
Method-3:
List newList = List.from(oldList);
Map newMap = Map.from(oldMap);
Method-4:
List newList = List.of(oldList);
Map newMap = Map.of(oldMap);
Method-5:
List newList = List.unmodifiable(oldList);
Map newMap = Map.unmodifiable(oldMap);
For more References:
https://www.kindacode.com/article/how-to-clone-a-list-or-map-in-dart-and-flutter/
https://coflutter.com/dart-flutter-how-to-clone-copy-a-list/
Best solution for me:
List temp = {1,2,3,4}
List platforms = json.decode(json.encode(parent.platforms));
This was my solution. I hope it can help someone.
factory Product.deepCopy(Product productToCopy) => new Product(
productToCopy.id,
productToCopy.title,
productToCopy.description,
productToCopy.price,
productToCopy.imageUrl,
productToCopy.isFavorite,
);}
To copy Map<String, List> filtered;
var filteredNewCopy = filtered.map((key, value) => MapEntry(key, [...value]));
There is no 100% bullet proof way of making an exact isolated copy, but the answer from Manish Dhruw is pretty good. However, it will only work for Maps containing simple variable types and nested Maps.
To extend it to also work with other common collections, such as List and Set, and combinations of them, you could use something like the code below.
You don't really need the DeepCopyable class, but it would be useful if you want to easily make your own classes "deep-copyable" with these functions.
abstract class DeepCopyable{
T deepCopy<T>();
}
List<T> listDeepCopy<T>(List list){
List<T> newList = List<T>();
list.forEach((value) {
newList.add(
value is Map ? mapDeepCopy(value) :
value is List ? listDeepCopy(value) :
value is Set ? setDeepCopy(value) :
value is DeepCopyable ? value.deepCopy() :
value
);
});
return newList;
}
Set<T> setDeepCopy<T>(Set s){
Set<T> newSet = Set<T>();
s.forEach((value) {
newSet.add(
value is Map ? mapDeepCopy(value) :
value is List ? listDeepCopy(value) :
value is Set ? setDeepCopy(value) :
value is DeepCopyable ? value.deepCopy() :
value
);
});
return newSet;
}
Map<K,V> mapDeepCopy<K,V>(Map<K,V> map){
Map<K,V> newMap = Map<K,V>();
map.forEach((key, value){
newMap[key] =
value is Map ? mapDeepCopy(value) :
value is List ? listDeepCopy(value) :
value is Set ? setDeepCopy(value) :
value is DeepCopyable ? value.deepCopy() :
value;
});
return newMap;
}
As I mentioned, it's obviously still not 100% bullet proof - for example you will loose type information for nested collections.
List<int> a = [1,2,3];
List<int> b = a.toList(); // copy of a
Seems to work too
**Dart 2.15
This was my solution,hope it works for you
class Person {
String? name;
int? age;
Person(this.name, this.age);
factory Person.clone(Person source) {
return Person(source.name, source.age);
}
}
final personList = [
Person('Tom', 22),
Person('Jane', 25),
];
final yourCopy = personList.map((p) => Person.clone(p)).toList();
If you are working with dynamic typed data (aka sourced from JSON or built to encode as JSON) you can use this function to perform a deep copy:
cloneDeep(value) {
if (value is List<dynamic>) {
return value.map<dynamic>(
(item) => cloneDeep(item),
).toList();
} else if (value is Map) {
return value.map<String, dynamic>(
(key, item) => MapEntry<String, dynamic>(key, cloneDeep(item)));
}
return value;
}
final list = [[],[]];
final cloned = list.copyRange(0, list.length-1);
best solution is use of toMap() and fromMap() for classes.
class A {
String title;
A(Map<String, Object> map){ // fromMap() or fromJson()
title= map['title'];
}
Map<String, Object> toMap(){
final res = <String, Object>{};
res['title'] = title;
return res;
}
}
The given answer is good, but be aware of the generate constructor which is helpful if you want to "grow" a fixed length list, e.g.:
List<String> list = new List<String>(5);
int depth = 0; // a variable to track what index we're using
...
depth++;
if (list.length <= depth) {
list = new List<String>.generate(depth * 2,
(int index) => index < depth ? list[index] : null,
growable: false);
}

How to pre-select an option in a dropdown knockout js

I've looked at this other question, but can't get my select box to work correctly:
Binding initial/default value of dropdown (select) list
I've got the following Game object:
function Game(visitingTeamDetails, homeTeamDetails, game) {
if (arguments.length > 0) {
this.VisitingTeamDetails = visitingTeamDetails;
this.HomeTeamDetails = homeTeamDetails;
this.GameId = ko.observable(game.GameId);
this.HomeTeamName = ko.observable(game.HomeTeamName);
this.VisitingTeamName = ko.observable(game.VisitingTeamName);
this.SportTypeName = ko.observable(game.SportTypeName);
this.HomeAccountName = ko.observable(game.HomeAccountName);
this.VisitingAccountName = ko.observable(game.VisitingAccountName);
this.GameDateString = ko.observable(game.GameDateString);
this.GameTimeString = ko.observable(game.GameTimeString);
this.AvailableSportTypes = ko.observableArray(game.Sports);
this.sportTypeFunction = function () {
for (sportType in this.AvailableSportTypes()) {
if (this.AvailableSportTypes()[sportType].Name == this.SportTypeName()) {
return this.AvailableSportTypes()[sportType];
}
}
return null;
};
this.SportType = ko.observable(game.SportType);
}
}
SportType is an object with Name and SportTypeId.
I have the following template:
<td rowspan="3"><select data-bind="options: AvailableSportTypes, value: SportType, optionsText:'Name', optionsCaption: 'Choose...'" class="sportType"></select></td>
AvailableSportTypes is a list of SportType.
The list is coming in with the names of the SportTypes in the drop down list, but I can't make the initial selection be SportType. I wrote sportTypeFunction to show myself that the data was coming in correctly, and it would select the correct value, but changing my selection in the drop down would not update SportType.
I'm sure I'm doing something wrong. Anyone see it?
Thanks
When game.SportType gets passed in, it needs to be a reference to the an item in the game.AvailableSportTypes and not just an object that looks the same.
Basically two objects are not equal unless they are actually a reference to the same object.
var a = { name: "test" },
b = { name: "test" };
alert(a === b); //false
So, you would need to call your function to locate the correct object in the array and set it as the value of your observable.
Not that it is way better, but in KO 1.3 you can extend .fn of observables, observableArrays, and dependentObservables to add additional functionality.
Here is a sample: http://jsfiddle.net/rniemeyer/ZP79w

Can't use foreach on a anonymous type

I have the code below where I am trying to go through the child questions of my qestAires anonymous type. When I get to the foreach loop i get the error:
foreach statement cannot operate on
variables of type 'Question' because
'Question' does not contain a public
definition for 'GetEnumerator'
What do I need to do differently to get around this?
var questAires = (from qs in dc.Questionnaires
from q in dc.Questions.Where(t => t.QuestionnaireId == qs.QuestionnaireID)
from r in dc.Responses.Where(qr => qr.QuestionID == q.QuestionId).DefaultIfEmpty()
where qs.QuestionnaireID == QuestionnaireId
select new
{
qs.Description,
Questions = q,
Responses = r
}).Single();
foreach(var question in questAires.Questions)
{
}
questAires.Questions will only resolve to a single question, and you will get one questAires object for each question (which will cause .Single() to throw).
I guess you want something like this:
var questAires = (from qs in dc.Questionnaires
select new {
qs.Description,
Questions = from q in dc.Questions where q.QuestionnaireId == qs.QuestionnaireID
select new {
Question = q,
Response = (from r in dc.Responses where r.QuestionID == q.QuestionId select r).DefaultIfEmpty()
}
}).Single()
q actually resolves to a single item from the enumerable dc.Questions.Where(...), so yeah, you're only going to get a single item - not an enumerable - for Questions.

Resources