EF Core: Add records with decimal, non-identity PK

0

I have an existing database with multiple tables with decimal (numeric(14,0)) PK columns. I'm experimenting with different ways to insert multiple records in one action.

I'm not sure if I can introduce sequences, so I've been trying to use HasDefaultValueSql("MAX(CASE_ATTRIBUTE_ID) + 1"). In a general use case, you could have 1-n CaseAttributes added at a time, so I don't necessarily to use EF to query for the next ID first.

I can probably add more detail, if necessary. However, what would typically be the best way to insert 1-n records with EF Core without the presence of an auto-incrementing identity column?

UDPATE:

Entity class except (I've tried both None and Computed):

[ExcludeFromCodeCoverage]
[Table("CASE_ATTRIBUTE_CONFIG", Schema = "dbo")]
public partial class CaseAttribute : ISoftDelete, IUpdateTracker
{
    [Key]
    [Column("CASE_ATTRIBUTE_ID", TypeName = "numeric(14, 0)"), DatabaseGenerated(DatabaseGeneratedOption.None)]
    public decimal CaseAttributeId { get; set; }
}

Context except:

modelBuilder.Entity<CaseAttribute>(entity =>
{
    entity.Property(e => e.CaseAttributeId).HasDefaultValueSql("MAX(CASE_ATTRIBUTE_ID) + 1").ValueGeneratedOnAdd();

    entity.HasQueryFilter(e => EF.Property<DateTime?>(e, "EffectiveEndDate") == null);
});

If I don't use ValueGeneratedOnAdd(), it just seems to insert '0'. Otherwise, I get the exception: Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'CASE_ATTRIBUTE_ID', table 'RPE_V3_12_02_00.dbo.CASE_ATTRIBUTE_CONFIG'; column does not allow nulls. UPDATE fails.

Using profiler, you can see it also generates some really strange SQL:

exec sp_executesql N'SET NOCOUNT ON;
DECLARE @inserted0 TABLE ([CASE_ATTRIBUTE_ID] numeric(14, 0), [_Position] [int]);
MERGE [dbo].[CASE_ATTRIBUTE_CONFIG] USING (
VALUES (@p21, @p22, @p23, @p24, @p25, @p26, @p27, @p28, @p29, @p30, @p31, @p32, @p33, @p34, @p35, @p36, @p37, @p38, 0)) AS i ([CASE_ATTRIBUTE_NAME], [CASE_ATTRIBUTE_REGEX], [CASE_ATTRIBUTE_TYPE], [CASE_SUB_TYPE], [CASE_TYPE], [CODE], [CODE_TYPE], [CORE], [DISPLAY_GROUP_NAME], [DISPLAY_ORDER], [DISPLAY_TAB], [EFFECTIVE_BEGIN_DT], [EFFECTIVE_END_DT], [IS_REQUIRED], [MAXIMUM_LENGTH], [NODE_NAME], [UPDATED_BY], [UPDATED_DATE], _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT ([CASE_ATTRIBUTE_NAME], [CASE_ATTRIBUTE_REGEX], [CASE_ATTRIBUTE_TYPE], [CASE_SUB_TYPE], [CASE_TYPE], [CODE], [CODE_TYPE], [CORE], [DISPLAY_GROUP_NAME], [DISPLAY_ORDER], [DISPLAY_TAB], [EFFECTIVE_BEGIN_DT], [EFFECTIVE_END_DT], [IS_REQUIRED], [MAXIMUM_LENGTH], [NODE_NAME], [UPDATED_BY], [UPDATED_DATE])
VALUES (i.[CASE_ATTRIBUTE_NAME], i.[CASE_ATTRIBUTE_REGEX], i.[CASE_ATTRIBUTE_TYPE], i.[CASE_SUB_TYPE], i.[CASE_TYPE], i.[CODE], i.[CODE_TYPE], i.[CORE], i.[DISPLAY_GROUP_NAME], i.[DISPLAY_ORDER], i.[DISPLAY_TAB], i.[EFFECTIVE_BEGIN_DT], i.[EFFECTIVE_END_DT], i.[IS_REQUIRED], i.[MAXIMUM_LENGTH], i.[NODE_NAME], i.[UPDATED_BY], i.[UPDATED_DATE])
OUTPUT INSERTED.[CASE_ATTRIBUTE_ID], i._Position
INTO @inserted0;

SELECT [t].[CASE_ATTRIBUTE_ID] FROM [dbo].[CASE_ATTRIBUTE_CONFIG] t
INNER JOIN @inserted0 i ON ([t].[CASE_ATTRIBUTE_ID] = [i].[CASE_ATTRIBUTE_ID])
ORDER BY [i].[_Position];

',N'@p21 varchar(50),@p22 varchar(200),@p23 varchar(6),@p24 varchar(25),@p25 varchar(25),@p26 varchar(10),@p27 varchar(25),@p28 tinyint,@p29 varchar(100),@p30 decimal(1,0),@p31 varchar(25),@p32 datetime,@p33 datetime,@p34 bit,@p35 decimal(1,0),@p36 varchar(25),@p37 varchar(128),@p38 datetime',@p21='test-attribute',@p22=NULL,@p23='STR',@p24='ADMIN',@p25='MYTEST',@p26='MN',@p27='STATE_PROVINCE',@p28=4,@p29='display-group',@p30=1,@p31='',@p32='2020-10-01 00:00:00',@p33=NULL,@p34=0,@p35=4,@p36='node-name',@p37='admin@rsimail.com',@p38='2020-10-19 21:03:03.787'
c#
entity-framework-core
asked on Stack Overflow Oct 19, 2020 by mschmidt73 • edited Oct 19, 2020 by mschmidt73

1 Answer

0

I'm not sure if I can introduce sequences,

Is there any technical reason not to use Sequences?

You are configuring EF to generate the value on the server-side anyway. A Sequence would be much more efficient in generating the values (and EF doesn't need to know how it's generated), and has the added benefit that manual inserts you do via SQL Management Studio will also leverage the sequence.

You can add an empty EF migration (dotnet ef migrations add ...) that creates the Sequence and updates the table to use the next value of the sequence as the default value for the primary key.

Assuming the current MAX(CaseAttributeId) is 5000:

CREATE SEQUENCE [dbo].[CaseAttributeIdSequence] AS NUMERIC(14,0)
    START WITH 5001
    MINVALUE 5001
    INCREMENT BY 1
;

Update the table to use the next value of the sequence as the default value:

ALTER TABLE [dbo].[CaseAttribute]
    ADD CONSTRAINT [DF_CaseAttribute_CaseAttributeId]
        DEFAULT NEXT VALUE FOR [dbo].[CaseAttributeIdSequence]
        FOR [CaseAttributeId]
;
answered on Stack Overflow Oct 20, 2020 by C. Augusto Proiete

User contributions licensed under CC BY-SA 3.0