In my database table I have 54 rows, of those 53 are NULL in the Code column which is defined as a varchar(100). (I confirmed that those fields are null by performing queries directly on the database using my Microsoft SQL Management Studio) I have tried to use the following LINQ code to return all of the rows where the field is null:
public IQueryable<LocationHeader> LocationHeaders
{
return from p in Context.LocationTypes
where p.Code == null
select new LocationHeader
{
ID = p.ID,
Description = p.Description,
Code = p.Code
};
}
I have also found that if Iremove the where clause and then call ToList() on my LocationHeaders property and then query against it, it returns only the 53 rows I expect.
var test = LocationHeaders.ToList().Where(x => x.Code == null);
I've also tried p.Code.Equals(null) and object.Equals(p.Code, null) as suggested in related questions on this website and elsewhere. It always return an empty collection. If I change it to p.Code != null it returns all 54 rows (53 of which have a null Code column, and 1 which does not) but I view those objects the Code property has been set to null.
I also tried to null coalesce my code property into an empty string that I could check later:
Code = p.Code ?? string.Empty
But that changed exactly nothing, when I viewed the items after the query was performed, the Code property of my objects was still set to null.
Does anyone know why this might be and what I can do to fix it? I am using EF 6 Code-First, if that matters.
EDIT: I've permanently changed my code to read this way:
public IQueryable<LocationHeader> LocationHeaders
{
return from p in Context.LocationTypes
where object.Equals(p.Code, null)
select new LocationHeader
{
ID = p.ID,
Description = p.Description,
Code = p.Code
};
}
and I finally thought to check the query using SQL Server profiler. It's STILL writing the query like this:
exec sp_executesql N'SELECT
[Extent1].[ID] AS [ID],
[Extent1].[Description] AS [Description],
[Extent1].[Code] AS [Code]
FROM [dbo].[LocationType] AS [Extent1]
WHERE [Extent1].[Code] = #p__linq__0',N'#p__linq__0 nvarchar(4000)',#p__linq__0=NULL
Update your entity models to not allow NULL values in the first place... you will have to set all the fields which are currently NULL to an empty string prior to attempting this, (you can set them through SSMS)...
After you've set all the null values to an empty string.
Add this attribute to the Code property.
[Required(AllowEmptyStrings = true)]
public string Code { get; set; }
And migrate those changes over.
From here on you can just do Foo.Code == string.Empty
Related
I have a problem when converting sql to query c# about left join
here is sql query
select mTeacher.Id as Id, mTeacher.Name as Name, mStudents.Name as Addresses
from Teachers mTeacher
left join Students mStudents
on mStudents.TeacherId=mTeacher.Id
where mStudents.Name = 'some of word'
here is the image
and here is i converted to sql to query c#
var zzz= from mTeacher in repo.Teachers
join mStudents in repo.mStudents on mTeacher.Id equals mStudents.TeacherId into a
from y1 in a.DefaultIfEmpty()
where mTeacher.Name.Equals("someofword") or mStudent.Name.Equals("somofword")
select new { mTeacher.Id,mTeacher.Name};
there will be student will have null value
mStudent.Name.Equals("somofword")
i got something like this how to handle this
If I go by the SQL query, your where clause is only looking up Student.Name.
This has to be not null (as per you where query). So I would suggest that you use simply an inner join. This will solve your C# conversion issue as well.
If you still want to retain the check, then you can modify your where clause to:
where mTeacher.Name.Equals("someofword") or (mStudent != null && mStudent.Name.Equals("somofword"))
The null check will make sure that you do not run into the object is null error (remember in case of left join the student value can be null).
Also add a similar null check in your select clause, if you are referring to values from student table.
select new { mTeacher.Id,mTeacher.Name, StudentName = ( y1 == null ) ? "No Student" : y1.Name};
I'm trying to list all items with an extra column describing whether it's owned by the current user.
So I'm looking for a Linq query that generates something like the following SQL:
SELECT *,
CASE WHEN
EXISTS (
SELECT NULL FROM OwnedItems
WHERE OwnedItems.UserId = #UserId AND OwnedItems.ItemId = Items.Id
)
THEN 'true'
ELSE 'false'
END AS Owned
FROM Items;
According to the internet as well as a successful LinqPad experiment this code should work.
from item in Items
select new
{
Owned = OwnedItems.Any(own => own.UserId == userId && own.ItemId == item.Id),
Item = item
}
In LinqPad this code generates the exact same SQL as I want. But in my project it does something completely different.
My code is a .Net Core 2.1 project using Entity Framework Core 2.1. Since it is a Core project I can't directly test it in LinqPad as it isn't supported yet.
In my project this code results in an unfiltered SELECT statement querying every Item, then for each of them a separate query to check if it exists in the OwnedItems table. Like this:
1 instance of this query runs:
Executed DbCommand (68ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT *
FROM [Items] AS [item]
Followed by hundreds of these queries taking multiple seconds to run:
Executed DbCommand (32ms) [Parameters=[#__userId_0='?' (DbType = Int32), #_outer_Id='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SELECT CASE
WHEN EXISTS (
SELECT 1
FROM [OwnedItems] AS [ownedItems]
WHERE ([ownedItems].[UserId] = #__userId_0) AND ([ownedItems].[ItemId] = #_outer_Id))
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
Some further info, maybe it helps:
If I use the same line as part of a where clause, it works perfectly.
var q = from item in Items
where OwnedItems.Any(o => o.UserId == userId && o.ItemId == item.Id)
select item;
The above linq results in this nice sql:
SELECT *
FROM [Items] AS [item]
WHERE EXISTS (
SELECT 1
FROM [OwnedItems] AS [o]
WHERE ([o].[UserId] = #__userId_0) AND ([o].[ItemId] = [item].[Id]))
Notes:
The above code has been mangled manually so there might be typos in there. Please disregard them.
I understand that this particular query can be done using a left join and checking for nulls but my actual one is more complex and I need (nested) exists clauses.
UPDATE FOR SOLUTION
As #KorsG pointed out if the Item isn't materialized, the proper query is generated.
What I found is that not materializing Item works even if I write the following:
from item in Items
select new
{
Owned = OwnedItems.Any(own => own.UserId == userId && own.ItemId == item.Id),
// Item = item //THIS LINE GENERATES BAD QUERY
Item = new Item {
Id = item.Id,
Name = item.Name,
...
[Literally every single property listed one by one] = item.CorrespondingProperty
...
}
}
So I can actually materialize the full item, I just have to explicitly type every last property. FUN!
You probably need to enable eager loading for the "OwnedItems" navigation property in the query:
https://learn.microsoft.com/en-us/ef/core/querying/related-data#eager-loading
Please post your full linq query if I should give an example.
UPDATE 1
Seems like subqueries have N+1 problems in EF Core and it will maybe be fixed in version 3.
Reference: https://github.com/aspnet/EntityFrameworkCore/issues/10001
UPDATE 2
If you don't need to fully materialize "Items" you should be able to do something like this where you create an anoymous object instead which should "trick" EF into what you want:
from item in Items
select new
{
Owned = OwnedItems.Any(own => own.UserId == userId && own.ItemId == item.Id),
Item = new { Id = item.Id, Name = item.Name }
}
Reference: https://github.com/aspnet/EntityFrameworkCore/issues/11186
You need to tell EF to load the related data, in this case the OwnedItems table.
One way to go about this is to Include the related table. If there's a foreign key that links tables it could be easily done like this:
var dataWithRelatedData = db_context.Items.Include(x => x.OwnedItems).Select ...
Another way to avoid a high number of round trips to the database is to load both datasets in separate queries and then merge them in memory. So you would first make a query to Items then with the data returned another query to OwnedItems and lastly merge them into a single list of objects. This would do just 2 calls to the database, therefore improving performance.
Perhaps I'm misunderstanding COALESCE, but in essence, what I'm trying to do is run a select query that if it returns NULL instead of an int, instead return 0 for the purposes of ExecuteScalar().
SQL Server query:
SELECT TOP 1 COALESCE(SeqNo,0)
FROM tblProjectChangeOrder
WHERE ProjectID = XXXXX
ORDER BY SeqNo DESC
If the supplied ProjectID exists in the Change Order table, it returns the expected highest SeqNo. However, if the supplied ProjectID has no existing Change Orders (thus returns NULL for SeqNo), rather than the COALESCE returning 0, I am still getting NULL.
Am I just getting the syntax wrong or is what I want to do possible with COALESCE? The other option I see is to have my ExecuteScalar() pass to a nullable int, then follow that with a ?? to coalesce in my C# codebehind.
As john has mentioned in the comments, COALESCE operates at row level. If a table contains no rows, or a statement returns no rows, then no rows will be returned. Take the simple example below:
CREATE TABLE #Sample (ID int);
SELECT COALESCE(ID, 0)
FROM #Sample;
DROP TABLE #Sample;
Notice that nothing is returned.
Instead, one method is to use a subquery. For your query, that would result in:
SELECT COALESCE(SELECT TOP 1 SeqNo
FROM tblProjectChangeOrder
WHERE ProjectID = XXXXX
ORDER BY SeqNo DESC),0) AS SeqNo;
This also assumes that Seqno has a data type of int; otherwise you're likely to get a conversion error.
My guess is that the null reference exception occures on the code and has nothing to do with the sql query. It might just be that your code is not handling that you return no rows (or no scalar in your case) but you might be trying to access it somewhere in c#.
Show us the line of code that is throwing this exception in c# so we might be able to confirm this.
regards
Edit : From this similar topic
In your c# code you might want to try ("cmd" being your "SqlCommand" object):
int result = 0;
int.TryParse(cmd.ExecuteScalar(), out result);
or in one line
int.TryParse(cmd.ExecuteScalar(), out int result);
I don't know if it is the most suitable solution for you but I hope it is a start.
As covered null and no row are not the same.
This sample covers it.
set nocount on;
select isnull(1, 0)
where 1 = 1;
select isnull(1, 0)
where 1 = 2;
select isnull(null, 0)
where 1 = 1;
select isnull(null, 0)
where 1 = 2;
-----------
1
-----------
-----------
0
-----------
this should work
select top 1 isnull(seq, 0)
from (select null as seq
union all
select max(seq) from tblProjectChangeOrder where ProjectID = XXXXX
) tt
order by seq desc
I have a gridview that can be filtered by 6 drop down boxes, so when writing the sql the easiest thing would be to use an 'or' statement if dropdown has selection or null etc.
However I have read on here and other sites that using sql or statements are a bad idea, can anyone offer any other suggestions i could use rather than me writing variations on whether each ddl selection is null? Below is an example of the first query, with every ddl returning a value
#ruleID int = null,
#engagementStatusID int = null,
#areaOfWorkID int = null,
#registered bit = null,
#staffGroupID int = null,
#assignmentTypeID int = NULL
AS
SET NOCOUNT ON
IF (#ruleID IS NOT NULL and #engagementStatusID IS NOT NULL and #areaOfWorkID IS NOT NULL and
#registered IS NOT NULL and #staffGroupID IS NOT NULL and #assignmentTypeID IS NOT NULL)
BEGIN
SELECT r.dbRuleId AS RuleID,r.dbEngagementStatusId AS EngagementStatusID,
r.dbIsAllStaffGroups AS AllStaffGroups,r.dbIsAllAssignments AS AllAssignments,
r.dbIsAllRegistered AS AllRegistered,r.dbIsAllUnregistered AS AllUnregistered,
r.dbSoftDelete AS Softdelete, es.dbName AS EngagementName,
sgc.dbName AS StaffGroupName, aow.dbName AS AreaOfWorkName,
at.dbName AS AssignmentName, at.dbIsRegistered AS Registered,sgc.dbStaffGroupCodeId AS StaffGroupCodeID,
at.dbAssignmentTypeId AS AssignmentID, aow.dbAreaOfWorkId AS AreaOfWorkID
FROM dbo.tbRule r INNER JOIN
dbo.EngagementStatus es ON r.dbEngagementStatusId = es.dbEngagementStatusId INNER JOIN
dbo.RuleStaffGroup rsg ON r.dbRuleId = rsg.dbRuleId INNER JOIN
dbo.StaffGroupCode sgc ON rsg.dbStaffGroupId = sgc.dbStaffGroupCodeId INNER JOIN
dbo.RuleAssignmentCode rac ON r.dbRuleId = rac.dbRuleId INNER JOIN
dbo.AssignmentCode ac ON
rac.dbAssignmentCodeId = ac.dbAssignmentCodeId INNER JOIN
dbo.AssignmentType at ON ac.dbAssignmentId = at.dbAssignmentTypeId INNER JOIN
dbo.AreaOfWork aow ON ac.dbAreaOfWorkId = aow.dbAreaOfWorkId
WHERE ((r.dbRuleId = #ruleID) and (r.dbEngagementStatusId = #engagementStatusID) and (aow.dbAreaOfWorkId = #areaOfWorkID) and
(at.dbIsRegistered = #registered) and (sgc.dbStaffGroupCodeId = #staffGroupID) and (at.dbAssignmentTypeId = #assignmentTypeID))
Any advice on this would be great
Update
I feel i should clarify something about my code, when i say null, this is the value i have assigned to the "all" selection of a drop down list, so for example imn most cases i do something like this to get the value that needs to be passed to the DB
int? Type = (this.ddlType.SelectedValue.ToString() == "All") ? (int?)null : Convert.ToInt32(this.ddlType.SelectedValue.ToString());
so if the user has selected all the Db recieves 'null' which i can then use on the 'if #blah IS NOT NULL' etc. I realise this is probably not the best way to do this
It seems you are executing this stored procedure and then validating the user input at the database level. You should not call the database stored procedure if the drop down list values are null, you can handle this at the client side (or server side).
Client side (JavaScript) would be better for the users experience, you can then invoke the stored procedure if the user has selected all appropriate drop down list values.
The problem comes when you do things like:
WHERE (r.dbRuleId = #ruleID or #ruleID is null)
and (r.dbEngagementStatusId = #engagementStatusID
or #engagementStatusID is null)
-- ... lots more
which quickly degrades to really bad query plans. The trick, then, is to have TSQL that matches your exact set of query parameters.
The hard to maintain way to fix this is to write DML for every single possibility, and branch into the correct one - but this is really ugly, and confuses a lot of tools.
The easiest way to do this is to build the TSQL appropriately at the caller - but if your system demands that you use a stored procedure (the benefits for which, these days, are dubious at best - btw), then the simplest choice is dynamic SQL. Obviously, you need to be careful here - you still don't want to concatenate inputs (for both injection and query-plan reasons), but - you can do something like:
declare #sql nvarchar(4000) = N'...start of query...';
if(#ruleID is not null)
set #sql = #sql + N' and r.dbRuleId = #ruleID';
if(#engagementStatusID is not null)
set #sql = #sql + N' and r.dbEngagementStatusId = #engagementStatusID';
You then need to execute that with sp_executesql, declaring the parameters:
exec 'sp_executesql', #sql,
N'#ruleID int, #engagementStatusID int',
#ruleID, #engagementStatusID
I'm not sure I understood your question, but if you're looking at avoiding repetition of OR operators, consider using IN('x','y','z') - listing the possible values. This would be easier to read than [something] = 'x' OR [something] = 'y' OR [something] = 'z'.
I'm using Linq to SQL. I have a DataContext against which I am .SubmitChanges()'ing. There is an error inserting the identity field:
Cannot insert explicit value for identity column in table 'Rigs' when IDENTITY_INSERT is set to OFF.
The only identity field is "ID", which has a value of 0. It's defined in the DBML as:
[Column(Storage="_ID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
There are a few foreign keys, and I've verified they have values that jive with the foreign tables' content.
Why would I be getting this error?
Edit: Here is the query:
exec sp_executesql N'INSERT INTO [dbo].[Rigs]([id], [Name], [RAM], [Usage], [MoreInfo], [datetime], [UID])
VALUES (#p0, #p1, #p2, #p3, #p4, #p5, #p6)
SELECT [t0].[id], [t0].[OSID], [t0].[Monitors]
FROM [dbo].[Rigs] AS [t0]
WHERE [t0].[id] = #p7',N'#p0 int,#p1 varchar(1),#p2 int,#p3 varchar(1),#p4 varchar(1),#p5 datetime,#p6 int,#p7
int',#p0=0,#p1='1',#p2=NULL,#p3='4',#p4='5',#p5=''2009-03-11 20:09:15:700'',#p6=1,#p7=0
Clearly it is passing a zero, despite having never been assigned a value.
Edit: Adding Code:
Rig rig = new Rig();
int RigID;
try
{ // Confirmed these always contain a nonzero value or blank
RigID = int.Parse(lbSystems.SelectedValue ?? hfRigID.Value);
if (RigID > 0) rig = mo.utils.RigUtils.GetByID(RigID);
}
catch { }
rig.Name = Server.HtmlEncode(txtName.Text);
rig.OSID = int.Parse(ddlOS.SelectedValue);
rig.Monitors = int.Parse(txtMonitors.Text);
rig.Usage = Server.HtmlEncode(txtUsage.Text);
rig.MoreInfo = Server.HtmlEncode(txtMoreInfo.Text);
rig.RigsToVideoCards.Clear();
foreach (ListItem li in lbYourCards.Items)
{
RigsToVideoCard r2vc = new RigsToVideoCard();
r2vc.VCID = int.Parse(li.Value);
rig.RigsToVideoCards.Add(r2vc);
}
rig.UID = c_UID > 0 ? c_UID : mo.utils.UserUtils.GetUserByToken(this.Master.LiveToken).ID;
if (!mo.utils.RigUtils.Save(rig))
throw new ApplicationException("There was an error saving your Rig. I have been notified.");
hfRigID.Value = rig.id.ToString();
public static User GetUserByToken(string token)
{
DataClassesDataContext dc = new DataClassesDataContext(ConfigurationManager.ConnectionStrings["MultimonOnlineConnectionString"].ConnectionString);
return (from u in dc.Users
where u.LiveToken == token
select u).FirstOrDefault();
}
Also, I notice that when I UPDATE an existing rig (insertonsubmit), it doesn't update. Profiler doesn't even show any queries being run.
My theory of what happened is as follows:
You designed and created the DataBase
You created the DB Context using LINQ TO SQL in visual Studio
You forgot to set the auto-identity to your table
You fixed that by going to your DataBase and setting the auto-identity
But you forgot to recreate/update your LINQ TO SQL DB Context!!!
:D
Is your code setting the ID value explicitely to 0? (instead of leaving it untouched).
Update 1: As you posted on the updated version, linq2sql is clearly passing the value to the db. Here is one I haven't had any trouble with:
[Column(Storage="_CustomerID", AutoSync=AutoSync.Always, DbType="Int NOT NULL IDENTITY", IsDbGenerated=true)]
public int CustomerID
I just saw another one, and it has the same exact definition of the one you are using.
[Column(Storage="_TestID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
public int TestID
Update 2: Regarding updates, you are not supposed to do InsertOnSubmit for that. Just update the values and call .SubmitChanges (should be throwing an exception). On the insert it is really weird, as the property attributes you posted seems to be correct, so the Insert method linq2sql generates should be correct as well. I would try, re-adding the table on the designer again and verifying all the properties are correct.
Note that the generated insert method should look like (with a matchingRig_Insert):
private void InsertRig(Rig obj)
{
System.Nullable<int> p1 = obj.Id;
this.Rig_Insert(/* bunch of values */, ref p1);
obj.Id = p1.GetValueOrDefault();
}
Ensure the property Auto Generated Value in your dbml for that column is set to true. If not, LINQ will attempt to default the value based on the type.
If you are supplying the value of the IDENTITY column, you must add
SET IDENTITY_INSERT ON
before your SQL Statements, and
SET IDENTITY_INSERT OFF
after to turn it off. That goes for ANY raw SQL. If you used LINQ to create an object like:
var customer = new LinqContext.Customer;
customer.FirstName = "Bob";
customer.LastName = "Smith";
LinqContent.Customer.Add(customer);
LinqContext.SubmitChanges();
I believe that will work, forgive me if I have classes wrong. You get the basic idea.
Edit: Oops.. Sorry, didn't read the entire question, I take it back, tired and fatigued from work...
seems like your ID is being assigned somewhere (even if just defaulting to 0) - would you care for posting some of your LINQ code?
I was having the exact same problem.
My solution was to manually make the identity INT column into a nullable INT? column in the DBML designer, not in the actual table of the DB of course. Also set the DbType to NULL instead of NOT NULL. Like so:
[Column(Storage="_TestID", AutoSync=AutoSync.OnInsert, DbType="Int NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
public int? TestID
This works with all DB operations. When inserting simply set the identity column to NULL rather than to 0.
my case, I forgot to update my dbml file after setting Id column to identity.