I'm having a problem updating an object in EF Core 7. On an update (during the SaveChanges method), I get the following error:
Cannot insert the value NULL into column 'QueryName', table 'Lightwave360.dbo.Query'; column does not allow nulls. UPDATE fails
The object in question, Query, has a one-to-many relationship with another object, QueryParameter. The error message says the QueryName property is null. However, I'm 100% sure it is not null prior to the update.
Everything works correctly when I do a create/insert instead of an update. The update is the only thing that fails.
I'm hoping someone out there has had a similar issue. I've been trying to figure this out for a couple of days, but nothing has helped.
Here is the full error message:
SqlException: Cannot insert the value NULL into column 'QueryName', table 'Lightwave360.dbo.Query'; column does not allow nulls. UPDATE fails.
Here is my code - table definitions:
CREATE TABLE [Query]
(
[QueryId] [int] IDENTITY(1,1) NOT NULL,
[QueryName] [varchar](100) NOT NULL,
[Sql] [ntext] NOT NULL,
[ConnectionId] [int] NOT NULL,
[Active] [bit] NOT NULL,
[FolderId] [int] NOT NULL,
[LastModifiedUser] [varchar](50) NULL,
[LastModifiedDate] [datetime] NULL,
CONSTRAINT [PK_Query]
PRIMARY KEY CLUSTERED ([QueryId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
CREATE TABLE [QueryParameter]
(
[QueryParameterId] [int] IDENTITY(1,1) NOT NULL,
[QueryId] [int] NOT NULL,
[ParameterName] [varchar](25) NOT NULL,
[ParameterType] [smallint] NOT NULL,
[DataType] [smallint] NOT NULL,
[Label] [varchar](50) NOT NULL,
[DynamicParameterId] [int] NULL,
[Required] [bit] NOT NULL,
[DefaultValue] [varchar](254) NULL,
[DisplayOrder] [smallint] NOT NULL,
CONSTRAINT [PK_QueryParameter]
PRIMARY KEY CLUSTERED ([QueryParameterId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE UNIQUE NONCLUSTERED INDEX [IX_Query]
ON [Query] ([QueryName] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF,
DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
ALTER TABLE [QueryParameter] CHECK CONSTRAINT [FK_QueryParameter_Query]
Here are the class definitions:
[Table(nameof(Query))]
public partial class Query
{
public Query()
{
QueryParameters = new List<QueryParameter>();
}
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QueryId { get; set; }
[Required]
public string QueryName { get; set; }
[Required]
public string Sql { get; set; }
[Required]
public int ConnectionId { get; set; }
public bool Active { get; set; } = true;
[Required]
public int FolderId { get; set; }
public string? LastModifiedUser { get; set; }
public DateTime? LastModifiedDate { get; set; }
[ForeignKey(nameof(ConnectionId))]
public virtual Connection Connection { get; set; }
[ForeignKey(nameof(FolderId))]
public virtual Folder Folder { get; set; }
public ICollection<QueryParameter>? QueryParameters { get; set; }
}
[Table("QueryParameter", Schema = "dbo")]
public partial class QueryParameter
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QueryParameterId { get; set; }
[Required]
public int QueryId { get; set; }
[Required]
public string ParameterName { get; set; }
[Required]
public QueryParameterType ParameterType { get; set; }
[Required]
public QueryParameterDataType DataType { get; set; }
[Required]
public string Label { get; set; }
public int? DynamicParameterId { get; set; }
public bool Required { get; set; }
public string DefaultValue { get; set; }
public short DisplayOrder { get; set; }
[ForeignKey(nameof(DynamicParameterId))]
public DynamicParameter DynamicParameter { get; set; }
[ForeignKey(nameof(QueryId))]
public Query Query { get; set; }
}
Here is the update code:
public IActionResult PutQuery(int key, [FromBody] Lightwave.Data.Query item)
{
try
{
ModelState.Clear();
//if(!ModelState.IsValid)
//{
// return BadRequest(ModelState);
//}
if(item == null || (item.QueryId != key))
{
return BadRequest();
}
item.LastModifiedDate = DateTime.Now;
OnQueryUpdated(item);
_context.Queries.Update(item);
_context.SaveChanges();
var itemToReturn = _context.Queries
.Include(i => i.Connection)
.Include(i => i.Folder)
.Include(i => i.QueryParameters)
.Where(i => i.QueryId == key);
Request.QueryString = Request.QueryString.Add("$expand", "Connection");
OnAfterQueryUpdated(item);
return new ObjectResult(SingleResult.Create(itemToReturn));
}
catch(Exception ex)
{
ModelState.AddModelError("", ex.GetBaseException().Message);
_logger.LogError(ex.GetBaseException(), ex.GetBaseException().Message);
return BadRequest(ModelState);
}
}
If anyone has any suggestions, I would greatly appreciate it. As I've said, I've been working on this for a couple of days and have exhausted all efforts that I can think of.
Thanks.
UPDATE: I'm still trying to figure this thing out. I wanted to add the SQL that is being generated on the backend during the Update/SaveChanges(). It looks as though it is deleting the entity, then inserting it back. Again, if anyone can help out, it would be greatly appreciated. Here is the generated SQL:
exec sp_executesql N'SET NOCOUNT ON;
DELETE FROM [Query]
OUTPUT 1
WHERE [QueryId] = @p0;
MERGE [Query] USING (
VALUES (@p1, @p2, @p3, @p4, @p5, @p6, @p7, 0),
(@p8, @p9, @p10, @p11, @p12, @p13, @p14, 1)) AS i ([Active], [ConnectionId], [FolderId], [LastModifiedDate], [LastModifiedUser], [QueryName], [Sql], _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT ([Active], [ConnectionId], [FolderId], [LastModifiedDate], [LastModifiedUser], [QueryName], [Sql])
VALUES (i.[Active], i.[ConnectionId], i.[FolderId], i.[LastModifiedDate], i.[LastModifiedUser], i.[QueryName], i.[Sql])
OUTPUT INSERTED.[QueryId], i._Position;
',N'@p0 int,@p1 bit,@p2 int,@p3 int,@p4 datetime2(7),@p5 nvarchar(4000),@p6 nvarchar(4000),@p7 nvarchar(4000),@p8 bit,@p9 int,@p10 int,@p11 datetime2(7),@p12 nvarchar(4000),@p13 nvarchar(4000),@p14 nvarchar(4000)',@p0=10,@p1=1,@p2=1,@p3=0,@p4=NULL,@p5=NULL,@p6=NULL,@p7=NULL,@p8=1,@p9=0,@p10=1,@p11=NULL,@p12=NULL,@p13=NULL,@p14=NULL
Query.QueryName. Other than that you can add a minimal reproducible example showing how do you try to fill and save the dataitem. QueryName = "Test Value";OnQueryUpdatedandOnAfterQueryUpdatedmethods look like? You should do a_context.Queries.Find(item.QueryId)call, then update the values you wish to update.