I have a situation where I am providing a method to query for data in various ways. We provide the user with 4 different fitler criteria and they can mix and match as much as they want.
For example:
public Fruit GetFruit(Boolean isLocal, string storeName, string classificationType, string state);
This is simple when all of the attributes are on the table, but my issue arises as a result of complexity in the data model. Three of my attributes are simple, they are just joins, but i have one table that sits behind a one to many relationship. So in order to query for it I have to do a many to many join.
So lets say i am trying to determine all fruit a store offers. A store has a list of fruits and our classificationType sits behind a many relationship (FruitClassification)
alt text http://tinyurl.com/39q6ruj
The only successful way i have been able to query this in EF is by selecting all Fruits (by classification), and then selecting all stores that meet the filter criteria and then joining them.
You would think this query in ef would be functional:
var final = (
from s in Stores
join fc in FruitClassifications.Where(z=>z.Classifications.Code == classificationType && z.Classifications.Type.Code =="FRT").Select(x=>x.Fruit).Distinct()
on s.Fruits.Id equals f.Id
where s.Name=name && s.isLocal && s.State==state
select s
).ToList();
But it runs horrible(and looks the same when i profile it), Is there any way i can push this query down to the database? A better way to query?
I think this is what you want:
var final = (from s in Stores
where s.Name=name && s.isLocal && s.State==state
&& s.Fruits.Any(f =>
f.FruitClassifications.Any(fc => fc.Code == classificationType
&& fc.Type.Code == "FRT"))
select s).ToList();
http://learnentityframework.com/LearnEntityFramework/tutorials/many-to-many-relationships-in-the-entity-data-model/
this might help you. EF has the possibility to generate those relations with navigation properties from the designer, so you don't have to use the join.
Related
I'm struggling to find the correct combination of LINQ Methods to perform a multi-table left join with a one to many mapping that makes a list along with the grouped results.
Current Status
I have a Plan table, joined with other tables to get the columns I need to get my list of plans.
var plans = await (
from ubp in db.ViewUserBusinessPlan
join bp in db.ViewBusinessPlan on ubp.BusinessPlanId equals bp.BusinessPlanId
where bp.BusinessId == businessId
select new
{
ubp.UserBusinessPlanId,
ubp.BusinessPlanId,
bp.Name,
bp.PlanGroup,
bp.BusinessId,
ubp.BusinessLocationId,
ubp.StripeSubscriptionId,
ubp.UserId,
ubp.BusinessPlanPriceCents
}
Problem:
Now I need to ultimately get the applicable Tax Rates that are associated with those Plans. This is stored in 2 tables.
UserBusinessPlanTaxRates - (a mapping table) that contains UserBusinessPlanId and TaxRateId.
TaxRates - TaxRate Information with TaxRateId as PK
Some plans have tax rates, some do not, so need a LEFT JOIN type scenario. Also, some plans can have multiple tax rates so I need a list of TaxRates. I've tried various Group methods, subqueries, and left joins. But nothing seems to put them all together.
I want to get all plans, with a list of TaxRates.
You can add a sub-query in the select to do the necessary join:
TaxRates = db.UserBusinessPlanTaxRates.Where(ubptr => ubptr.UserBusinessPlanId == ubp.UserBusinessPlanId)
.GroupJoin(db.TaxRates, ubptr => ubptr.TaxRateId, tr => tr.TaxRateId, (ubptr, trj) => trj)
.SelectMany(trj => trj)
.ToList()
Whether it will translate properly (or optimally) to SQL depends on what LINQ to database you are using.
I am querying for report generation and below are some scenarios scenarios which got pass while
var account_logs = container.GetItemLinqQueryable<AccountLog>(true).Where(u => u.AccessedOn > MinDate);
var TempGoodResetIDs = (from ll in account_logs
where (ll.AccessedOn >= StartDate) &&
(ll.AccessedOn <= EndDate)
&& ((ll.Activity == 3) &&
((ll.Result == (int)Log.AccountResult.PasswordReset) ||
(ll.Result == (int)Log.AccountResult.TempPWDSentThroughEmail)))
select ll)
This got passed and I gotaccount_log filled with data.
Then I have smething like this in code.
var BadResetIDs = TempBadResetIDs.Select(ll => ll.ActivityID).Distinct().Except(GoodResetIDs);
var Captcha = (from ll in account_logs
join
b in BadResetIDs on ll.ActivityID equals b
where ((ll.Activity == 3) && (ll.Result == 5))
select ll.ActivityID).Count()
Here I got an exception that 'Join' is not supported in Cosmos.Is there a workaround to join cosmos document with BadResetIDs which is an iquerable and contains activity ID?
I've used SelectMany but not sure who to compare two different object accountlog and BadResetIDs.
While Cosmos SQL has a JOIN operator it only works by joining data within a single document. Cosmos does not support joining several documents in a query so the LINQ join operator is not supported.
In your case you might be able to solve your problem by performing two queries. However, you will be moving data from the database to the client to create the second query and you run the risk of the database having changed in the meantime.
Having the desire to join documents in a query could be a sign that you are retrofitting a relational database approach on top of Cosmos. Designing your system based on "no SQL" thinking from the start can lead to a very different data model.
If you get really technical there is one exception to a query not being able to combine multiple documents: You can create a stored procedure in javascript that can do that. However, a stored procedure can only execute within a single partition so it's not a general solution to combing multiple documents in a single query.
Trying to create a Linq query that can do multiple searches in one command instead of having multiple search result pages. It is working great when I am trying to find multiple records that have a subject (in my case CHIEF_COMPLAINT) and comments with a specific word. The problem is when I want to search with serial numbers.
There are two issues here. One is that multiple pieces of equipment can be attached to a specific ticket and also a single piece of equipment can be associated with multiple tickets. When I query the table used to associate equipment to tickets (VIEW_WT_EQUIP, using the view because it is where the serial number is seen) I potential get multiple results with the same Ticket_ID.
This is the query that I have right now, but it returns no results when I put in a serial number that I know is in the system.
var query = from a in db.VIEW_WT_HEADERs
join c in db.VIEW_WT_EQUIPs on a.TICKET_ID equals c.TICKET_NUMBER into c_group
from c2 in c_group
join b in db.WT_EVENTs on a.TICKET_ID equals b.TICKET_ID
where b.COMMENTS.Contains(input) || a.CHIEF_COMPLAINT.Contains(input) || c2.SERIAL_NUMBER.Contains(input)
orderby a.TICKET_ID descending
select new { a.TICKET_ID, a.ENTRY_DATE, a.CONTACT, a.CHIEF_COMPLAINT, a.STATUS };
I also tried a method where I used 2 linq queries and put all the ticket numbers from a serial number search into a list, but the second query didn't like that I was trying to compare an int array.
I think I am just going about this wrong. Join is probably not the right way to do this, but I don't know how to tell the main query to pull all the tickets associated with a piece of equipment.
Please let me know where I can clarify, because I know this explination is rough.
I would put this as comment instead of an answer, but I want to show you some code, so I had to choose "answer".
If you are using Linq to Entities, you probably have a relationship between the objects. It means that the join is not necessary. You should only use join when no navigation property is available.
I can't tell exactly what you should do, but here is some code the might be helpful:
var query = from a in db.VIEW_WT_HEADERs
from b in a.WT_EVENTs
from c in a.VIEW_WT_EQUIPs
where b.COMMENTS.Contains(input) || a.CHIEF_COMPLAINT.Contains(input) ...
orderby a.TICKET_ID descending
select new { a.TICKET_ID, a.ENTRY_DATE, a.CONTACT, a.CHIEF_COMPLAINT, a.STATUS };
you can also use let to store a sub-expression:
var query = from a in db.VIEW_WT_HEADERs
from b in a.WT_EVENTs
from c in a.VIEW_WT_EQUIPs
let x = c.FirstOrDefault()
where b.COMMENTS.Contains(input) || a.CHIEF_COMPLAINT.Contains(input) || x.SomeProperty ....
orderby a.TICKET_ID descending
select new { a.TICKET_ID, a.ENTRY_DATE, a.CONTACT, a.CHIEF_COMPLAINT, a.STATUS };
Those are just example, maybe it helps!
In a one to many relationship situation which of the following has better performance.
1st approach
public Order GetOrder(long orderId) {
var orderDetails =
(from o in Orders
from d in OrderDetails
where d.OrderId = o.Id && o.Id = orderId
select new {
Order = o,
Detail = d
}).ToList();
var order = orderDetails.First().Order;
order.Details = orderDetails.Select(od => od.Detail).ToList();
return order;
}
2nd approach
public Order GetOrder(long orderId) {
var order = Orders.First(o => o.Id == orderId);
order.Details = OrderDetails.Where(od => od.OrderId = orderId).ToList();
return order;
}
The point I am trying to figure out (in terms of performance) is, in first approach there is single query but repeated data is being selected where, in second approach, there are two seperate queries but selecting only the data that is enough.
You can assume Orders and OrderDetails are IQueryable<T> of EntityFramework (dbContext.Set<T>()) or NHibernate (session.Query<T>()). I tried with both and they create very similar sql queries. Also as far as I know, these ORM's built in one to many queries use something like the first approach.
UPDATE, to clarify what I am asking: Which one (single query but repeated data or only required data but multiple queries) performs better under which circumstances? There may be many situations that I may not think of. That's why I am not trying benchmarking. As already stated in some answers column count or more joins were the kinds of answers that I expected. (There may be also something about row count of table and/or result set). Based on these kind of answers I may try benchmarking. And of course I am asking why? I am not trying to solve Order - OrderDetail problem or solve anything at all. I am trying to learn and understand when to use single query but repeated data or only required data but multiple queries.
A single one-to-many query is pretty straightforward for ORMs. It's when you need to make several interrelated one-to-many queries that performance considerations start making themselves known.
always measure performance for your particular case. if order table has few-small sized columns, getting all data in one round trip may be better. if order tables has too many or blob columns, issuing 2 seperate queries may outperform.
Using the EntityFramework, you should either call Include on the context
var order = context.Orders.Include(x => x.Details).First(x => x.Id == orderId);
Loading Related Objects
I'm looking into an issue that is related to...
Join and Include in Entity Framework
Basically the following query returns the list of "Property" objects the current user has permissions ( ACLs ) to view.
IQueryable<Property> currPropList
= from p in ve.Property
.Include("phyAddress")
.Include("Contact")
from a in ve.ACLs
from u in ve.Account
from gj in ve.ObjectGroupJoin
where u.username == currUsername // The username
&& (a.Account.id == u.id // The ACLs
&& a.objType == (int)ObjectType.Group)
&& (gj.ObjectGroup.id == a.objId // The groups
&& gj.objId == p.id) // The properties
select p;
The query returns the correct list of properties and in large works fine.
But the "Include" calls in the linq query above does not load the objects. If I call "Load()" explicitly after the LINQ query then the objects load.
The related SO question suggested that there may be a conflict between the "Include" call and the where clause. How can that be the case?
But at any rate, how can I restructure this query to load the "phyAddress" and "Contract" members? Specifically, I'd only like to load the members on returned objects, not all the "phyAddress" and "Contact" objects in the database.
Thanks.
Edit
I've tracked down the issue to the use of multiple from clauses
This works...
IQueryable<Property> currPropList
= from p in ve.Property
.Include("phyAddress")
select p;
And the "phyAddress" member is loaded.
But this doesn't work...
IQueryable<Property> currPropList
= from p in ve.Property
.Include("phyAddress")
from a in ve.ACLs
select p;
Basically the Include call is ignored when there are multiple from clauses. Does anyone know of a work around for this?
Edit 2
One workaround is to cast the IQueryable result as a ObjectQuery and get the include off of that. But I would like to prevent the second roundtrip to the database I am assuming this causes.
Eg. This works....
IQueryable<Property> currPropList
= ((from p in ve.Property
from a in ve.ACLs
select p) as ObjectQuery<Property>).Include("phyAddress");
Is there a way to do this with only a single query?
Edit 3
No second query because of deferred execution [ http://blogs.msdn.com/charlie/archive/2007/12/09/deferred-execution.aspx. So edit 2 would be the solution.
This is a known issue with Include... if you do something that changes the shape of the query (i.e. from from) then the Include is lost there are simple enough workarounds though:
you can wrap the include around the query, see Tip 22 - How to make include really include.
or you can get everything you need in the select clause and let relationship fixup do the job for you. i.e.
var x = from p in ve.Property
from a in ve.ACLs
select new {p,p.phyAddress};
var results = x.AsEnumerable().Select(p => p.p);
Now results is an enumeration of property entities, but each one has it's phyAddress loaded, as a side-effect of the initial request for the phyAddress, and EF's relationship fixup.