Anonymous Type with Linq and Guid
I have a simple table:
ID | Value
When I do this:
var sequence = from c in valuesVault.GetTable()
select new {RandomIDX = Guid.NewGuid(), c.ID, c.Value};
each element in the projection has the value of the same guid... How do I write this so that I get a different random guid value for each of my element in the projection?
Edit
To clarify on the issue. The GetTable() method simply calls this:
return this.context.GetTable<T>();
where the this.contenxt is the DataContext of type T.
The itteration is done as it's always done, nothing fancy:
foreach (var c in seq)
{
Trace.WriteLine(c.RandomIDX + " " + c.Value);
}
Output:
bf59c94e-119开发者_开发问答c-4eaf-a0d5-3bb91699b04d What is/was your mother's maiden name?
bf59c94e-119c-4eaf-a0d5-3bb91699b04d What was the last name of one of your high school English teachers?
bf59c94e-119c-4eaf-a0d5-3bb91699b04d In elementary school, what was your best friend's first and last name?
Edit 2 Using out the box linq2Sql Provider. I had built some generic wrappers around it but they do not alter the way IQuaryable or IEnumerable function in the code.
What is underneath valuesVault.GetTable()
?
You probably have a Linq provider such as Linq 2 SQL.
That means that valuesVault.GetTable()
is of type IQueryable
which in turn means that the entire query becomes an expression.
An expression is a query that is defined but not yet executed.
When sequence
is being iterated over, the query is executed using the Linq provider and that Linq provider and one of the steps it has to perform is to execute this expression: Guid.NewGuid()
. Most Linq providers cannot pass that expression to the underlying source (SQL Server wouldn't know what to do with it) so it gets executed once and the result of the execution returned with the rest of the result.
What you could do is to force the valuesVault.GetTable()
expression to become a collection by calling the .ToList()
or .ToArray()
methods. This executes the expression and returns an IEnumerable
which represents an in-memory collection.
When performing queries against an IEnumerable
, the execution is not passed to the Linq provider but executed by the .NET runtime.
In your case this means that the expression Guid.NewGuid()
can be executed correctly.
Try this:
var sequence = from c in valuesVault.GetTable().ToArray()
select new {RandomIDX = Guid.NewGuid(), c.ID, c.Value};
Notice the .ToArray() there. That is what will make the statement go from IQueryable
to IEnumerable
and that will change its behaviour.
I think it's happening when it gets translated into SQL (ie: it's the database doing it). Since you have no WHERE clauses in your example, you could just do:
var sequence = from c in valuesVault.GetTable().ToList()
select new { RandomID = Guid.NewGuid(), c.ID, c.Value };
Which forces Guid.NewGuid() to be executed in the client. However, it's ugly if your table grows and you start adding filtering clauses. You could solve it by using a second LINQ query that projects a second result set with your new GUIDs:
var sequence = from c in valuesVault.GetTable()
where c.Value > 10
select new { c.ID, c.Value };
var finalSequence = from s in sequence.ToList()
select new { RandomID = Guid.NewGuid(), s.ID, s.Value };
Seems to work for me.
List<int> a = new List<int> {10, 11, 12, 13};
var v = a.Select(i => new {ID = Guid.NewGuid(), I = i});
foreach (var item in v)
{
Console.WriteLine(item);
}
output
{ ID = b760f0c8-8dcc-458e-a924-4401ce02e04c, I = 10 }
{ ID = 2d4a0b17-54d3-4d69-8a5c-d2387e50f054, I = 11 }
{ ID = 906e1dc7-6de4-4f8d-b1cd-c129142a277a, I = 12 }
{ ID = 6a67ef6b-a7fe-4650-a8d7-4d2d3b77e761, I = 13 }
I'm not able to reproduce this behavior with a simple LINQ query. Sample:
List<int> y = new List<int> { 0, 1, 2, 3, 4, 5 };
var result = y.Select(x => new { Guid = Guid.NewGuid(), Id = x }).ToList();
I'm imagining if you try to convert your Table value to a List in Linq, then perform your select, you'll get different Guids.
精彩评论