EF Core: how to load a single item from collection-nav property? - asp.net

I have one-to-many relationship between two entities: User (one) has Messages (many) collection.
There needs to display some information about User and at the same time needs to load single Message.
I try to do something like:
mycontext.Users.Where(..).Include(user => user.Messages.Take(1).First());
However this code throws ecxeption
InvalidOperationException: The property expression 'user=> {from
Message m in [chat].Messages select
[m] => Take(1) => First()}' is not valid. The expression should
represent a property access: 't => t.MyProperty'. For more information
on including related data, see
http://go.microsoft.com/fwlink/?LinkID=746393.
How can I solve it?

Pretty easy. Instead of trying to load a message from a user, load the user from the message:
mycontext.Messages.Where(..).Include(msg => msg.User);
In fact, you can do it easier for you:
mycontext.Messages
.Include(msg => msg.User)
.Where(msg => msg.User...);
So you don't have to filter on Message rather than on User

You cannot do that the way you have tried.You have to retrieve all the related messages from the db and after that filter it on the memory (IEnumerable) as shown below.
var useList= mycontext.Users.Where(..).Include(user => user.Messages);
foreach (var u in useList)
{
var message= u.Messages.Take(1).First();
}

Related

How to filter objects based on the Symfony workflow's place which is an array?

