I am fairly new to LINQ and can't get my head around some inconsistency in behaviour. Any knowledgeable input would be much appreciated. I see similar issues on SO and elsewhere but they don't seem to help.
I have a very simple setup - a company table and an addresses table. Each company can have 0 or more addresses, and if > 0 one must be specified as the main address. I'm trying to handle the cases where there are 0 addresses, using an outer join and altering the select statement accordingly.
Please note I'm currently binding the output straight to a GridView so I would like to keep all processing within the query.
The following DOES work
IQueryable query =
from comp in context.Companies
join addr in context.Addresses on comp.CompanyID equals addr.CompanyID into outer // outer join companies to addresses table to include companies with no address
from addr in outer.DefaultIfEmpty()
where (addr.IsMain == null ? true : addr.IsMain) == true // if a company has no address ensure it is not ruled out by the IsMain condition - default to true if null
select new {
comp.CompanyID,
comp.Name,
AddressID = (addr.AddressID == null ? -1 : addr.AddressID), // use -1 to represent a company that has no addresses
MainAddress = String.Format("{0}, {1}, {2} {3} ({4})", addr.Address1, addr.City, addr.Region, addr.PostalCode, addr.Country)
};
but this displays an empty address in the GridView as ", , ()"
So I updated the MainAddress field to be
MainAddress = (addr.AddressID == null ? "" : String.Format("{0}, {1}, {2} {3} ({4})", addr.Address1, addr.City, addr.Region, addr.PostalCode, addr.Country))
and now I'm getting the Could not translate expression error and a bunch of spewey auto-generated code in the error which means very little to me.
The condition I added to MainAddress is no different to the working condition on AddressID, so can anybody tell me what's going on here?
Any help is greatly appreciated.
The error you are getting is telling you that LinqToSql cannot translate your null check and then string.Format expression into SQL. If you look at the SQL your first query is generating (using either LinqPad or SQL Profiler), you'll see something like:
SELECT [t0].[CompanyID], [t0].[Name],
(CASE
WHEN [t1].[AddressID] IS NULL THEN #p0
ELSE [t1].[AddressID]
END) AS [AddressID],
[t1].[Address1] AS [value],
[t1].[City] AS [value2],
[t1].[Region] AS [value3],
[t1].[PostalCode] AS [value4],
[t1].[Country] AS [value5]
FROM [Company] AS [t0]
LEFT OUTER JOIN [Address] AS [t1] ON [t0].[CompanyID] = [t1].[CompanyID]
WHERE ([t1].[IsMain] IS NULL) OR ([t1].[IsMain] = 1)
For your AddressID field, you can see that it uses a CASE-WHEN to handle the condition when AddressID is null. When you add a CASE-WHEN for MainAddress, it's trying to do the same thing for that field, but there is no SQL equivalent to string.Format it can use for the ELSE clause, so it blows up.
An easy way around this problem is to use a method to format the string. By calling a private method, LinqToSql won't try to translate the string.Format to SQL, and will instead return all of the fields necessary to populate the Address object. The method can then take care of the formatting.
For example:
LINQ:
....
select new {
comp.CompanyID,
comp.Name,
AddressID = (addr.AddressID == null ? -1 : addr.AddressID),
MainAddress = FormatAddress(addr)
};
Method:
private static string FormatAddress(Address addr)
{
return (addr == null ? "" :
string.Format("{0}, {1}, {2} {3} ({4})",
addr.Address1, addr.City,
addr.Region, addr.PostalCode, addr.Country));
}
Related
I'm having some strange feeling abour sqlite3 parameters that I would like to expose to you.
This is my query and the fail message :
#query
'SELECT id FROM ? WHERE key = ? AND (userid = '0' OR userid = ?) ORDER BY userid DESC LIMIT 1;'
#error message, fails when calling sqlite3_prepare()
error: 'near "?": syntax error'
In my code it looks like:
// Query is a helper class, at creation it does an sqlite3_preprare()
Query q("SELECT id FROM ? WHERE key = ? AND (userid = 0 OR userid = ?) ORDER BY userid DESC LIMIT 1;");
// bind arguments
q.bindString(1, _db_name.c_str() ); // class member, the table name
q.bindString(2, key.c_str()); // function argument (std::string)
q.bindInt (3, currentID); // function argument (int)
q.execute();
I have the feeling that I can't use sqlite parameters for the table name, but I can't find the confirmation in the Sqlite3 C API.
Do you know what's wrong with my query?
Do I have to pre-process my SQL statement to include the table name before preparing the query?
Ooookay, should have looked more thoroughly on SO.
Answers:
- SQLite Parameters - Not allowing tablename as parameter
- Variable table name in sqlite
They are meant for Python, but I guess the same applies for C++.
tl;dr:
You can't pass the table name as a parameter.
If anyone have a link in the SQLite documentation where I have the confirmation of this, I'll gladly accept the answer.
I know this is super old already but since your query is just a string you can always append the table name like this in C++:
std::string queryString = "SELECT id FROM " + std::string(_db_name);
or in objective-C:
[#"SELECT id FROM " stringByAppendingString:_db_name];
Hello I have to write in linq the sql query below:
Declare #OrgID int
Declare #OrgFinalID int
Set #OrgID = 91702 ---91703, 91702, 83279
select #OrgFinalID =
case
when ParentOrganisationId is null then ItemID
else ParentOrganisationId
end
from Organisations
where ItemID = #OrgID
I tried to write this but I am not on the right way, sorry but I am new with LINQ:
var OrgID=91207;
var OrgFinalID = from o in context.Organisations
where o.ItemID == OrgID
select new
{
o.ParentOrganisationId == null ? o.ItemID : o.ParentOrganisationId,
}
I have to put, with the LINQ expression, the value inside the variable OrgFinalID.
Looks like you are expecting this to be just a single number? Than you can call .Single() on your query, that basically returns the value itself:
var OrgFinalID = (from o in context.Organisations
where o.ItemID == OrgID
select new
{
ID = o.ParentOrganisationId == null ? o.ItemID : Convert.ToInt32(o.ParentOrganisationId),
}).Single().ID;
Also note call to Convert.ToInt32 which is supported by Linq to SQL and should help you avoid type casting problem.
Other options:
SingleOrDefault - if there could be single result, or no result at all (returns null in this case)
First - if you expect one or more results from the query
FirstOrDefault - similar to SingleOrDefault , returns null if no results came from the query
I saw this post Linq int to string, and tried it
var personalInfoQuery = from t in crnnsupContext.Tombstones
join i in crnnsupContext.InitialEducations on t.InitialEducation equals SqlFunctions.StringConvert((double)i.InitalEducationID)
where t.RegNumber == 25952
select new CPersonalInfo
{
Tombstone = t,
InitialEducation = i
};
in the database t.InitialEducation is char, i.InitalEducationID is int, but the retrieved result is null. I am pretty sure the value is not empty in the SQL server. So I think the problem is SqlFunctions.StringConvert((double)i.InitalEducationID)
when i remove the join statement, it got this person's information.
Does anyone know why. thanks
Finally find the reason!!
t.InitialEducation is nvarchar(1) in the database, i.InitalEducationID is int, after I modified to SqlFunctions.StringConvert((double)i.InitialEducationID, 1) it works!
"1" is the length of the returned string, the default length is 10, I guess there are some extra space.
Since you're using the SqlFunctions.StringConvert method, I'm assuming you're using EF as the underlying LINQ provider, no?
From the information given, it would appear that you're looking to do a 1 to many join on those properties. The code you've written could coalesce (not enough info on the context to be certain) as an INNER JOIN in SQL, so to force the LEFT JOIN behavior, you can add a .DefaultIfEmpty() call:
crnnsupContext.InitialEducations.DefaultIfEmpty()
I have this SQL query:
SELECT Sum(ABS([Minimum Installment])) AS SumOfMonthlyPayments FROM tblAccount
INNER JOIN tblAccountOwner ON tblAccount.[Creditor Registry ID] = tblAccountOwner.
[Creditor Registry ID] AND tblAccount.[Account No] = tblAccountOwner.[Account No]
WHERE (tblAccountOwner.[Account Owner Registry ID] = 731752693037116688)
AND (tblAccount.[Account Type] NOT IN
('CA00', 'CA01', 'CA03', 'CA04', 'CA02', 'PA00', 'PA01', 'PA02', 'PA03', 'PA04'))
AND (DATEDIFF(mm, tblAccount.[State Change Date], GETDATE()) <=
4 OR tblAccount.[State Change Date] IS NULL)
AND ((tblAccount.[Account Type] IN ('CL10','CL11','PL10','PL11')) OR
CONTAINS(tblAccount.[Account Type], 'Mortgage')) AND (tblAccount.[Account Status ID] <> 999)
I have created a Linq query:
var ownerRegistryId = 731752693037116688;
var excludeTypes = new[]
{
"CA00", "CA01", "CA03", "CA04", "CA02",
"PA00", "PA01", "PA02", "PA03", "PA04"
};
var maxStateChangeMonth = 4;
var excludeStatusId = 999;
var includeMortgage = new[] { "CL10", "CL11", "PL10", "PL11" };
var sum = (
from account in context.Accounts
from owner in account.AccountOwners
where owner.AccountOwnerRegistryId == ownerRegistryId
where !excludeTypes.Contains(account.AccountType)
where account.StateChangeDate == null ||
(account.StateChangeDate.Month - DateTime.Now.Month)
<= maxStateChangeMonth
where includeMortgage.Contains(account.AccountType) ||
account.AccountType.Contains("Mortgage")
where account.AccountStatusId != excludeStatusId
select account.MinimumInstallment).ToList()
.Sum(minimumInstallment =>
Math.Abs((decimal)(minimumInstallment)));
return sum;
Are they equal/same ? I dont have records in db so I cant confirm if they are equal. In SQL there are brackets() but in Linq I didnt use them so is it ok?
Please suggest.
It is not possible for us to say anything about this, because you didn't show us the DBML. The actual definition of the mapping between the model and the database is important to be able to see how this executes.
But before you add the DBML to your question: we are not here to do your work, so here are two tips to find out whether they are equal or not:
Insert data in your database and run the queries.
Use a SQL profiler and see what query is executed by your LINQ provider under the covers.
If you have anything more specific to ask, we will be very willing to help.
The brackets will be generated by LINQ provider, if necessary.
The simplest way to check if the LINQ query is equal to the initial SQL query is to log it like #Atanas Korchev suggested.
If you are using Entity Framework, however, there is no Log property, but you can try to convert your query to an ObjectQuery, and call the ToTraceString method then:
string sqlQuery = (sum as ObjectQuery).ToTraceString();
UPD. The ToTraceString method needs an ObjectQuery instance for tracing, and the ToList() call already performs materialization, so there is nothing to trace. Here is the updated code:
var sum = (
from account in context.Accounts
from owner in account.AccountOwners
where owner.AccountOwnerRegistryId == ownerRegistryId
where !excludeTypes.Contains(account.AccountType)
where account.StateChangeDate == null ||
(account.StateChangeDate.Month - DateTime.Now.Month)
<= maxStateChangeMonth
where includeMortgage.Contains(account.AccountType) ||
account.AccountType.Contains("Mortgage")
where account.AccountStatusId != excludeStatusId
select account.MinimumInstallment);
string sqlQuery = (sum as ObjectQuery).ToTraceString();
Please note that this code will not perform the actual query, it is usable for testing purposes only.
Check out this article if you are interested in ready-for-production logging implementation.
There can be a performance difference:
The SQL query returns a single number (SELECT Sum...) directly from the database server to the client which executes the query.
In your LINQ query you have a greedy operator (.ToList()) in between:
var sum = (...
...
select account.MinimumInstallment).ToList()
.Sum(minimumInstallment =>
Math.Abs((decimal)(minimumInstallment)));
That means that the query on the SQL server does not contain the .Sum operation. The query returns a (potentially long?) list of MinimumInstallments. Then the .Sum operation is performed in memory on the client.
So effectively you switch from LINQ to Entities to LINQ to Objects after .ToList().
BTW: Can you check the last proposal in your previous question here which would avoid .ToList() on this query (if the proposal should work) and would therefore be closer to the SQL statement.
I have a query in sql server 2008. That I want to either pass a value from the dropdownlist or IS NOT NULL (so it shows all of the values). What's the best way to handle this? I know you can't pass the string "IS NOT NULL" to the parameter. I'm a bit stuck on this one.
ASP.NET 3.5 and SQL Server 2008.
Assuming this is a stored procedure, say your parameter is called #Param1, have the parameter set to NULL to indicate IS NOT NULL, as follows:
SELECT ...
FROM ...
WHERE (
(#Param1 IS NULL AND field1 IS NOT NULL)
OR (field1 = #Param1)
)
Suggested by GSerg
Testing ISNULL(#Param1, field1) = field1 with the following:
DECLARE #test1 nvarchar(10) = 'testing',
#test2 nvarchar(10) = NULL; -- or 'random' or 'testing'
SELECT 1
WHERE ISNULL(#test2, #test1) = #test1;
Computations are showing as 1 for each case. This appears to be a better solution than my original answer.
You can use the like operator:
select * from table1 where name like #param
setting #param to % if you want not null values. But then you have to escape the %.