Using linq to entities i am connecting to a database, the database has tables in it that has payments that have a multi to multi relationship with jobs. This is acheived via an allocs table. I want a list box with all the jobs that has a column called due price which takes all of the allocations of payments for this job and takes that away from the job price. However, using the below linq to entities statement. The problem is that if the job has no allocations it returns null and therefore the due payment is empty. What i really want is for the due payment to be the job price if there are no allocations however, i cannot think of a way around this. Please help before i finally go insane :-(
var jobs = from j in data.jobs
where j.property.customer.id == customerid
&& j.completed != null
select new
{
j.id,
j.price,
dueprice = j.price - ( from a in data.allocs
where a.job.id == j.id
select a.amount ).Sum(),
lineone = j.property.lineone,
postcode = j.property.postcode,
jobtype = j.jobtype.name,
j.completed
};
You can also use the Null Coalescing operator to simplify code like this:
dueprice = j.price - (( from a in data.allocs
where a.job.id == j.id
select a.amount ).Sum() ?? 0)
This operator returns the first value which is not null, so myNum ?? 0 will return myNum if it is not null, and if it is null the operator will return 0.
select a.amount ).Concat(/* array with one zero element */).Sum()
I actually found an answer my self, i will try oleg's as well as it seems to be a little more precise but i thought of doing this
dueprice = j.price - (( from a in data.allocs
where a.job.id == j.id
select a.amount ).Sum())==null ? j.price:
//sets the due price to the normal sum if it has allocs
j.price - ( from a in data.allocs
where a.job.id == j.id
select a.amount ).Sum(),
however i like the idea of only adding on more line of code instead of my several(plus it's repeated code) i will try this and let you all know. Thanks for the response.
Related
I'm trying to do something like this in entity framework.
SELECT (SELECT COUNT(*) FROM Log Where LogTypeId =1) as Log1,
(SELECT COUNT(*) FROM Log Where LogTypeId =2) as Log2
Note the lack of from. In essence, I just want to select a single row that contains aggregation of the Log table. I tried something like this.
(from x in ctx.Logs
select new {
Log1 = ctx.Logs.Count((x) => x.LogTypeId == 1),
Log2 = ctx.Logs.Count((x) => x.LogTypeId == 2)
}).First();
However, this returns a null if ctx.Logs doesn't contain any row (new system, or recently archived). While it's not that difficult check for a null return and simply create a blank entry, I want to know if there's a way to handle it so I don't have to do "If null create a blank aggregate with all field set to 0" everywhere I want to create this query (since I will have several similar style of select in multiple places).
I also want to avoid having to do multiple select, to prevent multiple round trips to the database.
Okay, feel a bit silly and figured out that I can use DefaultIfEmpty.
(from x in ctx.Logs.DefaultIfEmpty()
select new {
Log1 = ctx.Logs.Count((x) => x.LogTypeId == 1),
Log2 = ctx.Logs.Count((x) => x.LogTypeId == 2)
}).First();
To avoid null value you need to use one on the null handling function:
!.nvl
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions105.htm
2.nvl2
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions106.htm
3.coalesce
https://docs.oracle.com/cd/B28359_01/server.111/b28286/functions023.htm#SQLRF00617
Usage:
nvl(yourcolumn,0)
try like this in your code or google for these functions i hope you get some help from my side.
I have FinancialCompliances and Compliance tables. Below my query work flow is get one latest row .The problem is i have empty values in table because i deleted all rows in my table .But my below query is return one old rows .
var Compliance = (from c in datamodel.Compliances
join f in datamodel.FinancialCompliances on c.ComplianceId equals f.ComplianceId
where (c.VerifierId == userId || c.OwnerId == userId || c.UserId == userId) && (f.ComplianceId == c.ComplianceId)
orderby (f.AddedDate)
select f);
financialCompliance = Compliance.ToList().LastOrDefault();
What is the problem?
It sounds like you may be deleting your objects in the datamodel instance but not saving the changes and resetting the datamodel, thereby keeping the old records still in the context even if they aren't in the database. To be safe, try using a new context for this query.
Also, you may want to consider modifying the query to order the results decending and then selecting the top one rather than ordering them ascending and taking only the last one:
var Compliance = (from c in datamodel.Compliances
join f in datamodel.FinancialCompliances on c.ComplianceId equals f.ComplianceId
where (c.VerifierId == userId || c.OwnerId == userId || c.UserId == userId) && (f.ComplianceId == c.ComplianceId)
orderby (f.AddedDate) descending
select f);
financialCompliance = Compliance.FirstOrDefault();
Perhaps lastordefalt are sending the default value. Could you please confirm that you actually have real data in your object that is returned? I doubt that would be the case.
One obvious problem with your code is that you are calling ToList() before LastOrDefault(). This will cause your code to load all data from your storage into your application and context, and then from the result retrieving the last object. I suspect that this may cause some problems.
Try to skip your .ToList() and call LastOrDefault() directly.
The Any() linq function seems to load all of the entity's columns even though they're not needed.
The following code:
if(Session.Query<Project>().Any(p=>p.ID == projectID && p.ProjectOwner.Responsible == CurrentUserID))
// Current user is the responsible for this project
Generates the following SQL:
select TOP (1) project0_.ProjectID as ProjectID7_,
project0_.DateCreated as DateCrea2_7_,
project0_.Type as Type7_,
project0_.ProjectOwner_FK as ProjectOy8_7_,
project0_.Address_FK as Address9_7_,
**[Snip -- the statement selects all of Project's columns]**
from [Project] project0_
inner join [OrganizationProject] organizati1_
on project0_.ProjectOwner_FK = organizati1_.OrganizationProjectID
where project0_.ProjectID = 1 /* #p0 */
and organizati1_.Responsible_FK = 1 /* #p1 */
However, the following code:
if(Context.Projects.Where(p=>p.ID == projectID && p.ProjectOwner.Responsible == CurrentUserID).Count() == 1)
// Current user is the responsible for this project
Generates the following sql, which is what is expected:
select cast(count(*) as INT) as col_0_0_
from [Project] project0_
inner join [OrganizationProject] organizati1_
on project0_.ProjectOwner_FK = organizati1_.OrganizationProjectID
where project0_.ProjectID = 1 /* #p0 */
and organizati1_.Responsible_FK = 1 /* #p1 */
The Count() method does what is expected, but it is a bit less straightforward.
Is the Any() behavior considered normal or is it a bug? It doesn't seem optimal to me, but maybe loading the entity isn't really slower than asking SQL to return the count?
In light of this, what is considered to be the best way to test a condition in Linq to NHibernate?
It was interesting but did you tryed:
if(Session.Query<Project>()
.FirstOrDefault(p=>p.ID == projectID
&& p.ProjectOwner.Responsible == CurrentUserID) != null)
I think it would be faster from your current options. In fact it doesn't checks for all items (like count) Also it doesn't fetch all data.
Something like this should create a query with only a single column:
if(Session
.Query<Project>()
.Where(p=>p.ID == projectID && p.ProjectOwner.Responsible == CurrentUserID)
.Select(p=>new { pID = p.ID })
.Any())
{
...
}
Projection to an anonymous type allows NHibernate to fetch only the specified column (ID, for example).
But it is usually more suitable when there is a significant number of rows to retrieve.
Using LINQ to MySQL
MySQL TABLE Definition
ID binary(16) PK
UtcTriggerTime datetime NOT NULL
PersonID binary(16) NOT NULL FK
Status int(11) NOT NULL
I have a array of 1000s of PersonIDs(Guids) and for each of the PersonID I would like to pick matching records from the table with the following criteria:
UtcTriggerTime >= PREDEFINED_DATE_TIME (e.g. UtcNow - 30days)
AND
Status=1 OR Status=2
I am currently using a
foreach(var personID in personIDsArray){
var qryResult = (from a in AlertObjects.AlertsTriggered
where a.PersonID == personID &&
(a.Status == 1 || a.Status == 2) &&
a.UtcTriggerTime >= PREDEFINED_DATE_TIME
select a).ToArray();
}
What are the possible options to optimise this for performance? Or is there?
I tried putting an Index on (UtcTriggerTime, PersonID, Status) and then used the array of PersonIDs to do it in one query as follows, but it was even slower which when I thought about makes sense:
var qryResult = (from a in AlertObjects.AlertsTriggered
where personIDsArray.Contains(a.PersonID) &&
(a.Status == 1 || a.Status == 2) &&
a.UtcTimeTriggered >= PREDEFINED_DATE_TIME
group a by a.PersonID into alerts
select alerts).ToArray();
It seems to me that you are dealing with typical Select N+1 problem which is caused by
group a by a.PersonID into alerts
select alerts
part.
Could you look in the generated SQL and see what it looks like?
Also there is no need to put Status in index if there not many values because the increase on the performance will be minimal.
If my quess is right you can look on these question to see how the problem can be handled:
How to Detect Select n+1 problems in Linq to SQL?
http://www.west-wind.com/weblog/posts/2009/Oct/12/LINQ-to-SQL-Lazy-Loading-and-Prefetching
I'm not familiar with MySql but it seems to me that this link could be helpful in diagnosing which queries are slow.
http://www.electrictoolbox.com/show-running-queries-mysql/
I have following query, and I am converting it to LINQ.
select acq.ACQPub as Prev_ACQPub
, ve.CompanyID
, ve.EntityID
, ve.RoundID
, ve.EntityName
, ve.Date
, ve.RoundTypeCode
, ve.EventType
, ve.PostValue_DJVS
, ve.PostVal
, ve.PreVal
, fin.FinanceStat
from ValuationEvents_PIT_New as ve
left join Acq_PublicDummy as acq
on ve.EntityID = acq.EntityID
left join FinStat_New as fin
on ve.EntityID = fin.EntityID
where ve.EventType in('ACQ','LBO')
and acq.ACQPub is null
I wanted to double check if I've done it right or there is a better way of doing it.
Here is my code:
return (from ve in valuationEvents where ve.EventType == EventTypes.Acq || ve.EventType == EventTypes.Lbo
join acq in acqPublicDummies on ve.EntityId equals acq.EntityID into veAcq
from x in veAcq.DefaultIfEmpty() where x != null && x.ACQPub == null
join fin in finStats on ve.EntityId equals fin.EntityID into veFin
from y in veFin.DefaultIfEmpty()
select new AcqResearch
{ PrevAcqPub = x == null ? null : x.ACQPub,
EntityId = ve.EntityId,
CompanyId = ve.CompanyId,
RoundId = ve.RoundId,
Date = ve.Date,
RoundTypeCode = ve.RoundTypeCode,
EventType = ve.EventType.ToString(),
PostValueDjvs = ve.PostMoneyValue,
PostVal = ve.PostVal,
PreVal = ve.PreVal,
FinanceStat = y == null ? null : y.FinanceStat
}).ToList();
Since result will be used > 1 times I am returning List instead of IEnumerable.
Also I can't run SQL and compare result of it with LINQ result, since query above runs against of Raw data and LINQ is running after data calculations and additional cleansing process. So there is no way for me to compare query result with my LINQ result. I just need to rely that logic is correct. What is the same is SQL logic and LINQ logic.
Thank you very much for your help and feedback!
If you want to verify that the query is the same, you can look at the SQL that is generated from the linq. there are a few ways of doing this:
Use SQL Profiler
Paste the query into LinqPad and view the SQL tab
Set the Log property on your datacontext
You can then compare the two SQL queries and check for differences.
As an aside, in your case the only things I would change are style related - I would move all of the where clauses to just above the select clause, so it is easier to see what filter you are applying. Also, the line
PrevAcqPub = x == null ? null : x.ACQPub
Seems like it may as well be
PrevAcqPub = null
Since you have already filtered out all x that are null and that have x.ACQPub != null (Same goes for the SQL query).
Your solution looks correct to me