GUID is one way if you are lazy...
Implementing a separate thread-safe service that creates call signs is another (auto-increment or similar). Probably want to store either in DDS or SQL database...I use SQL database for another project.
Something similar to this
public class IdService : IIdService { public const string DefaultSequenceId = "GLOBAL"; public const int DefaultBatchSize = 1000; private static readonly ConcurrentDictionary<string, SequenceInfo> _sequenceInfos = new ConcurrentDictionary<string, SequenceInfo>(); private IConfigService _configService { get; set; } public IdService(IConfigService configService) { _configService = configService; } public long NewId(string sequenceId = null, int? batchSize = null) { sequenceId = sequenceId ?? DefaultSequenceId; batchSize = batchSize ?? DefaultBatchSize; var sequenceInfo = _sequenceInfos.GetOrAdd(sequenceId, key => new SequenceInfo()); if (batchSize < 1) { throw new Exception($"The batch size was set to {batchSize}, but it must be greater than 0."); } lock (sequenceInfo.Lock) { if (sequenceInfo.Next > sequenceInfo.Until || sequenceInfo.Next == 0) { //Skapa connection mot databasen using (var con = new SqlConnection(_configService.ConnectionString)) { //Öppna databasen con.Open(); SqlTransaction tx = null; try { //Starta transaction tx = con.BeginTransaction(IsolationLevel.Serializable); //Läs nästa värde using (var cmd = con.CreateCommand()) { cmd.Transaction = tx; cmd.CommandText = "select [Next] from [Sequence] where [SequenceId] = @sequenceId"; cmd.Parameters.AddWithValue("@sequenceId", sequenceId); sequenceInfo.Next = (long) cmd.ExecuteScalar(); } //Tills ... sequenceInfo.Until = sequenceInfo.Next + batchSize.Value - 1; //Uppdatera sekvensen i databasen using (var cmd = con.CreateCommand()) { cmd.Transaction = tx; cmd.CommandText = "update [Sequence] set [Next] = @next where [SequenceId] = @sequenceId"; cmd.Parameters.AddWithValue("@next", sequenceInfo.Until + 1); cmd.Parameters.AddWithValue("@sequenceId", sequenceId); cmd.ExecuteNonQuery(); } tx.Commit(); } catch (Exception exc) { tx?.Rollback(); throw new Exception($"Could not generate id from {sequenceId}", exc); } con.Close(); } } return sequenceInfo.Next++; } } private class SequenceInfo { public long Next { get; set; } public long Until { get; set; } public object Lock { get; } public SequenceInfo() { Lock = new object(); } } }
Hi Daniel,
Thanks for the reply.
In our scenario, call-sign is content which is editable. We expect at least 5000 of this particular block to be created so we need to be able to inform editors if the call-sign exists as they may have trouble remembering/finding the block otherwise.
Regards
Ben
Store them in a static dictionary with call sign as key and do lookup vs that instead (in a threadsafe manner with locks)?
I'm using that to match users on personal identity number / emails for 100 000s of users. Takes zero time :)
Initialize dictionary on site startup using Episerver find to get all existing keys. Add new key to dictionary in the onpublishing event / validation...
Hello,
We have a requirement to ensure that a property (Callsign) on a block is unique in our site.
We are currently inherting IValidate to allow us to validate the block and within the Validate method we use the method below to get all blocks of the same type so we can iterate over them and check the values accordingly to find if it already exists.
This works for the most part but if I create/edit two blocks in quick succession then the index doesnt appear to keep up and allows duplicates. If I wait around five minutes between edits all appears to work OK.
Snippet