Adonis Query Builder: How to make .where condition on relationship - query-builder

So I have this query
return await Order
.query()
.with('order_status')
.where('order_status.is_completed_type', true)
I want to only get the orders with order_status.is_completed_type === true
but it seems that doesn't work that way, is there a way to do this?

You can do it by passing query builder config to .with method: https://adonisjs.com/docs/4.1/relationships#_adding_runtime_constraints
So in your case:
return await Order
.query()
.with('order_status', q=> {
q.where('is_completed_type', true)
})

Related

How can i construct a NEST query with optional parameters?

I'm using the NEST .NET client (6.3.1), and trying to compose a search query that is based on a number of (optional) parameters.
Here's what i've got so far:
var searchResponse = await _client.SearchAsync<Listing>(s => s
.Query(qq =>
{
var filters = new List<QueryContainer>();
if (filter.CategoryType.HasValue)
{
filters.Add(qq.Term(p => p.CategoryType, filter.CategoryType.Value));
}
if (filter.StatusType.HasValue)
{
filters.Add(qq.Term(p => p.StatusType, filter.StatusType.Value));
}
if (!string.IsNullOrWhiteSpace(filter.Suburb))
{
filters.Add(qq.Term(p => p.Suburb, filter.Suburb));
}
return ?????; // what do i do her?
})
);
filter is an object with a bunch of nullable properties. So, whatever has a value i want to add as a match query.
So, to achieve that i'm trying to build up a list of QueryContainer's (not sure that's the right way), but struggling to figure out how to return that as a list of AND predicates.
Any ideas?
Thanks
Ended up doing it by using the object initialisez method, instead of the Fluent DSL"
var searchRequest = new SearchRequest<Listing>
{
Query = queries
}
queries is a List<QueryContainer>, which i just build up, like this:
queries.Add(new MatchQuery
{
Field = "CategoryType",
Query = filter.CategoryType
}
I feel like there's a better way, and i don't like how i have to hardcode the 'Field' to a string... but it works. Hopefully someone shows me a better way!

LINQ include and Projection

I have some classes defining entities with relationships
Account
has many Conversations [IEnumerable<Conversation> Conversations]
Conversation
has many Participants [IEnumerable<Account> Participants]
has many Messages [IEnumerable<Message> Messages]
Message
has one Sender [Account Sender]
has one Conversation [Conversation Conversation]
I'm trying to write a LINQ query that returns a list of Conversation ordered by date and including related participants and messages.
public async Task<List<Conversation>> FindAllByAccountIdAsync(Int32 id)
{
return await _Db.Conversations
.Where(c => c.Participants.Any(p => p.AccountId == id))
.Include(c => c.Participants)
.Include(c => c.Messages)
.ToListAsync();
}
This do the work but includes to much data i do not really need.
public async Task<List<Conversation>> FindAllByAccountIdAsync(Int32 id)
{
return await _Db.Conversations
.Where(c => c.Participants.Any(a => a.AccountId == id))
.Include(c => c.Participants.Select(a=> new
{
AccountId = a.AccountId,
Profile = new { FullName = a.Profile.FullName,
Email = a.Profile.Email
}
}))
// Only return the last message in
// Eventually I would not return an array with a single object but just the single object inside associated with the property LastMessageIn
.Include(c => c.Messages.OrderBy(m => m.Date).Select(m=> new
{
Body = m.Body,
SenderId = m.Sender.AccountId
}).Last())
.ToListAsync();
}
This script returns a mile long exception
{"message":"An error has occurred.","exceptionMessage":"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........}
My mind resist understanding and learning LINQ I do not know if its just me but as soon requirements exceeds basic querying and projection it break out of my control
Someone have some hints?
I'm not sure if I understand your question, but I believe you want something like this:
public async Task<List<Conversation>> FindAllByAccountIdAsync(Int32 id)
{
return await _Db.Conversations
.Where(c => c.Participants.Any(p => p.AccountId == id))
.Include(c => c.Participants)
.Include(c => c.Messages)
.Select(c => new
{
Participants = c.Participants.Select(a=> new
{
AccountId = a.AccountId,
Profile = new { FullName = a.Profile.FullName,
Email = a.Profile.Email
}
},
//EDIT: using OrderByDescending and FirstOrDefault
Messages = c.Messages.OrderByDescending(m => m.Date).Select(m=> new
{
Body = m.Body,
SenderId = m.Sender.AccountId
}).FirstOrDefault())
//others properties here
}
.ToListAsync();
}
You cannot project on an Include. An include is simply Eager Loading. The output does not change in the C#. Only the amount of data that is originally loaded (ie performance) changes.
It seems you want a projection and not eager loading, which are completely incompatible concepts.
However I cannot understand what exactly what you are trying to achieve.
public async Task<List<Conversation>> FindAllByAccountIdAsync(Int32 id)
{
return await _Db.Conversations
.Where(c => c.Participants.Any(p => p.AccountId == id))
.Include(c => c.Participants.Select(_=>_))
.Include(c => c.Messages.Select(_=>_))
.ToListAsync();
}
Should be enough.

LINQ to Entities complex query

Is it possible ...??? I have 4 DropDownLists on my main page and the
user may select from any, all or some of
the DropDownLists. I am capturing their selection (or non-selection) using a SESSION
variable. What I would like to be able to do is pass the session
variable values to my Data Access Layer and build a WHERE clause
(maybe using StringBuilder) and then place that variable SOMEHOW into
my query expression. Is that possible??? Sorry, I'm a newbie. Thanks ~susan~
public class DLgetRestaurants
{
FVTCEntities db = new FVTCEntities();
public List<RESTAURANT> getRestaurants(string cuisineName, string priceName, string cityName)
[Build a string based on the values passed to the function]
{
var cuisineID = db.CUISINEs.First(s => s.CUISINE_NAME == cuisineName).CUISINE_ID;
List<RESTAURANT> result = (from RESTAURANT in db.RESTAURANTs.Include("CITY").Include("CUISINE").Include("Price")
where **[USE STRINGBUIDER EXPRSSION HERE]**
select RESTAURANT).ToList();
return result;
}
}
You can compose Where conditions which are linked by a logical AND relatively easy in LINQ extension method syntax:
var query = db.RESTAURANTs.Include("CITY").Include("CUISINE").Include("Price");
if (userHasSelectedInDDL1)
query = query.Where(r => r.PropertyForDDL1 == ValueFromDDL1);
if (userHasSelectedInDDL2)
query = query.Where(r => r.PropertyForDDL2 == ValueFromDDL2);
if (userHasSelectedInDDL3)
query = query.Where(r => r.PropertyForDDL3 == ValueFromDDL3);
if (userHasSelectedInDDL4)
query = query.Where(r => r.PropertyForDDL4 == ValueFromDDL4);
List<RESTAURANT> result = query.ToList();
For a much more flexible solution to build queries dynamically the Dynamic LINQ Library recommended by boca is probably the better choice.
I have done this in the past using the Dynamic Linq Library.

Is it safe to pass Linq and a .ToList(), .Single(), etc to another method as a func parameter?

I needed to wrap some Linq queries with some Retry Policy logic.
Is it safe to pass this:
return WithRetry<User>(() =>
dataContext.Users.Where(u => u.UserID == userID).SingleOrDefault());
to this:
public TResult WithRetry<TResult>(Func<TResult> methodCall)
{
// My Try/Catch Retry Code
}
Or should the first line be constructed like this instead:
return WithRetry<User>(() =>
{
return dataContext.Users
.Where(u => u.UserID == userID)
.SingleOrDefault();
});
The anonymous wrapper is not needed. Just pass the lambda expression function call directly.
AFAIK, if the argument type of a method is Func, calling it will automatically pass as a function without executing it. You don't need to further wrap it in an anonymous function wrapper.

How do you return a default value if a LINQ to entities query returns no values

In a LINQ to entities expression like this:
var vote = (from vote in db.Vote where
vote.Voter.Id == user.Id
select v).FirstOrDefault();
How do you add a DefaultIfEmpty value so that when there's no vote I'd get a default value?
Another approach, if Vote is a reference type and thus uses null as its default value, would be to use the null coalescing operator:
var vote = (db.Vote
.Where(v => v.Voter.Id == user.Id)
.FirstOrDefault()) ?? defaultVote;
Add your own extension method. For instance:
public static class Extension
{
public static T FirstOrDefault(this IEnumerable<T> sequence, T defaultValue)
{
return sequence.Any() ? sequence.First() : defaultValue;
}
}
With that class in scope, you can say:
var vote = (from vote in db.Vote where
vote.Voter.Id == user.Id
select v).FirstOrDefault(yourDefaultValue);
Of course, your method can also have an overload that returns default(T), if that was what you where looking for. There is already defined a DefaultIfEmpty extension method in the built-in Extension class, so I named the method in the example "FirstOrDefault", which seems like a better fit.
Just add the default value before getting the first element.
var vote = db.Vote
.Where(v => v.Voter.Id == user.Id)
.DefaultIfEmpty(defaultVote)
.First();
Note that you can now safely use First() instead of FirstOrDefault().
UPDATE
LINQ to Entity does not recognize the DefaultIfEmpty() extension method. But you can just use the null coalescing operator.
var vote = db.Vote.FirstOrDefault(v => v.Voter.Id == user.Id) ?? defaultVote;
I ended up going for a very simple approach which was recommended by an answer here that was latter erased:
var vote = (from vote in db.Vote
where vote.Voter.Id == user.Id
select v).FirstOrDefault();
if (vote == null) {
vote = new Vote() { .... };
db.AddToVoteSet(vote);
}
For some reason if I turn the resultset into a List, the Defaultifempty() works I don't know if I've inadvertantly crossed over into Linq area.
var results = (from u in rv.tbl_user
.Include("tbl_pics")
.Include("tbl_area")
.Include("tbl_province")
.ToList()
where u.tbl_province.idtbl_Province == prov
select new { u.firstName, u.cellNumber, u.tbl_area.Area, u.ID, u.tbl_province.Province_desc,
pic = (from p3 in u.tbl_pics
where p3.tbl_user.ID == u.ID
select p3.pic_path).DefaultIfEmpty("defaultpic.jpg").First()
}).ToList();

Resources