我想为某个日期的Linq查询结果生成唯一标识符。 最初我想过使用Guid,但我不得不即兴发现这个问题。 但是,我想看看是否有人可以使用Guid解决方案,所以我们去了。
想象一下我们有:
class Query
{
public class Entry
{
public string Id { get; set; }
public int Value { get; set; }
}
public static IEnumerable<Entry> GetEntries( IEnumerable<int> list)
{
var result =
from i in list
select new Entry
{
Id = System.Guid.NewGuid().ToString("N"),
Value = i
};
return result;
}
}
现在我们希望Id对于每个条目都是唯一的,但是我们需要这个值对于从GetEntries获得的IEnumerable的每次遍历都是相同的。这意味着我们要调用以下代码:
List<int> list = new List<int> { 1, 2, 3, 4, 5 };
IEnumerable<Query.Entry> entries = Query.GetEntries(list);
Console.WriteLine("first pass");
foreach (var e in entries) { Console.WriteLine("{0} {1}", e.Value, e.Id); }
Console.WriteLine("second pass");
foreach (var e in entries) { Console.WriteLine("{0} {1}", e.Value, e.Id); }
给我们类似的东西:
第一次通过
1 47f4a21a037c4ac98a336903ca9df15b 2 f339409bde22487e921e9063e016b717 3 8f41e0da06d84a58a61226a05e12e519 4 013cddf287da46cc919bab224eae9ee0 5 6df157da4e404b3a8309a55de8a95740
第二次传递
1 47f4a21a037c4ac98a336903ca9df15b 2 f339409bde22487e921e9063e016b717 3 8f41e0da06d84a58a61226a05e12e519 4 013cddf287da46cc919bab224eae9ee0 5 6df157da4e404b3a8309a55de8a95740
但是我们得到:
第一次通过
1 47f4a21a037c4ac98a336903ca9df15b 2 f339409bde22487e921e9063e016b717 3 8f41e0da06d84a58a61226a05e12e519 4 013cddf287da46cc919bab224eae9ee0 5 6df157da4e404b3a8309a55de8a95740
第二次传递
1 a9433568e75f4f209c688962ee4da577 2 2d643f4b58b946ba9d02b7ba81064274 3 2ffbcca569fb450b9a8a38872a9fce5f 4 04000e5dfad340c1887ede0119faa16b 5 73a11e06e087408fbe1909f509f08d03
现在再看看上面的代码,我意识到我的错误在哪里: 将ID分配给Guid.NewGuid()。每次遍历集合时都会调用ToString(“N”),因此每次都会不同。
那我该怎么办呢? 有没有办法让我放心,我每次都只能收到一份该系列的副本? 有没有办法让我确定我不会得到查询结果的新实例?
提前感谢您的时间:)
答案 0 :(得分:5)
这是所有LINQ查询的固有内容。可重复是巧合,不能保证。
您可以使用.ToList()
解决此问题,例如:
IEnumerable<Query.Entry> entries = Query.GetEntries(list).ToList();
或者更好的是,移动.ToList()
内的GetEntries()
答案 1 :(得分:1)
也许您需要生成一次条目列表,并且每次都在GetEntries
返回相同的列表。
编辑:
啊不,你每次都得到不同的名单!那么,这取决于你想要得到什么。如果您希望为每个特定Id
获取相同的Value
,可能在不同的列表中,则需要缓存Id
:您应该拥有Dictionary<int, Guid>
。存储已分配的GUID。如果您希望每个源列表的GUID都是唯一的,您可能需要将输入缓存为IEnumerable
,并始终检查此输入列表是否已经返回。
编辑:
如果您不想为GetEntries
的不同运行共享相同的GUID,则应该“实现”查询(例如,将return result;
替换为return result.ToList();
),因为它是在你的问题的评论中建议。
否则每次遍历列表时都会运行查询。这就是所谓的懒惰评估。惰性求值通常不是问题,但在您的情况下,它会导致重新计算每个查询运行的GUID(即结果序列上的每个循环)。
答案 2 :(得分:1)
你有什么理由要使用LINQ吗?以下似乎对我有用:
public static IEnumerable<Entry> GetEntries(IEnumerable<int> list)
{
List<Entry> results = new List<Entry>();
foreach (int i in list)
{
results.Add(new Entry() { Id = Guid.NewGuid().ToString("N"), Value = i });
}
return results;
}
答案 3 :(得分:1)
这是因为linq的工作方式。当您只返回linq查询时,每次枚举它时都会执行它。因此,对于每个列表项,Guid.NewGuid将在您对查询进行枚举时执行多次。
尝试在查询上迭代一次后,尝试将项添加到列表中,您将看到,当第二次迭代时,刚刚添加的列表项也将出现在结果集中。那是因为linq查询包含列表的实例,而不是独立的副本。
要获得始终相同的结果,请返回数组或列表而不是linq查询,因此将GetEntries
方法的返回行更改为:
return result.ToArray();
这会强制立即执行,也只会发生一次。
最诚挚的问候,
Oliver Hanappi
答案 4 :(得分:0)
你可能认为不使用Guid,至少不使用“new”。
使用GetHashCode()
会返回在您多次遍历列表时不会更改的唯一值。
问题是您的列表是IEnumerable<int>
,因此每个项目的哈希码与其值一致。
您应该重新评估您的方法并使用不同的策略。我想到的一件事是使用用集合的哈希码初始化的伪随机数生成器。只要用相同的值初始化,它就会返回相同的数字。但是,再次忘记Guid
答案 5 :(得分:0)
一个建议:(不知道这是不是你的情况)
如果要将条目保存在数据库中,请尝试在数据库级别为条目的主键指定Guid。这样,每个条目都将有一个唯一且持久的Guid作为其主键。结帐this link了解详情。