I have the below code which works perfectly fine:
return from fa in [...]
where fa.Flows.Any()
from f in fa.Flows
select new Flow(f.Id, f.DealingDate, f.NetCashUsd, fa.Account);
As you can see, I need Account from fa deeper down in the second select.
However I'm required to use ".Selects" instead, and this (equivalent I thought) does not work:
return [...]
.Where(fa => fa.Flows.Any())
.SelectMany(fa => fa.Flows)
.Select(f => new Flow(f.Id, f.DealingDate, f.NetCashUsd, fa.Account));
The issue here is obvious, the second .Select doesn't "know" the fa stuff anymore so can't get to Account.
(As you can probably tell by now, an "fa" has one Account and multiple Flows, and I want to turn them into "Flow"s which all have the Account assigned to them as well.)
How can I solve this using only the "." statements? I looked into different GroupBys as well but couldn't make them work either.
Thank you!
An overload of SelectMany uses an extra argument (result selector) in which you can define the returned objects. In this result selector you have access to both source item and sub item:
[....]
.SelectMany(fa => fa.Flows, (fa,f)=> new Flow(f.Id, f.DealingDate, f.NetCashUsd, fa.Account));
The equivalent would be to create an intermediate anonymous type to hold both objects.
return [...]
.SelectMany(fa => fa.Flows.Select(f => new { f, fa}))
.Select(x => new Flow(x.f.Id, x.f.DealingDate, x.f.NetCashUsd, x.fa.Account));
Also you don't need the Where since an empty Flows will just result in no selected items.
Related
var query = client.Cypher
.Match("(n1)-[r]-[n2]")
.Where("n1.NodeId={startNodeId} and n2.NodeId={endNodeId}")
.WithParam("startNodeId",startNodeId)
.withParam("endNodeId",endNodeId)
.return((r,n1,n2)=>new {Edge=r.As<Node<string>>(),Type=r.Type()});
By this way,I can only get the relationship's Label and Properties.But I also want to get the relationship's startNodeId and endNodeId.
By the way,I can get the relationship's startNodeId and endNodeId by using REST API.
How can anyone help me?
Z.Tom
It depends on what you're meaning by startNodeId - if you mean the Neo4j Ids, then you'll be wanting to use the Relationship<T> class:
client.Cypher
.Match("(n1)-[r]-(n2)")
//Your where stuff here.
.Return(r => r.As<RelationshipInstance<string>());
The T should be a type you've put on the relationship, but if you haven't got that, just use object, you will need to use the Relationship<T> type, you can't use the non-generic version.
If the startNodeId is a property on a type, then you either do r => r.As<YourType>() or r => r.As<Relationship<YourType>>().
As a side note, you can use Neo4jClients ability to parse parameters and save some code (also make it more compile safe) with respect to your Where clauses.
I would write your code as:
var query = client.Cypher
.Match("(n1)-[r]-[n2]")
.Where((NodeType n1) => n1.NodeId == startNodeId)
.AndWhere((NodeType n2) => n2.NodeId == endNodeId)
.Return(r => new { Edge = r.As<RelationshipInstance<object>>(), Type = r.Type()});
which auto parses the parameters.
If you know the classes which you want to cast to you can use:
var results= client.Cypher
.Match("(n1)-[r]-[n2]")
.Where("n1.NodeId={startNodeId} and n2.NodeId={endNodeId}")
.WithParam("startNodeId",startNodeId)
.withParam("endNodeId",endNodeId)
.Return((n1, r1 n2) => new {Edge=r.As<Node<string>>(),Type=r.Type()}, Class1= n1.As<Class1>(), Class2= n2.As<Class2>()})
.Results;
Then you can iterate through the results. If only one result is expected its first item in the 'results' array
I have a list of objects that I want to reload their data.
Like always, I have several options. I wanted just to select these items but encountered this "Additional information": Unable to create a constant value of type 'Item'. Only primitive types or enumeration types are supported in this context.
// (System.Collections.Generic.List<Item> selectedItems)
System.Collections.Generic.List<Item> items;
var q = from i in db.Items
where selectedItems.Any(s => s.Id == i.Id)
select i;
items = q.ToList()
the following yields the same, as expected...
var q = db.Items.Where(i => selectedItems.Any(si => i.Id == si.Id));
items = q.ToList();
I could have reattached each of the objects and call the reload, but then I would have(or not, but I don't know how) to run across the db lot of times to reload their Navigation Properties.
The only "fine" solution I've found until now is selecting the Id's of selectedItems and then running with it like follows:
int[] itemIds = selectedItems.Select(i => i.Id).ToArray();
var q = db.Items.Where(i => itemIds.Any(iId => i.Id == iId)); //Of course `Contains` could be used instead of `Any` here, since `itemIds` is a simple array of integers
items = q.ToList();
But is it a necessity or there is a more straight forward, neat or proper way to accomplish this?
But is it a necessity or there is a more straight forward, neat or proper way to accomplish this?
Not that I can think of. EF will try and turn your where clause into SQL (which is not as easy as you'd think). When it parses the expression and encounters a call to Any on a collection of non-primitive types, it does not know how to generically convert that list into a list of values to put in an into an in clause and gives you the error you quoted.
When the source collection is a collection of primitive or enumeration types, it can turn the source collection into a list of values and create an in clause. Contains does the same thing (it is also shorter is closer to the intent IMHO):
var q = db.Items.Where(i => itemIds.Contains(i.Id));
I have a list of a custom type called Holding. I am trying to use LNIQ to query certain values from this list. I would a new list of type Holding returned.
Below is the line I currently have which does not work.
List<Holding> hlds = _holdingList
.Where(hld => hld.IdSedol == lookThroList[i].Sedol)
.GroupBy(hld => new Holding())
.ToList();
By changing the line above to var hlds = does work and produce the correct result. Why can I not cast or do something to make the first line work?
Also the line below I'm not entirely sure about. The Holding object that is returned will that reference the holding object in my original list or is it a new object?
GroupBy(hld => new Holding())
Update
The reason I was using GroupBy (wrongly by the looks of it) was because I didn't want multiple fund codes that are the same, just wanted where the fund codes are different. However as I think about it I do not need to group by.
It sounds like this is all you need:
List<Holding> hlds = _holdingList
.Where(hld => hld.IdSedol == lookThroList[i].Sedol)
.ToList();
GroupBy() will never produce a collection of Holdings.
In your code, GroupBy() will produce an IEnumerable<IGrouping<Holding, Holding>>.
If you want to filter out duplicates, you can do this (assuming the Holding class has a FundCode property or something similar):
List<Holding> hlds = _holdingList
.Where(hld => hld.IdSedol == lookThroList[i].Sedol)
.GroupBy(hld => hld.FundCode)
.Select(g => g.First())
.ToList();
Linq also has a Distinct() method, but that usually requires defining an IEqualityComparer and that might be overkill in this case.
I have tried This answer, This one and this one to merge two iqueryables. But I always receive the following error:
The type 'Estudio' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.
I'm mapping from two different but similar Entity Framework Entities (EXAMEN and EXPLORACION) to my domain entity Estudio, with the following code.
IQueryable<Estudio> listExamen = context.Set<EXAMEN>().Project().To<Estudio>();
IQueryable<Estudio> listExploracion = context.Set<EXPLORACION>().Project().To<Estudio>();
var listCombined = listExamen.Concat(listExploracion);
Is there anyway of generate a IQueryable (not enumerable) with the merging of both list? If AsEnumerable() is used, then the following filters (Order, Take, etc) are executed on memory. So I need to merge the list but still be able to apply filter to the merged list wihtout execute the queries.
//This will force the next condition is executed on memory
var listCombined = listExamen.AsEnumerable().Concat(listExploracion);
Is that possible?
I would try to select your data into an anonymous type in your linq query, perform the union, and add your criteria.
var listExamen = context.Examen
.Select(x => new { x.Prop1, x.Prop2, ... }); // Add properties
var listExploracion = context.Exploraction
.Select(x => new { x.Prop1, x.Prop2, ... }); // Add identical properties
var listCombined = listExamen.Concat(listExploracion);
var whereAdded = listCombines
.Where(x => x.Prop1 == someValue);
var result = whereAdded
.Skip(skipCount)
.Take(takeCount)
.ToList();
Note: I have no idea if you can use Common Table Expressions (the SQL necessity for skip/take) in combination with a Union-query
Note: I've changed the methods used to create the expressions, since I do not know your methods (Project, To)
So I think the solution is not to cast to a specific type, but to an anonymous type, since that probably can be translated to SQL.
Warning: didn't test it
My solution was to revise my mapping code. Instead of using individual property-based mappers, I had to project the entire entity at once, making sure that all of the properties were given in the same order.
So, instead of the ForMember syntax:
Mapper.CreateMap<Client, PersonResult>()
.ForMember(p => p.Name, cfg => cfg.MapFrom(c => c.Person.FirstName + " " + c.Person.LastName))
...
I used the ProjectUsing syntax:
Mapper.CreateMap<Client, PersonResult>()
.ProjectUsing(c => new PersonResult()
{
Name = c.Person.FirstName + " " + c.Person.LastName
...
});
This must be because of the way AutoMapper constructs its projections.
One way to work around this is to add dummy types:
class Estudio<T> : Estudio { }
And new mapping:
Mapper.CreateMap<Estudio , Estudio>();
Mapper.CreateMap<EXAMEN , Estudio<EXAMEN>>();
Mapper.CreateMap<EXPLORACION, Estudio<EXPLORACION>>();
One caveat is that all fields in Estudio need some value in mapping.
You can't use ignore. Returning 0 or "" is fine.
Now we can do:
var a = context.Set<EXAMEN>().ProjectTo<Estudio<EXAMEN>>();
var b = context.Set<EXPLORACION>().ProjectTo<Estudio<EXPLORACION>>();
return a.ProjectTo<Estudio>().Concat(b.ProjectTo<Estudio>());
I know this is simple, but my mind is playing tricks on me right now. If we have a flat list of objects with the properties GroupSortIndex and ItemSortIndex (within the group) and we want to find the first item in the list, what's the Linq/lambda for that?
About all I can think of is (meta, not literal code...)
var soughtItem = Source.OrderBy(ItemSortIndex).OrderBy(GroupSortIndex).ToList()[0]
...but that just looks so wrong to me for some reason.
Read post : Default Extension methods to get difference between first and firstordefault
you can use FirstOrDefualt() or First() function
var soughtItem = Source.OrderBy(ItemSortIndex).
ThenBy(GroupSortIndex).FirstOrDefualt();
if(soughtItem !=null)//advantage of using firstordefault
{
}
its better to use FirstOrDefualt because if there is no data it will return null intead of excetipn
You can use IOrderedEnumerable.ThenBy (Note: an IOrderedEnumerable is returned from IEnumerable.OrderBy):
var firstItem = source.OrderBy(s => s.GroupSortIndex)
.ThenBy(s => s.ItemSortIndex)
.First();
This orders first by the group and then by the item. You should use FirstOrDefault if the sequence can be empty. Otherwise First raises an exception.
(i've assumed that you want to order first by group and then by the item instead, since the ItemSortIndex is the index of the item within the group(as mentioned))
var soughtItem = Source
.OrderBy(ItemSortIndex)
.ThenBy(GroupSortIndex).First();
If ItemSortIndex and GroupSortIndex are properties instead of functions, then you need:
var soughtItem = Source
.OrderBy(i => ItemSortIndex)
.ThenBy(i => GroupSortIndex).First();