I'm using NHibernate first time and in this line it throws exception for me
var total = session
.QueryOver<Comment>().Where(p => p.Entry.Author == username)
.ToRowCountQuery()
.FutureValue<int>();
var results = session
.QueryOver<Comment>().Where(p => p.Entry.Author == username)
.Fetch(x => x.Entry).Eager()
.OrderBy(x => x.Posted).Desc()
.Skip(skip)
.Take(take)
.List();
The Exception is
could not resolve property: Entry.Author of: FunnelWeb.Model.Comment
I guess, the problem is that Entry object doesn't loaded here. How can I do that trick with Nhibernate?
QueryOver is just a strongly-typed wrapper for Criteria, and doesn't allow implicit deep references.
You'd have to use:
session.QueryOver<Comment>()
.JoinQueryOver(x => x.Entry)
.Where(x => x.Author == username)
Or you can use Query<> instead (LINQ) which will work with the syntax you've tried.
You'd need to JoinAlias or JoinQueryOver. I have an example below on how to use Future queries...
Entry entryAlias = null;
var q = session.QueryOver<Comment>()
.JoinAlias(x => x.Entry, () => entryAlias)
.Where(() => entryAlias.Author == username);
var totalFuture = q.ToRowCountQuery().FutureValue<int>(); //ToRowcountQuery clones the query, we can reuse it for results
var resultsFuture = q
//.Fetch(x => x.Entry).Eager() //already joined
.OrderBy(x => x.Posted).Desc()
.Skip(skip)
.Take(take)
.Future<Comment>();
var results = resultsFuture.ToList(); //both future queries are executed in the same batch
var total = totalFuture.Value;
Related
Consider the following LINQ statements:
var model = getModel();
// apptId is passed in, not the order, so get the related order id
var order = (model.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => y.OrderId));
var orderId = 0;
var orderId = order.LastOrDefault();
// see if more than one appt is associated to the order
var apptOrders = (model.getMyData
.Where(x => x.OrderId == orderId)
.Select(y => new { y.OrderId, y.AppointmentsId }));
This code works as expected, but I could not help but think that there is a more efficient way to accomplish the goal ( one call to the db ).
Is there a way to combine the two LINQ statements above into one? For this question please assume I need to use LINQ.
You can use GroupBy method to group all orders by OrderId. After applying LastOrDefault and ToList will give you the same result which you get from above code.
Here is a sample code:
var apptOrders = model.getMyData
.Where(x => x.ApptId == apptId)
.GroupBy(s => s.OrderId)
.LastOrDefault().ToList();
Entity Framework can't translate LastOrDefault, but it can handle Contains with sub-queries, so lookup the OrderId as a query and filter the orders by that:
// apptId is passed in, not the order, so get the related order id
var orderId = model.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => y.OrderId);
// see if more than one appt is associated to the order
var apptOrders = model.getMyData
.Where(a => orderId.Contains(a.OrderId))
.Select(a => a.ApptId);
It seems like this is all you need:
var apptOrders =
model
.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => new { y.OrderId, y.AppointmentsId });
As shown in the below code, the API will hit the database two times to perform two Linq Query. Can't I perform the action which I shown below by hitting the database only once?
var IsMailIdAlreadyExist = _Context.UserProfile.Any(e => e.Email == myModelUserProfile.Email);
var IsUserNameAlreadyExist = _Context.UserProfile.Any(x => x.Username == myModelUserProfile.Username);
In order to make one request to database you could first filter for only relevant values and then check again for specific values in the query result:
var selection = _Context.UserProfile
.Where(e => e.Email == myModelUserProfile.Email || e.Username == myModelUserProfile.Username)
.ToList();
var IsMailIdAlreadyExist = selection.Any(x => x.Email == myModelUserProfile.Email);
var IsUserNameAlreadyExist = selection.Any(x => x.Username == myModelUserProfile.Username);
The .ToList() call here will execute the query on database once and return relevant values
Start with
var matches = _Context
.UserProfile
.Where(e => e.Email == myModelUserProfile.Email)
.Select(e => false)
.Take(1)
.Concat(
_Context
.UserProfile
.Where(x => x.Username == myModelUserProfile.Username)
.Select(e => true)
.Take(1)
).ToList();
This gets enough information to distinguish between the four possibilities (no match, email match, username match, both match) with a single query that doesn't return more than two rows at most, and doesn't retrieve unused information. Hence about as small as such a query can be.
With this done:
bool isMailIdAlreadyExist = matches.Any(m => !m);
bool isUserNameAlreadyExist = matches.LastOrDefault();
It's possible with a little hack, which is grouping by a constant:
var presenceData = _Context.UserProfile.GroupBy(x => 0)
.Select(g => new
{
IsMailIdAlreadyExist = g.Any(x => x.Email == myModelUserProfile.Email),
IsUserNameAlreadyExist = g.Any(x => x.Username == myModelUserProfile.Username),
}).First();
The grouping gives you access to 1 group containing all UserProfiles that you can access as often as you want in one query.
Not that I would recommend it just like that. The code is not self-explanatory and to me it seems a premature optimization.
You can do it all in one line, using ValueTuple and LINQ's .Aggregate() method:
(IsMailIdAlreadyExist, IsUserNameAlreadyExist) = _context.UserProfile.Aggregate((Email:false, Username:false), (n, o) => (n.Email || (o.Email == myModelUserProfile.Email ? true : false), n.Username || (o.Username == myModelUserProfile.Username ? true : false)));
i have a somewhat complex structure i wont get into,
but what i try doing is:
Get all ShopItems, who's SourceItem has changed,
Get and update them according to their Source/Shop data.
i conjured the following:
var query = _ctx.ShopItems
.Include(si => si.Shop)
.Include(si=>si.SourceItem)
.Include(si => si.SourceItem.Source)
.Include(si=>si.Shop.ShopType)
.GroupBy(i => i.SourceItem)
.Where(g => g.Key.LastUpdate > lastUpdate)
.OrderBy(g => g.Key.LastUpdate)
.Take(updateCountLimit);
the query seems to work, but when itterating the Groups:
groupItem.Key.Source is null.
I somewhat solved it by Removing the Include()s, saving the Entities to an Array, and explicitly loading the references using
_ctx.Entry(updatedSourceItem.Key).Reference(src=>src.Source).Load();
How can i perform the query i want without round-tripping the DB for explicit loading ?
Not sure, but it's backwards to start with ShopItems and then group by SourceItem. Try just starting with SourceItem, something like
:
var query = _ctx.SourceItems
.Include(i => i.ShopItems)
.Include(i => i.Source)
.Include(i => i.ShopItems.Select( si => si.Shop))
.Include(i => i.ShopItems.Select( si => si.Shop).ShopType)
.Where(i => i.LastUpdate > lastUpdate)
.OrderBy(i => i.LastUpdate)
.Take(updateCountLimit);
//or
var query = _ctx.SourceItems
.Include("ShopItems")
.Include("Source")
.Include("ShopItems.Shops")
.Include("ShopItems.Shops.ShopType")
.Where(i => i.LastUpdate > lastUpdate)
.OrderBy(i => i.LastUpdate)
.Take(updateCountLimit);
I'm doing a query over two different tables.
In the first query, i get some Ids that I then have to check in another table.
Then I do the first query again with the result of the second query.
This can't be the best way to do this.
But I haven't found a good way to solve it. So some help would be appreciated.
IntOrderInvoiceCostOut y = null;
var list = session.QueryOver<IntOrderInvoiceCostOut>(() => y)
.Where(x => x.IntegrationHandleDate == null)
.Select(Projections.Distinct(Projections.Property(() => y.Externalid)))
.List<string>();
var nonPreliminaryOrders = session.QueryOver<RefImplOrderEntity>()
.WhereRestrictionOn(x => x.ExternalId).IsIn(list.ToList())
.Where(x => x.StatusTypeId != 95)
.Select(x => x.ExternalId)
.List<string>();
var finalList = session.QueryOver<IntOrderInvoiceCostOut>()
.WhereRestrictionOn(x => x.Externalid).IsIn(nonPreliminaryOrders.ToList())
.Where(x => x.IntegrationHandleDate == null)
.OrderBy(x => x.IntegrationCreateDate)
.Asc
.List();
The code works...but i't really ugly.
you could use detacheCriteria for this. I have omitted couple of conditions and you might have to twick a bit as per your requirement.
for example
IntOrderInvoiceCostOut y = null;
var list = QueryOver.Of<IntOrderInvoiceCostOut>(() => y)
.Where(x => x.IntegrationHandleDate == null)
.Select(Projections.Distinct(Projections.Property(() => y.Externalid)))
.DetachedCriteria;
var nonPreliminaryOrders = QueryOver.Of<RefImplOrderEntity>()
.Where(Subqueries.PropertyIn(nameof(RefImplOrderEntity.ExternalId), list));
.Select(x => x.ExternalId)
.DetachedCriteria
var finalList = session.QueryOver<IntOrderInvoiceCostOut>()
.Where(Subqueries.PropertyIn(nameof(IntOrderInvoiceCostOut.ExternalId), nonPreliminaryOrders));
.List();
I had written a Query in NHibernate as below:
var queryResult = CurrentSession.QueryOver()
.Where(r => r.StatusId == 1)
.JoinQueryOver(a => a.ActorList)
.Where(s=>s.IsActor==1)
.List()
.Distinct()
.ToList();
I am trying to retrieve only Where(s=>s.IsActor==1), But It Is Getting Records
Where(s=>s.IsActor==0) also...
How can I get only IsActor==1 records?
Thanks in Advance
You need to specify a predicate in the join, so that it is applied to the join not the top where:
(will look something like ...LEFT JOIN actor on actor.Id = p.ActorId AND IsActor = 1)
Actor actorAlias = null;
var queryResult = CurrentSession.QueryOver()
.Where(r => r.StatusId == 1)
.Left.JoinQueryOver(r => r.ActorList, () => actorAlias, a => a.IsActor==1)
.List()
.Distinct()
.ToList();