EF Core FromSql Returning Odd Error Message - entity-framework

This code throws an exception when rawVoters.Count() is called:
string sql = #"select * from Voters as v
inner join Homes as h on v.HomeID = h.ID
inner join Locations as l on h.LocationID = l.ID
inner join Streets as s on l.StreetID = s.ID
inner join Cities as c on s.CityID = c.ID
inner join VoterAgencies as va on v.CountyID = va.CountyID and v.VoterID = va.VoterID
where (va.AgencyID = #agencyID)
and (c.Name like '%' + #city + '%')
and (v.FirstName like '%' + #firstName + '%')
and (v.LastName like '%' + #lastName + '%')
and (s.Name like '%' + #street + '%')
and ((#voterID = 0) or (v.VoterID = #voterID))";
List<SqlParameter> parameters = new List<SqlParameter>();
parameters.Add( new SqlParameter( "#agencyID", agencyID ) );
parameters.Add( new SqlParameter( "#city", model.City ) );
parameters.Add( new SqlParameter( "#firstName", model.FirstName ) );
parameters.Add( new SqlParameter( "#lastName", model.LastName ) );
parameters.Add( new SqlParameter( "#street", model.Street ) );
parameters.Add( new SqlParameter( "#voterID", model.VoterID ) );
IQueryable<Voter> rawVoters = _context.Voters
.AsNoTracking()
.FromSql( sql, parameters.ToArray() );
int numVoters = 0;
try
{
numVoters = rawVoters.Count();
}
catch( Exception e )
{
int i = 9;
i++;
}
The error message is:
"The column 'ID' was specified multiple times for 'v'."
I thought this might be because EF Core doesn't like the "as x" phrasing, so I substituted the table names for each of the identifiers...and got the same error message.
I'm curious as to what's going on here.

The problem was that the T-SQL was returning all fields (select *). Under EF Core, the returned fields must match the fields specified for the entity being returned, in this case Voter.
An inner join, like the one I was using, by default returns far more than just the Voter fields.
Changing the SQL to be select v.* (where v is the alias for Voters) solved the problem.

Hence you're just getting the Count,you can specify your column name as shown below.Then no issues :)
string sql = #"select v.ID from Voters as v

Related

converting sql statement back to lambda expression

I have the query below, and its sql code. It's running really slow, so it was re written in sql, now I'm just not sure how to convert the sql back to a lambda expression.
This is the part of the expression giving me the problems, somewhere in
r.RecordProducts.Any()
records = records
.Include(r => r.Employer)
.Include(r => r.Contractor)
.Include(r => r.RecordProducts)
.ThenInclude(rp => rp.ProductDefendant.Defendant)
.Where(r => EF.Functions.Like(r.Employer.DefendantCode, "%" + input.DefendantCode + "%")
|| EF.Functions.Like(r.Contractor.DefendantCode, "%" + input.DefendantCode + "%")
|| r.RecordProducts.Any(rp => EF.Functions.Like(rp.ProductDefendant.Defendant.DefendantCode, "%" + input.DefendantCode + "%") && rp.IsActive == true));
the any clause does an exist and some funky stuff in the sql where clause below
SELECT [t].[Id], [t].[StartDate], [t].[EndDate], [t].[WitnessName], [t].[SourceCode], [t].[JobsiteName], [t].[ShipName], [t].[EmployerCode]
FROM (
SELECT DISTINCT [r].[RecordID] AS [Id], [r].[StartDate], [r].[EndDate], [r.Witness].[FullName] AS [WitnessName], CASE
WHEN [r].[SourceID] IS NOT NULL
THEN [r.Source].[SourceCode] ELSE N'zzzzz'
END AS [SourceCode], CASE
WHEN [r].[JobsiteID] IS NOT NULL
THEN [r.Jobsite].[JobsiteName] ELSE N'zzzzz'
END AS [JobsiteName], CASE
WHEN [r].[ShipID] IS NOT NULL
THEN [r.Ship].[ShipName] ELSE N'zzzzz'
END AS [ShipName], CASE
WHEN [r].[EmployerID] IS NOT NULL
THEN [r.Employer].[DefendantCode] ELSE N'zzzzz'
END AS [EmployerCode]
FROM [Records] AS [r]
LEFT JOIN [Ships] AS [r.Ship] ON [r].[ShipID] = [r.Ship].[ShipID]
LEFT JOIN [Jobsites] AS [r.Jobsite] ON [r].[JobsiteID] = [r.Jobsite].[JobsiteID]
LEFT JOIN [Sources] AS [r.Source] ON [r].[SourceID] = [r.Source].[SourceID]
LEFT JOIN [Witnesses] AS [r.Witness] ON [r].[WitnessID] = [r.Witness].[WitnessID]
LEFT JOIN [Defendants] AS [r.Contractor] ON [r].[ContractorID] = [r.Contractor].[DefendantID]
LEFT JOIN [Defendants] AS [r.Employer] ON [r].[EmployerID] = [r.Employer].[DefendantID]
WHERE ([r].[IsActive] = 1) AND (([r.Employer].[DefendantCode] LIKE (N'%' + 'cert') + N'%' OR [r.Contractor].[DefendantCode] LIKE (N'%' + 'cert') + N'%') OR EXISTS (
SELECT 1
FROM [Records_Products] AS [rp]
INNER JOIN [Product_Defendant] AS [rp.ProductDefendant] ON [rp].[DefendantProductID] = [rp.ProductDefendant].[DefendantProductID]
INNER JOIN [Defendants] AS [rp.ProductDefendant.Defendant] ON [rp.ProductDefendant].[DefendantID] = [rp.ProductDefendant.Defendant].[DefendantID]
WHERE ([rp.ProductDefendant.Defendant].[DefendantCode] LIKE (N'%' + 'cert') + N'%' AND ([rp].[IsActive] = 1)) AND ([r].[RecordID] = [rp].[RecordID])))
) AS [t]
ORDER BY [t].[SourceCode]
OFFSET 0 ROWS FETCH NEXT 500 ROWS ONLY
Here is the new sql that works better, just not sure how to convert it back to a lambda expression
SELECT [t].[Id]
,[t].[StartDate]
,[t].[EndDate]
,[t].[WitnessName]
,[t].[SourceCode]
,[t].[JobsiteName]
,[t].[ShipName]
,[t].[EmployerCode]
FROM (
SELECT DISTINCT [r].[RecordID] AS [Id]
,[r].[StartDate]
,[r].[EndDate]
,[r.Witness].[FullName] AS [WitnessName]
,CASE
WHEN [r].[SourceID] IS NOT NULL
THEN [r.Source].[SourceCode]
ELSE N'zzzzz'
END AS [SourceCode]
,CASE
WHEN [r].[JobsiteID] IS NOT NULL
THEN [r.Jobsite].[JobsiteName]
ELSE N'zzzzz'
END AS [JobsiteName]
,CASE
WHEN [r].[ShipID] IS NOT NULL
THEN [r.Ship].[ShipName]
ELSE N'zzzzz'
END AS [ShipName]
,CASE
WHEN [r].[EmployerID] IS NOT NULL
THEN [r.Employer].[DefendantCode]
ELSE N'zzzzz'
END AS [EmployerCode]
FROM [Records] AS [r]
LEFT JOIN [Ships] AS [r.Ship] ON [r].[ShipID] = [r.Ship].[ShipID]
LEFT JOIN [Jobsites] AS [r.Jobsite] ON [r].[JobsiteID] = [r.Jobsite].[JobsiteID]
LEFT JOIN [Sources] AS [r.Source] ON [r].[SourceID] = [r.Source].[SourceID]
LEFT JOIN [Witnesses] AS [r.Witness] ON [r].[WitnessID] = [r.Witness].[WitnessID]
LEFT JOIN [Defendants] AS [r.Contractor] ON [r].[ContractorID] = [r.Contractor].[DefendantID]
LEFT JOIN [Defendants] AS [r.Employer] ON [r].[EmployerID] = [r.Employer].[DefendantID]
LEFT JOIN (
SELECT [rp].[RecordID]
FROM [Records_Products] AS [rp]
INNER JOIN [Product_Defendant] AS [rp.ProductDefendant] ON [rp].[DefendantProductID] = [rp.ProductDefendant].[DefendantProductID]
INNER JOIN [Defendants] AS [rp.ProductDefendant.Defendant] ON [rp.ProductDefendant].[DefendantID] = [rp.ProductDefendant.Defendant].[DefendantID]
WHERE (
[rp.ProductDefendant.Defendant].[DefendantCode] LIKE (N'%' + 'cert') + N'%'
AND ([rp].[IsActive] = 1)
)
) AS RecordProduct ON [r].[RecordID] = RecordProduct.[RecordID]
WHERE ([r].[IsActive] = 1)
AND (
(
[r.Employer].[DefendantCode] LIKE (N'%' + 'cert') + N'%'
OR [r.Contractor].[DefendantCode] LIKE (N'%' + 'cert') + N'%'
)
OR RecordProduct.RecordID IS NOT NULL --OR EXISTS ( -- SELECT 1 -- FROM [Records_Products] AS [rp] -- INNER JOIN [Product_Defendant] AS [rp.ProductDefendant] ON [rp].[DefendantProductID] = [rp.ProductDefendant].[DefendantProductID] -- INNER JOIN [Defendants] AS [rp.ProductDefendant.Defendant] ON [rp.ProductDefendant].[DefendantID] = [rp.ProductDefendant.Defendant].[DefendantID] -- WHERE ([rp.ProductDefendant.Defendant].[DefendantCode] LIKE (N'%' + 'cert') + N'%' -- AND ([rp].[IsActive] = 1)) AND ([r].[RecordID] = [rp].[RecordID]) -- ) )) AS [t]ORDER BY [t].[SourceCode]OFFSET 0 ROWS FETCH NEXT 500 ROWS ONLY
)
)
The linq expression you supplied and the SQL generated do not match. For one, the linq expression is performing an Include on the various related tables which would have included all of those entity columns in the top-level SELECT which are not present in your example SQL. I also don't see conditions in the Linq expression for the Take 500 & OrderBy, or IsActive assertion on Record.
To be able to help determine the source of any performance concern we need to see the complete Linq expression and the resulting SQL.
Looking at the basis of the Linq expression you provided:
records = records
.Include(r => r.Employer)
.Include(r => r.Contractor)
.Include(r => r.RecordProducts)
.ThenInclude(rp => rp.ProductDefendant.Defendant)
.Where(r => EF.Functions.Like(r.Employer.DefendantCode, "%" + input.DefendantCode + "%")
|| EF.Functions.Like(r.Contractor.DefendantCode, "%" + input.DefendantCode + "%")
|| r.RecordProducts.Any(rp => EF.Functions.Like(rp.ProductDefendant.Defendant.DefendantCode, "%" + input.DefendantCode + "%") && rp.IsActive == true));
There are a few suggestions I can make:
There is no need for the Functions.Like. You should be able to achieve the same with Contains.
Avoid using Include and instead utilize Select to retrieve the columns from the resulting structure that you actually need. Populate these into ViewModels or consume them in the code. The less data you pull back, the better optimized the SQL can be for indexing, and the less data pulled across the wire. Consuming entities also leads to unexpected lazy-load scenarios as systems mature and someone forgets to Include a new relation.
.
records = records
.Where(r => r.IsActive
&& (r.Employer.DefendantCode.Contains(input.DefendantCode)
|| r.Contractor.DefendantCode.Contains(input.DefendantCode)
|| r.RecordProducts.Any(rp => rp.IsActive
&& rp.ProductDefendant.Defendant.DefendantCode.Contains(input.DefendantCode))
.OrderBy(r => r.SourceCode)
.Select(r => new RecordViewModel
{
// Populate the data you want here.
}).Take(500).ToList();
This also adds the IsActive check, OrderBy, and Take(500) based on your sample SQL.

ExecuteSQL Concatenate string and integer

I am using trying to populate a dropdown using magicVaueList hack and ExecuteSQl and I am having trouble with joining a text and number.
Here is my code:
MVL_Dropdown ( ExecuteSQL ( "select L.Product + GetAsText( L.Quantity )
from T08_ESTIMATES E
join T09_EST_LINE_ITEMS L on E.ID_Estimate = L.id_estimate
where E.ID_Estimate = ? "; ""; ""; T19_TASKS::preitem ) )
The trouble I get is with the + GetAsText( L.Quantity ) where I have also tried & GetAsText( L.Quantity ) will no results but if I was to remov ethe join like:
MVL_Dropdown ( ExecuteSQL ( "select L.Product
from T08_ESTIMATES E
join T09_EST_LINE_ITEMS L on E.ID_Estimate = L.id_estimate
where E.ID_Estimate = ? "; ""; ""; T19_TASKS::preitem ) )
Then it works minus the fact that I will need both values. I'm therefore certain the problem exists Concatenating my text and number but I'm quite new to FileMaker and not sure what to use to get it to work.
.
Any help appreciated.
For anyone wondering i had to use a double pipe like so:
L.Product || ' - ' || L.Quantity
MVL_Dropdown ( ExecuteSQL ( "SELECT L.Product || ' - ' || L.Quantity || ' off'
FROM T08_ESTIMATES E
JOIN T09_EST_LINE_ITEMS L ON E.ID_Estimate = L.id_estimate
WHERE E.ID_Estimate = ? "; ""; ""; T19_TASKS::preitem ) )
STRVAL - Converts a value of any type to a character string
Here I wanted to concatenate an integer "Quantity" and string "Units":
ExecuteSQL ( "
SELECT p.Description, STRVAL ( m.Quantity ) || p.Units
FROM JobMaterials m
JOIN Products p
ON m.kf_Product = p.kp_ProductID
WHERE m.kf_JobID = ?
" ;
"¦" ; "¶" ;
Jobs::kp_JobID
)

Variable "Rs.Temp" not found

I'm getting error while executing a sql query in Alpha Anywhere Query (AlphaADO) section.
The code i'm executing is attached.
SELECT * FROM
(SELECT
tbl_wo_kitting.wo_kitting_id,
tbl_wo_kitting.level_no,
tbl_wo_kitting.line_no_parent,
tbl_wo_kitting.line_no AS kitting_line_no,
tbl_wo_kitting.production_type,
tbl_wo_kitting.quantity AS kitting_quantity,
tbl_wo_kitting.release_to_wr,
tbl_wo_kitting.mpn,
m_product.m_product_id,
m_product.value,
m_product.name,
m_product.storelocator,
tbl_wo_project.wo_project_id,
tbl_wo_project.line_no AS project_line_no,
tbl_wo_project.quantity AS project_quantity,
m_transaction_moveqty_v.qtyonhand AS qtyonhand,
tbl_work_order.work_order_id,
tbl_work_order.reference_key,
case
when tbl_wo_kitting.level_no, = 'P+'
then '0'
else tbl_wo_kitting.level_no,
end as linenumber,
case
when tbl_wo_kitting.line_no_parent = 'PT+'
then '0'
else tbl_wo_kitting.line_no_parent
end as linenoparent
FROM (tbl_wo_kitting tbl_wo_kitting
INNER JOIN (m_product m_product
INNER JOIN m_transaction_moveqty_v m_transaction_moveqty_v
ON m_product.m_product_id = m_transaction_moveqty_v.m_product_id )
ON tbl_wo_kitting.m_product_id = m_product.m_product_id
INNER JOIN (tbl_wo_project tbl_wo_project
INNER JOIN tbl_work_order tbl_work_order
ON tbl_wo_project.work_order_id = tbl_work_order.work_order_id )
ON tbl_wo_kitting.wo_project_id = tbl_wo_project.wo_project_id )) AS TABLE1
ORDER BY tbl_wo_kitting.level_no, (string_to_array(linenoparent, '.'))::int[],(string_to_array(linenumber, '.'))::int[]
Any idea? This is selecting from PostgreSQL.
Thank you very much in advance.

Request report with parameters

I want to create a report with JasperReports with multiple parameters, the report is generated correctly when the user passes all the parameters but nothing is generated when one parameter is missed i use this request
SELECT
t.*,
u."name" AS username,
c."name" AS componentName,
s."designation" AS statusName,
pr."name" AS priorityName,
p."name" AS projectName
FROM
"component" c INNER JOIN "ticket" t ON c."id" = t."component_id"
INNER JOIN "personne" u ON t."personne_id" = u."id"
INNER JOIN "status" s ON t."status_id" = s."id"
INNER JOIN "priority" pr ON t."priority_id" = pr."id"
INNER JOIN "project" p ON c."project_id" = p."id"
WHERE
pr.name = $P{priority}
and u.login = $P{userLogin}
and s.designation = $P{status}
and t.creation_date between $P{start} and $P{end}
and c.name = $P{componenet}
Please can you help me to generate the report even where there is one parameter missed?
do you want outer joins? or you may choose a structure like this:
and ( u.login = $P{userLogin} or $P{userLogin} is null )
for each line
You can set the default values for every parameter or use this query:-
SELECT
t.*,
u."name" AS username,
c."name" AS componentName,
s."designation" AS statusName,
pr."name" AS priorityName,
p."name" AS projectName
FROM
"component" c INNER JOIN "ticket" t ON c."id" = t."component_id"
INNER JOIN "personne" u ON t."personne_id" = u."id"
INNER JOIN "status" s ON t."status_id" = s."id"
INNER JOIN "priority" pr ON t."priority_id" = pr."id"
INNER JOIN "project" p ON c."project_id" = p."id"
WHERE
(pr.name = $P{priority} or $P{priority} is null)
and (u.login = $P{userLogin} or $P{userLogin} is null)
and (s.designation = $P{status} or $P{status} is null )
and t.creation_date between $P{start} and $P{end}
and (c.name = $P{componenet} or $P{componenet} is null)
You can control the where clause in java class before generate the report.
1.In java u can check whether the parameter is null or not, then do small validation such as
StringBuffer sb = new StringBuffer();
if(StringUtils.isNotEmpty(priority)){
sb.append(" AND pr.name = "+priority);
}
2.do for every other condition for u.login, s.designation
3.after that u can pass reportParam.put("sqlQuery", sb.toString());
4.the in ireport simply change your query
WHERE
pr.name = $P{priority}
and u.login = $P{userLogin}
and s.designation = $P{status}
and t.creation_date between $P{start} and $P{end}
and c.name = $P{componenet}
to
WHERE 1=1 $P!{sqlQuery}
now you dont need to worry about the null, since it will ignore the condition in java class.

Can some one help me to conver it into LINQ i m using Entity Framework

select ForumCategories.ID , ForumCategories.Title , ForumCategories.DateCreated,
CO = ( select COUNT(*) from ForumSubCategories where ForumSubCategories.CategoryID_FK = ForumCategories.ID)
from ForumCategories
var q = from fc in Context.ForumCategories
select new
{
Id = fc.ID,
Title = fc.Title,
DateCreated = fc.DateCreated
CO = fc.ForumSubCategories.Count()
};
return q;
The "join" (subquery) is implicit; it's defined in the relationship between ForumCategories and ForumSubCategories in your model. Using this syntax, the call to Count() will be done on the DB server.