linq select中的System.Guid.NewGuid()

时间:2010-11-06 10:20:18

标签: c# linq

我想为某个日期的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”),因此每次都会不同。

那我该怎么办呢? 有没有办法让我放心,我每次都只能收到一份该系列的副本? 有没有办法让我确定我不会得到查询结果的新实例?

提前感谢您的时间:)

6 个答案:

答案 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了解详情。