C# 使用Nhibernate检查已用钥匙的最佳方法?
在我的网站上,我允许人们批量购买我网站的订阅(我称之为代金券)。一旦他们有了这些凭证,他们就会把它们交给任何人,并将代码输入到他们的帐户中进行升级 现在我正在考虑编写4个字母数字代码(大写、小写和数字),并将有类似的内容C# 使用Nhibernate检查已用钥匙的最佳方法?,c#,.net,asp.net-mvc,nhibernate,C#,.net,Asp.net Mvc,Nhibernate,在我的网站上,我允许人们批量购买我网站的订阅(我称之为代金券)。一旦他们有了这些凭证,他们就会把它们交给任何人,并将代码输入到他们的帐户中进行升级 现在我正在考虑编写4个字母数字代码(大写、小写和数字),并将有类似的内容 var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var stringChars = new char[4]; var random = new Random(); fo
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[4];
var random = new Random();
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[random.Next(chars.Length)];
}
var finalString = new String(stringChars);
var chars=“abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzo123456789”;
var stringChars=新字符[4];
var random=新的random();
for(int i=0;i
现在,我认为这将给我足够多的组合,如果我用完了,我总是可以增加代码的长度。我想保持简短,因为我不希望用户必须输入大量的数字
我也没有时间做一个更优雅的解决方案,可能是他们点击电子邮件中的链接或其他东西,激活了他们的帐户,当然这会减少人们试图随机猜测凭证号码的次数
如果这个网站越来越受欢迎,我会处理这些事情
我想知道如何处理可能重复生成同一张凭证的问题。我的第一个想法是在每次创建凭证时检查数据库,如果凭证存在,则创建一个新的凭证
然而,这似乎是缓慢的。所以我想也可以先把所有的密钥存储在内存中,然后在内存中进行检查,但是如果列表继续增长,我可能会遇到内存不足的异常和所有这些很棒的东西 有人有什么想法吗?还是我一直在做上面列出的两种方法中的一种 我正在使用nhibernate、asp.net mvc和C 编辑
static void Main(string[] args)
{
List<string> hold = new List<string>();
for (int i = 0; i < 10000; i++)
{
HashAlgorithm sha = new SHA1CryptoServiceProvider();
byte[] result = sha.ComputeHash(BitConverter.GetBytes(i));
string hex = null;
foreach (byte x in result)
{
hex += String.Format("{0:x2}", x);
}
hold.Add(hex.Substring(0,3));
Console.WriteLine(hex.Substring(0, 4));
}
Console.WriteLine("Number of Distinct values {0}", hold.Distinct().Count());
}
static void Main(字符串[]args)
{
列表保持=新列表();
对于(int i=0;i<10000;i++)
{
HashAlgorithm sha=新的SHA1CryptoServiceProvider();
byte[]result=sha.ComputeHash(BitConverter.GetBytes(i));
字符串hex=null;
foreach(结果中的字节x)
{
十六进制+=String.Format(“{0:x2}”,x);
}
hold.Add(十六进制子串(0,3));
控制台写入线(十六进制子字符串(0,4));
}
WriteLine(“不同值的数量{0}”,hold.Distinct().Count());
}
以上是我尝试使用哈希的尝试。然而,我认为我错过了一些东西,因为它似乎有相当多的重复比预期
编辑2
我想我补充了我所缺少的,但不确定这是否正是他的意思。我也不知道当我把它移动到我能移动的地方时该怎么办(我的has似乎给了我40个我能移动的地方)
static void Main(字符串[]args)
{
int subStringLength=4;
列表保持=新列表();
对于(int i=0;i<10000;i++)
{
SHA1CryptoServiceProvider sha=新的SHA1CryptoServiceProvider();
byte[]result=sha.ComputeHash(BitConverter.GetBytes(i));
字符串hex=null;
foreach(结果中的字节x)
{
十六进制+=String.Format(“{0:x2}”,x);
}
int开始位置=0;
字符串possibleVoucherCode=十六进制子字符串(起始位置,子字符串长度);
字符串voucherCode=Move(子字符串长度、保持、十六进制、起始位置、可能的voucherCode);
保留.添加(凭证代码);
}
WriteLine(“不同值的数量{0}”,hold.Distinct().Count());
}
私有静态字符串移动(int subStringLength、列表保持、字符串十六进制、int startingposition、string possibleVoucherCode)
{
if(hold.Contains(可能为Uchercode))
{
int newPosition=startingposition+1;
if(新位置十六进制长度)
{
possibleVoucherCode=十六进制子字符串(newPosition,subStringLength);
返回移动(子字符串长度、保持、十六进制、新位置、可能的触发代码);
}
//归还某物
返回“0”;
}
其他的
{
//归还某物
返回“0”;
}
}
其他的
{
返回可能的用户代码;
}
}
}
您可能的解决方案如下:查找凭证的最大ID(整数)。然后,对其运行任何哈希函数,取前32位并转换为要向用户显示的字符串(或使用32位哈希函数,如)。这可能会起作用,哈希冲突非常罕见。但是这个解决方案在随机性方面与您的非常相似。
您可以运行一个测试,找到前10个或100个碰撞(这对您来说应该足够了),并强制算法“跳过”它们并使用不同的起始值。然后,您根本不需要检查数据库(至少在您获得大约4294967296张凭证之前……)
使用nHibernate的算法如何?
这是一个关于如何获取下一个值(无需DB访问)的示例。速度会很慢,因为您希望随机生成凭证,然后检查数据库中生成的每个代码 我将创建一个带有id、代码和is_used列的表
凭单。我会用足够多的随机代码填满那张表。因为这可以在单独的过程中完成,所以性能不会有太大问题。让它在晚上运行,第二天你会得到一张满桌的凭单
如果您想防止生成重复的凭证,这将不是一个问题
static void Main(string[] args)
{
int subStringLength = 4;
List<string> hold = new List<string>();
for (int i = 0; i < 10000; i++)
{
SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
byte[] result = sha.ComputeHash(BitConverter.GetBytes(i));
string hex = null;
foreach (byte x in result)
{
hex += String.Format("{0:x2}", x);
}
int startingPositon = 0;
string possibleVoucherCode = hex.Substring(startingPositon,subStringLength);
string voucherCode = Move(subStringLength, hold, hex, startingPositon, possibleVoucherCode);
hold.Add(voucherCode);
}
Console.WriteLine("Number of Distinct values {0}", hold.Distinct().Count());
}
private static string Move(int subStringLength, List<string> hold, string hex, int startingPositon, string possibleVoucherCode)
{
if (hold.Contains(possibleVoucherCode))
{
int newPosition = startingPositon + 1;
if (newPosition <= hex.Length)
{
if ((newPosition + subStringLength) > hex.Length)
{
possibleVoucherCode = hex.Substring(newPosition, subStringLength);
return Move(subStringLength, hold, hex, newPosition, possibleVoucherCode);
}
// return something
return "0";
}
else
{
// return something
return "0";
}
}
else
{
return possibleVoucherCode;
}
}
}
''//given intValue as your random integer
Dim result As String = String.Empty
Dim digits as String = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
Dim x As Integer
While (intValue > 0)
x = intValue Mod digits.Length
result = digits(x) & result
intValue = intValue - x
intValue = intValue \ digits.Length
End While
Return result
SELECT DISTINCT b.Code
FROM @batch b
WHERE NOT EXISTS (
SELECT v.Code
FROM dbo.Voucher v
WHERE v.Code = b.Code
);
DECLARE @batchid uniqueidentifier;
SET @batchid = NEWID();
INSERT INTO dbo.Voucher (Code, BatchId)
SELECT DISTINCT b.Code, @batchid
FROM @batch b
WHERE NOT EXISTS (
SELECT Code
FROM dbo.Voucher v
WHERE b.Code = v.Code
);
SELECT Code
FROM dbo.Voucher
WHERE BatchId = @batchid;
CREATE TYPE dbo.VoucherCodeList AS TABLE (
Code nvarchar(8) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL
/* !!! Remember to specify the collation on your Voucher.Code column too, since you want upper and lower-case codes. */
);
public ICollection<string> GenerateCodes(int numberOfCodes)
{
var result = new List<string>(numberOfCodes);
while (result.Count < numberOfCodes)
{
var batchSize = Math.Min(_batchSize, numberOfCodes - result.Count);
var batch = Enumerable.Range(0, batchSize)
.Select(x => GenerateRandomCode());
var oldResultCount = result.Count;
result.AddRange(FilterAndSecureBatch(batch));
var filteredBatchSize = result.Count - oldResultCount;
var collisionRatio = ((double)batchSize - filteredBatchSize) / batchSize;
// Automatically increment length of random codes if collisions begin happening too frequently
if (collisionRatio > _collisionThreshold)
CodeLength++;
}
return result;
}
private IEnumerable<string> FilterAndSecureBatch(IEnumerable<string> batch)
{
using (var command = _connection.CreateCommand())
{
command.CommandText = _sqlQuery; // the concurrency-safe query listed above
var metaData = new[] { new SqlMetaData("Code", SqlDbType.NVarChar, 8) };
var param = command.Parameters.Add("@batch", SqlDbType.Structured);
param.TypeName = "dbo.VoucherCodeList";
param.Value = batch.Select(x =>
{
var record = new SqlDataRecord(metaData);
record.SetString(0, x);
return record;
});
using (var reader = command.ExecuteReader())
while (reader.Read())
yield return reader.GetString(0);
}
}