I've been struggling with this for a while but can't find a clean way to do it, so I'm seeking for some help.
I have custom filters (ApiPlatform 2.5 and Symfony 5.1) on database API outputs, and I need to filter on the current workflow place, or status as you like, of each output.
The Status has the below structure, which is a symfony workflow's place :
Status = { "OPEN": 1 }
My issue is that the status is stored as an array in the DB, and I can't find a way to have the querybuilder finding a match.
I've tried to build locally an array to do an = , a LIKE or an IN :
$status['OPEN'] = 1;
$queryBuilder->andWhere(sprintf('%s.Status = :st', $rootAlias))
->leftJoin(sprintf('%s.Objs', $rootAlias), 'o')
->andWhere('o.Profile = :p')
->setParameters(array(
'st' => $status,
'p' => $profile
));
But no way :(
I implemented a workaround that works but I don't like it as I'm using workflows a lot and need a clean way to filter outputs.
My workaround is fairly simple, when the status is writen as an array in the DB, I also store it as a string in another field called StatusText, then filtering on StatusText is easy and straight.
Status can have different contents obviously : OPEN, CLOSING, CLOSED, ...
Help appreciated !!
Thanks
EDIT & Solution
As proposed by Youssef, use scienta/doctrine-json-functions and use JSON_EXTRACT :
composer require scienta/doctrine-json-functions
Important, that was part of my issue, use the Doctrine type json_array an not array to store the status or the state, however you call it, in the Database.
Integrate the alias provided inside the ApiPlatform custom filter :
$rootAlias = $queryBuilder->getRootAliases()[0];
$json_extract_string = "JSON_EXTRACT(".$rootAlias.".Status, '$.OPEN') = 1";
$queryBuilder->andwhere($json_extract_string )
->leftJoin(sprintf('%s.Objs', $rootAlias), 'o')
->andWhere('o.Profile = :p')
->setParameter('p', $profile);
You need to ask Doctrine if the JSON array contains the status, but you can't do that with the QueryBuilder method.
When you hit the ORM limitations you can use a Native Query with ResultSetMapping. It allows you to write a pure SQL query using specific features of your DBMS but still get entity objects.
Or you can use scienta/doctrine-json-functions and use JSON_EXTRACT

Firestore startAfter last snapshot is not working, if 2 or more documents have same field value

I have a collection named 'test', which has multiple documents.
Each document has a unique name and a number field.
I have implemented pagination using query cursor. The query to get initial documents is as follows:
let lastDocument={};
ref.collection("test").orderBy('number', 'desc').limit(3).get()
.then(snapshot => {
lastDocument = snapshot.docs[snapshot.docs.length-1];
})
.catch(err => {
console.log("Error getting documents", err);
});
After getting the lastDocument, I paginate it as follows:
ref.collection("test").orderBy('number', 'desc').startAfter(lastDocument).limit(3); //lastDocument has the last document of the snapshot that was retrieved.
In my collection, if there are 2 or more documents which have same value of field 'number', then I don't get that document in the query result. Whereas, If I change the value of the field 'number', so as to make all the documents unique, I get all the results in batches(according to the limit clause) properly.
It's mentioned in the document as well, that, if multiple fields have same population value, the query won't work https://firebase.google.com/docs/firestore/query-data/query-cursors#paginate_a_query
How should I modify the query, so as to get all documents in the result, even if 2 or more documents have same values for the field used in orderBy?
I am using:
"react-native-firebase": "^5.5.6",
"react-native": "^0.59.8",
"firebase": "^6.6.1",
Update: I have tested the queries on JSbin and it returns proper
results. I have issues when I use it in react native app with
react-native-firebase library. The pattern that I have recognised so
far is as follows: let's say, there are documents like below
{id:a,num:1},{id:b,num:2},{id:c,num:2},{id:d,num:2},{id:e,num:3},{id:f,num:4}
Then if I query for the first set with orderBy('num','desc') and limit
as 2, I'll get [a,b]. Now, if I query with the startAfter last
document as 'b' and limit as 2, then it will skip all the documents
after b, which have num same as that of b (i.e 2). So, it'll skip doc
[c,d] and return me [e,f]. This works fine on jsbin, but not on react
native using react-native-firebase library.
Please find the exact output for the code here on react-native below:
"Got all results"
"wbXwyLJheRfYXXWlY46j => {\"index\":2,\"number\":2}"
"kGC5cYPN1nKnZCcAb9oQ => {\"index\":6,\"number\":2}"
"8Ek8iWCDQPPJ5s2n8PiQ => {\"index\":4,\"number\":2}"
"mr7MdAygvuheF6AUtWma => {\"index\":1,\"number\":1}"
"RCO5SvNn4fdoE49OKrIV => {\"index\":3,\"number\":1}"
"CvVG7VP1hXTtcfdUaeNl => {\"index\":5,\"number\":1}"
"Got first page"
"wbXwyLJheRfYXXWlY46j => {\"index\":2,\"number\":2}"
"kGC5cYPN1nKnZCcAb9oQ => {\"index\":6,\"number\":2}"
"Got second page"
"mr7MdAygvuheF6AUtWma => {\"index\":1,\"number\":1}"
"RCO5SvNn4fdoE49OKrIV => {\"index\":3,\"number\":1}"
Note that it has skipped : "8Ek8iWCDQPPJ5s2n8PiQ => {\"index\":4,\"number\":2}"
Encountered one more issue in react-native-firebase library:
If there is a map structure in the documents like below:
{
subscribedUsers:{
+1656666:true,
+1657878:false,
+1677676:true
}
}
Now if I fire a query like below:
db.collection("ownerUsers").where(subscribedUsers.${phonenumber} ,
'==' , true).limit(3).get();
The query returns 3 results.Now, if I try to get the next 3 results using startAfter clause like below, it works fine with firebase, but doesn't work with react-native-firebase library:
db.collection("ownerUsers").where(subscribedUsers.${phonenumber} , '==' , true).startAfter(last).limit(3).get()
then I get the below error:
[Info] 11-10 21:38:42.631 21908 21967 I ReactNativeJS: 'Error getting documents', { [Error: Firestore: Client specified an invalid argument. (firestore/invalid-argument).]
This is a bug in the react-native-firebase library. Check out the issue on Github to follow along. The issue is now marked as fixed, and this fix will be included in the next release (>5.5.6).
Old answer below...
The document you pass into startAfter needs to contain all the Firestore needs to determine where to start next. If the fields you filter on are not unique in the collection, the DocumentSnapshot will also need to contain the ID of the document. If your query is showing the first or second document matching the criteria, instead of the one after the one you're looking for, it's most likely because the ID is missing from lastDocument.
After your update, I tried reproducing the problem with this code:
var db = firebase.firestore();
var ref = db.collection("58783480").orderBy('number', 'desc');
ref.get().then(function(snapshot) {
console.log("Got all results");
snapshot.forEach(function(doc) {
console.log(doc.id+' => '+JSON.stringify(doc.data()));
});
var last;
ref.limit(2).get().then(function(snapshot) {
console.log("Got first page");
snapshot.forEach(function(doc) {
console.log(doc.id+' => '+JSON.stringify(doc.data()));
last = doc;
});
ref.startAfter(last).limit(2).get().then(function(snapshot) {
console.log("Got second page");
snapshot.forEach(function(doc) {
console.log(doc.id+' => '+JSON.stringify(doc.data()));
last = doc;
});
});
})
});
live example here
But the output matches exactly with what I'd expect:
Got all results
wbXwyLJheRfYXXWlY46j => {"index":2,"number":2}
kGC5cYPN1nKnZCcAb9oQ => {"index":6,"number":2}
8Ek8iWCDQPPJ5s2n8PiQ => {"index":4,"number":2}
mr7MdAygvuheF6AUtWma => {"index":1,"number":1}
RCO5SvNn4fdoE49OKrIV => {"index":3,"number":1}
CvVG7VP1hXTtcfdUaeNl => {"index":5,"number":1}
Got first page
wbXwyLJheRfYXXWlY46j => {"index":2,"number":2}
kGC5cYPN1nKnZCcAb9oQ => {"index":6,"number":2}
Got second page
8Ek8iWCDQPPJ5s2n8PiQ => {"index":4,"number":2}
mr7MdAygvuheF6AUtWma => {"index":1,"number":1}

MVC5 combine db .Include with OrderBy

In my controller I'm including Balances and RZiS like this
Analysis1Full analysis1Full = db.Analysis1Full
.Include(u => u.Balance)
.Include(r => r.RZiS)
.SingleOrDefault(u => u.Analysis1FullId == id);
//enter code here
it gives me right object. The point is that I want to sort Balance by string filed (Year). But it gives me an error:
Analysis1Full analysis1Full = db.Analysis1Full
.Include(u => u.Balance.OrderBy(y => y.Year))
.Include(r => r.RZiS)
.SingleOrDefault(i => i.Analysis1FullId == id);
Error:
An exception of type 'System.ArgumentException' occurred in EntityFramework.dll but was not handled in user code
Additional information: The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
The reason why the exception is thrown is simply because it is designed this way. According to MSDN, the Include() only Specifies the related objects to include in the query results.
To have the children in an order, you can add an OrderBy() when accessing them or you'd need to load them in a different query.
I solved it like this ... it is working
Analysis1Full analysis1Full = db.Analysis1Full.Find(id);
analysis1Full.Balance = db.Balances.Where(t=>t.Analysis1FullId == analysis1Full.Analysis1FullId).OrderBy(y=>y.Year).ToList();
analysis1Full.RZiS = db.RZiS.Where(t => t.Analysis1FullId == analysis1Full.Analysis1FullId).OrderBy(y => y.Year).ToList();
do you think it's good approach ?

RubyMotion Firebase (motion-firebase) How to retrieve data?

Using the motion-firebase library for RubyMotion Firebase (https://github.com/colinta/motion-firebase) I can set data easy enough either by doing:
firebase.set({ full_name: ['first_name' => 'Fred', 'last_name' => 'Flintstone'] })
or
firebase['first_name'] = 'Fred'
But I'm having trouble figuring out how to retrieve data.
I've tried the different firebase.query methods listed in the reponse readme but I'm definitely missing something.
I'd hoped it would be as simple as:
firebase.query['first_name']
=> 'Fred'
Could someone please explain how I might go about querying Firebase with a key and returning the value.
colinta here! You'll need to attach an observer (https://github.com/colinta/motion-firebase#attaching-observers-to-read-data).
firebase_ref.once(:value) { |snapshot| snapshot.value }
See https://www.firebase.com/docs/ios-api/Classes/FDataSnapshot.html for info on the FDataSnapshot type.

Nested requests with Scala and play Framework

I'm working with Play Framework (2.0.4) and Scala, and I have a problem.
I call my backend to get a list of users (in json), and for each user, I have to get extra info from the backend (one request per user).
So in my services, I have :
def getUsers(/*different uninteresting parameters*/ ): Promise[List[Option[User]]]
and
def getExtraUserInfo(user:User):Promise[Option[Double]]
So for each User which is returned by getUsers, I want to call getExtraUserInfo, and return the user plus the extra info about each user.
So in my controller, i've tried to do something like that :
def getUsers(/*parameters*/) = AuthenticatedAsync{ request =>
val users = UserService.getUsers(/*parameters*/)
users.flatMap {
case Some(userList) =>
Ok(Success("users" -> Json.toJson(userList.flatMap{
user => UserService.getExtraUserInfo(user).map {
case Some(price) => user.price = price
user
case _ => user
}
}.map(_.json))))
case _ => InternalServerError(Error("error while getting users", Errors.TECHNICAL))
}
}
Do you guys have any idea how to do it ? (this code doesn't work, but that's all I managed to do...)
Try Promise.sequence to sequence a list of promise.
Promise.sequence transforms a List[Promise[T]] to a Promise[List[T]]...
see the Play! Scala API here

Resources