Come generare un numero di chiave personalizzato utilizzando C # in ASP.NET Core

questa mia class,

public class PatReg { [DatabaseGenerated(DatabaseGeneratedOption.Computed), ScaffoldColumn(false)] public Int64 RecId { get; set; } [Key,Display(Name = "File Id"), ScaffoldColumn(true), DatabaseGenerated(DatabaseGeneratedOption.None )] public Int64 FileId { get; set; } [Required, Display(Name = "First Name")] public string FName { get; set; } [Required, Display(Name = "Middle Name")] public string MName { get; set; } [Required, Display(Name = "Last Name")] public string LName { get; set; } [Required, Display(Name = "Date of Birth"), DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] public DateTime Dob { get; set; } } 

“FileId” è la mia chiave primaria e voglio generarlo dopo aver salvato il record salvandolo insieme al record,

Il numero personalizzato avrebbe la seguente specifica, YYMMDD0001 dove YY è due cifre dell’anno, MM Due cifre del mese. DD è di due cifre del giorno, 001 è l’avvio seriale e resettato ogni giorno.

Questo è il mio controller

 // POST: PatReg/Create // To protect from overposting attacks, please enable the specific properties you want to bind to, for // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public async Task Create([Bind("FileId,FName,MName,LName,Dob")] PatReg patReg) { if (ModelState.IsValid) { _context.Add(patReg); await _context.SaveChangesAsync(); return RedirectToAction("Index"); } return View(patReg); } 

sfondo

Ho usato per generare questo numero utilizzando la stored procedure SQL come di seguito,

  Set @YY = (RIGHT(CONVERT(VARCHAR(8), GETDATE(), 1),2)) Set @MM = SUBSTRING(CONVERT(nvarchar(6),getdate(), 112),5,2) Set @DD = RIGHT('00' + CONVERT(NVARCHAR(2), DATEPART(DAY, GETDATE())), 2) Set @SRL = (SELECT FileNumSrl FROM SetTblSrls WHERE RecID = 1) SET @FileId =(select CAST(CONCAT ( @YY , @MM , @DD,00 ,@SRL) AS int)) 

“@SRL” rappresenta la sequenza seriale che cerco da “SetTblSrls” e ho usato un trigger sulla tabella di destinazione per aggiornare questo numero su ogni inserto con cui ottengo un nuovo numero ogni volta che genera il FileId

Come posso farlo usando EF e C #,

Avrai bisogno di mantenere un numero di sequenza da qualche parte in modo da poterlo incrementare in modo sicuro ogni volta che hai un nuovo file. La memoria non è realmente un’opzione, dal momento che IIS ripristina i pool di app ogni 29 ore per impostazione predefinita, perdendo tutto ciò che è ansible memorizzare nella cache. Come tale sei rimasto con il database o il file system.

Il seguente SQL fornisce un mezzo sicuro e performante per ottenere il prossimo numero di sequenza disponibile semplicemente eseguendo la stored procedure dal codice C # sul lato server e leggendo il valore restituito:

 create table DailySequence ( SequenceDate date not null primary key, LastSequence int not null default(0) -- Change the default if you want your first sequence to be 1 ) go create procedure dbo.GetNextSequence as begin Declare @today date = getdate() Declare @table table (id int) begin tran update DailySequence set LastSequence=LastSequence+1 output inserted.LastSequence into @table where [email protected] if (@@ROWCOUNT=0) insert into DailySequence(SequenceDate) output inserted.LastSequence into @table values (@today) commit declare @s varchar(20) select @s = convert(varchar(20), id) from @table if (Len(@s)<4) set @s = Right('000' + @s, 4) select CONVERT(VARCHAR(10), @today, 12) + @s [Sequence] end go 

Questa soluzione è basata sugli usi di SQL Server Sequences (disponibile da SQL Server 2012 e Database SQL di Azure ). Se non puoi usarli, puoi saltare ad un’altra risposta.


Questa soluzione consiste nel creare una sequenza che calcolerà automaticamente FileId. Ma dovrai ripristinare la sequenza ogni giorno a mezzanotte per ottenere ciò che desideri. Ecco come è ansible creare la sequenza:

 Set @YY = (RIGHT(CONVERT(VARCHAR(8), GETDATE(), 1),2)) Set @MM = SUBSTRING(CONVERT(nvarchar(6),getdate(), 112),5,2) Set @DD = RIGHT('00' + CONVERT(NVARCHAR(2), DATEPART(DAY, GETDATE())), 2) DROP SEQUENCE IF EXISTS dbo.DailyFileId; CREATE SEQUENCE dbo.DailyFileId START WITH CAST(CONCAT(@YY, @MM, @DD, '0001') AS int) INCREMENT BY 1; GO 

(O qualcosa del genere, non ho un motore SQL Server per testarli. Per favore non esitare a correggerli nei commenti se necessario)

