I have created a script to generate 5 random codes, check if any one of them exists in the database, and if not, insert the record. The script will throw an exception if a single code could not be generated.
The rationale is that of an arbitrary 5 random codes, at least one should be unique. If not, throw an exception.
Is there a better way to ensure that a random code is generated without trying infinitely?
Note: This is a LinqPad script in C#.
Approach #1: Total execution time ~15 seconds for 1k codes.
void Main()
{
bool exceptionThrown = false;
int NumberOfVouchersToGenerate = 1000;
using (var scope = new TransactionScope())
{
try
{
for (int i = 0; i < NumberOfVouchersToGenerate; i++)
{
// generate a list of voucher codes 0 to N
List<string> voucherCodes = Enumerable.Range(0, MaxGenerateUniqueVoucherAttempts)
.Select(_ => GenerateCode(VoucherCodeLength)).ToList();
// see if any of the voucher codes we generated already exist in the database
var existingVouchers = Vouchers.Select(v => v.Code).Where(v => voucherCodes.Contains(v)).ToList();
// find a voucher code from the list we generated that is not currently being used in the database
var availableVoucherCode = voucherCodes.Where(v => !existingVouchers.Select(e => e).Contains(v)).FirstOrDefault();
if (availableVoucherCode != null)
{
Vouchers.InsertOnSubmit(new Voucher { MapGuid = Guid.NewGuid(), Code = availableVoucherCode, Percent = 100, CreatedDate = DateTime.Now }); // entity framework equivalent to entity<T>.Add(T);
SubmitChanges(); // entity framework equiavalent to context.SaveChanges();
availableVoucherCode.Dump();
}
else
{
throw new ArgumentException($"Could not generate {NumberOfVouchersToGenerate} unique codes.");
}
}
}
catch (Exception ex)
{
ex.Dump();
exceptionThrown = true;
}
finally
{
if (!exceptionThrown)
scope.Complete();
}
}
}
private const int VoucherCodeLength = 10;
private const int MaxGenerateUniqueVoucherAttempts = 5;
// since Random does not make any guarantees of thread-safety, use different Random instances per thread
private static readonly ThreadLocal<Random> _random = new ThreadLocal<Random>(() => new Random());
// Define other methods and classes here
private static string GenerateCode(int numberOfCharsToGenerate)
{
char[] chars = "ACDEFGHJKLMNPQRTUVWXYZ234679".ToCharArray(); // uppercase only; remove similar looking characters such as 1IilS5s0Oo etc...
var sb = new StringBuilder();
for (int i = 0; i < numberOfCharsToGenerate; i++)
{
int num = _random.Value.Next(0, chars.Length);
sb.Append(chars[num]);
}
return sb.ToString();
}
Approach #2: Total execution time ~12 seconds for 1k codes.
void Main()
{
bool exceptionThrown = false;
int NumberOfVouchersToGenerate = 1000;
using (var scope = new TransactionScope())
{
try
{
// generate a list of voucher codes 0 to N
List<string> voucherCodes = Enumerable.Range(0, NumberOfVouchersToGenerate)
.Select(_ => GenerateCode(VoucherCodeLength)).ToList();
// see if any of the voucher codes we generated already exist in the database
var existingVouchers = Vouchers.Select(v => v.Code).Where(v => voucherCodes.Contains(v)).ToList();
if (existingVouchers.Count() == 0)
{
foreach (var voucherCode in voucherCodes)
{
Vouchers.InsertOnSubmit(new Voucher { MapGuid = Guid.NewGuid(), Code = voucherCode, Percent = 100, CreatedDate = DateTime.Now }); // entity framework equivalent to entity<T>.Add(T);
SubmitChanges(); // entity framework equiavalent to context.SaveChanges();
voucherCode.Dump();
}
}
else
{
throw new ArgumentException($"Could not generate {NumberOfVouchersToGenerate} unique codes.");
}
}
catch (Exception ex)
{
ex.Dump();
exceptionThrown = true;
}
finally
{
if (!exceptionThrown)
scope.Complete();
}
}
}
private const int VoucherCodeLength = 10;
// since Random does not make any guarantees of thread-safety, use different Random instances per thread
private static readonly ThreadLocal<Random> _random = new ThreadLocal<Random>(() => new Random());
// Define other methods and classes here
private static string GenerateCode(int numberOfCharsToGenerate)
{
char[] chars = "ACDEFGHJKLMNPQRTUVWXYZ234679".ToCharArray(); // uppercase only; remove similar looking characters such as 1IilS5s0Oo etc...
var sb = new StringBuilder();
for (int i = 0; i < numberOfCharsToGenerate; i++)
{
int num = _random.Value.Next(0, chars.Length);
sb.Append(chars[num]);
}
return sb.ToString();
}