Comment générer un nombre de keys personnalisées à l'aide de C # dans ASP.NET Core

c'est ma 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 ssortingng FName { get; set; } [Required, Display(Name = "Middle Name")] public ssortingng MName { get; set; } [Required, Display(Name = "Last Name")] public ssortingng LName { get; set; } [Required, Display(Name = "Date of Birth"), DisplayFormat(DataFormatSsortingng = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] public DateTime Dob { get; set; } } 

"FileId" est ma key primaire et je veux la générer en sauvegardant l'logging et le sauvegarder avec l'logging,

Numéro personnalisé aurait la spécification suivante, YYMMDD0001 où YY est deux numbers de l'année, MM Deux numbers du mois. DD est deux numbers du jour, 001 est le démarrage en série et réinitialiser tous les jours.

Ceci est mon 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<IActionResult> Create([Bind("FileId,FName,MName,LName,Dob")] PatReg patReg) { if (ModelState.IsValid) { _context.Add(patReg); await _context.SaveChangesAsync(); return RedirectToAction("Index"); } return View(patReg); } 

Context

J'avais l'habitude de générer ce nombre en utilisant la procédure stockée SQL comme ci-dessous,

  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" représente la séquence sérielle que je consulte depuis "SetTblSrls" et j'avais l'habitude d'avoir un sortinggger sur la table cible pour mettre à jour ce nombre sur chaque insert par lequel j'obtiens un nouveau nombre chaque fois que je génère le FileId

Comment puis-je le faire en utilisant EF et C #,

Vous allez devoir conserver un numéro de séquence quelque part afin de pouvoir l'incrémenter en toute security à chaque fois que vous avez un nouveau file. En-memory n'est pas vraiment une option puisque IIS réinitialise ses pools d'applications toutes les 29 heures par défaut, perdant tout ce que vous avez mis en cache. En tant que tel, vous vous retrouvez avec la database ou le système de files.

Le SQL suivant vous fournit un moyen sûr et performant d'get le prochain numéro de séquence disponible en exécutant simplement la procédure stockée à partir de votre code C # côté server et en lisant la valeur renvoyée:

 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 SequenceDate=@today 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 

Cette solution est basée sur les utilisations des séquences SQL Server (disponibles depuis SQL Server 2012 et Azure SQL Database ). Si vous ne pouvez pas les utiliser, vous pouvez passer à une autre réponse.


Cette solution consiste à créer une séquence qui va calculer le FileId automatiquement. Mais vous devrez réinitialiser la séquence tous les jours à minuit pour get ce que vous voulez. Voici comment vous pouvez créer la séquence:

 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 

(Ou quelque chose comme ça, je n'ai pas de moteur SQL Server pour les tester. N'hésitez pas à les corriger dans les commentaires si nécessaire)

Pour exécuter le script tous les jours, vous pouvez utiliser un agent SQL. C'est à mon avis la meilleure option, mais vous pouvez aussi lancer un nouveau thread dans votre application qui exécutera le script tous les jours.

Si vous préférez cette option, voici comment vous pouvez le faire. Je vais vous laisser décider où vous devez mettre ce code dans votre application:

 // 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(); } } } 

(S'il vous plaît noter que je ne gère pas la fermeture de votre application.Vous devez probablement annuler la tâche à ce moment-là, et probablement d'autres choses comme ça)

Une fois que votre séquence a été créée, il vous suffit d'interroger cette séquence pour get votre nouvel identifiant de file. SQL Engine gère automatiquement les appels simultanés et s'assure que chaque ID renvoyé est unique.

Il semble que nous ne puissions pas exécuter de requêtes brutes avec EF Core comme nous le ferions avec EF6 ( dbContext.Data.SqlQuery ). Une solution serait d'exécuter manuellement une command sql. Je ne sais pas comment ces opérations (get la connection, l'ouvrir, etc.) sont thread safe donc je préfère être en security et utiliser un mécanisme de locking:

 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; } } 

(Pareil, je ne peux pas le tester, donc ne pas hésiter à partager des corrections / améliorations dans les commentaires si nécessaire)

La méthode est une méthode d'extension, il suffit de l'appeler ainsi:

 var newFileId = dbContext.GetNextFileId(); 

Pour ce faire, vous devrez installer: Microsoft.EntityFrameworkCore.Relational .

S'il vous plaît votre consortingbution est très appréciée, donc je l'ai résolu avec beaucoup d'aide de @Pete,

Ma class de model SP,

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

Mon 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 SequenceDate=@today 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