Per eseguire lo script ogni giorno, è ansible utilizzare un agente SQL. È a mio parere l’opzione migliore, ma puoi anche eseguire un nuovo thread all’interno dell’applicazione che eseguirà lo script ogni giorno.

Se preferisci questa opzione, ecco come puoi farlo. Ti lascerò decidere dove devi inserire quel codice nella tua applicazione:

 // Call that method as close as you can from the startup of your application Task.Run(() => DailyResetSequence()); private async void DailyResetSequence() { while (true) { using (var dbContext = new DbContext()) { var tomorrow = DateTime.Now.AddDays(1); var sleepingTime = tomorrow - DateTime.Now; // waiting until tomorrow morning await Task.Delay(sleepingTime); // See below dbContext.ResetSequence(); } } } 

(Tieni presente che non gestisco la chiusura della tua domanda Probabilmente hai bisogno di annullare l’attività in quel momento, e probabilmente alcune altre cose del genere)

Una volta che la sequenza è stata creata, devi solo interrogare quella sequenza per ottenere il tuo nuovo ID di file. SQL Engine gestirà automaticamente le chiamate simultanee e garantisce che ogni ID restituito sia univoco.

Sembra che non possiamo eseguire query non elaborate con EF Core come avremmo potuto fare con EF6 ( dbContext.Data.SqlQuery ). Una soluzione sarebbe quella di eseguire manualmente un comando sql. Non so come queste operazioni (ottenere la connessione, aprirla, ecc.) Siano sicure, quindi preferisco essere sicuro e utilizzare un meccanismo di blocco:

 static class DbContextExtensions { private static object DbContextLock = new object(); public static void ResetSquence(this DbContext dbContext) { lock (DbContextLock) { using (var command = dbContext.Database.GetDbConnection().CreateCommand()) { command.CommandText = @"Set @YY = (RIGHT(CONVERT(VARCHAR(8), GETDATE(), 1),2)) Set @MM = SUBSTRING(CONVERT(nvarchar(6),getdate(), 112),5,2) Set @DD = RIGHT('00' + CONVERT(NVARCHAR(2), DATEPART(DAY, GETDATE())), 2) DROP SEQUENCE IF EXISTS dbo.DailyFileId; CREATE SEQUENCE dbo.DailyFileId START WITH CAST(CONCAT(@YY, @MM, @DD, '0001') AS int) INCREMENT BY 1; GO "; command.CommandType = CommandType.Text; dbContext.Database.OpenConnection(); command.ExecuteNonQuery(); dbContext.Database.CloseConnection(); } } } public static long GetNextFileId(this DbContext dbContext) { long fileId; lock (DbContextLock) { using (var command = dbContext.Database.GetDbConnection().CreateCommand()) { command.CommandText = "SELECT NEXT VALUE FOR dbo.DailyFileId;"; command.CommandType = CommandType.Text; dbContext.Database.OpenConnection(); fileId = (long)command.ExecuteScalar(); dbContext.Database.CloseConnection(); } } return fileId; } } 

(Lo stesso, non posso testarlo quindi non esitare a condividere correzioni / miglioramenti nei commenti se necessario)

Il metodo è un metodo di estensione quindi devi solo chiamarlo in questo modo:

 var newFileId = dbContext.GetNextFileId(); 

Per fare ciò, è necessario installare: Microsoft.EntityFrameworkCore.Relational .

Il tuo input è molto apprezzato, quindi l’ho risolto con un sacco di aiuto da @Pete,

La mia class di modello SP,

 public class FileIdSeq { [Key] public DateTime SequenceDate { get; set; } [DefaultValue(1)] public int LastSequence { get; set; } } 

Il mio SQL-SP,

 USE [ARTCORE] GO /****** Object: StoredProcedure [dbo].[FileIdSeqSP] Script Date: 08/04/2017 10:19:24 PM ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER PROCEDURE [dbo].[FileIdSeqSP] AS BEGIN Declare @today date = getdate() Declare @table table (id int) SET NOCOUNT ON; If Exists(select * from information_schema.columns where table_name = 'FileIdSeq' and column_name = 'LastSequence' and Table_schema = 'dbo' and column_default is NULL) BEGIN ALTER TABLE [dbo].FileIdSeq ADD DEFAULT (1) FOR LastSequence END BEGIN TRAN UPDATE FileIdSeq SET LastSequence = LastSequence + 1 output inserted.LastSequence into @table where [email protected] if (@@ROWCOUNT=0) INSERT INTO FileIdSeq (SequenceDate) output inserted.LastSequence into @table VALUES (@today) commit declare @s varchar(20) select @s = convert(varchar(20), id) from @table if (Len(@s)<4) set @s = Right('000' + @s, 4) SELECT Cast(CONVERT(VARCHAR(10), @today, 12) + @s as int) as LastSequence, SequenceDate FROM FileIdSeq WHERE (SequenceDate = @today) END