我在将 telerik RadGrid 和普通 ASP.NET GridView 绑定到以下 LINQ to 实体查询的结果时遇到问题。在这两种情况下,网格都包含正确的行数,但仅前几行的数据会在所有其他行中重复。我直接将此代码的返回值分配给网格上的 DataSource 属性。
public IEnumerable<DirectoryPersonEntry> FindPersons(string searchTerm)
{
DirectoryEntities dents = new DirectoryEntities();
return from dp in dents.DirectoryPersonEntrySet
where dp.LastName.StartsWith(searchTerm) || dp.Extension.StartsWith(searchTerm)
orderby dp.LastName, dp.Extension
select dp;
}
这是有效的替代纯 ADO.NET 代码:
DataTable ret = new DataTable();
using (SqlConnection sqn = new SqlConnection(ConfigurationManager.ConnectionStrings["WaveAdo"].ConnectionString))
{
SqlDataAdapter adap = new SqlDataAdapter("select * from DirectoryPersonList where LastName like '" + searchTerm + "%' order by LastName ", sqn);
sqn.Open();
adap.Fill(ret);
}
return ret;
根据下面 Marc Gravel 提供的非常合乎逻辑且合适的建议,我发现 EF 设计者对我的实体类的实体键做出了错误的猜测:其字段列表中的第一个字段是 Department,其中只有大约七个条目在所有其他记录中共享。
这确实是重复的原因。如果我可以更改或删除实体密钥就好了,但我不能。
在我看来,你的主键坏了。 LINQ-to-SQL 和 EF 的“身份管理”方面意味着,每当它看到相同对象类型的相同主键值时,它就有义务返回相同的实例。
例如,给定数据:
id | name | ...
-------+------------+------
1 | Fred | ...
2 | Barney | ...
1 | Wilma | ...
1 | Betty | ...
然后 如果在从 LINQ 迭代对象时认为
id
是主键,则强制给你“Fred”、“Barney”、“Fred”、“Fred”。本质上,当它再次看到 id
1 时,它甚至不会查看其他列 - 它只是从身份缓存中获取具有 id
1 的实例 - 并为您提供之前为您提供的相同 Fred 实例。如果它不认为id
是主键,它会将每一行视为一个单独的对象(如果其中一个字段中的值与另一条记录相同怎么办 - 这并不罕见).
我建议检查您标记为主键(在 DBML/EDM 模型中)的任何字段确实每行都是唯一的。在上面的例子中,
id
列显然不代表唯一标识符,因此不适合作为主键。只需在 LINQ-to-SQL / EF 设计器中取消标记即可。
更新:特别是,请查看设计器中各种属性的“Entity Key”属性 - 特别是在查询视图时。检查“Entity Key”是否仅针对合适的列(即使行唯一的列)设置为 true。如果设置不正确,则设置为 false。这也显示为黄色钥匙图标 - 这只应该出现在真正是记录唯一标识符的事物上。
如果将链接查询括在括号中并使用 .Distinct() 扩展名?
public IEnumerable<DirectoryPersonEntry> FindPersons(string searchTerm)
{
DirectoryEntities dents = new DirectoryEntities();
return (from dp in dents.DirectoryPersonEntrySet
where dp.LastName.StartsWith(searchTerm) || dp.Extension.StartsWith(searchTerm)
orderby dp.LastName, dp.Extension
select dp).Distinct();
}
工作查询和损坏查询之间的一个区别是 orderby 子句。我在 Linq to Entities 的 orderby 实现中发现了一个已记录的错误...可能还有其他错误。
尝试从损坏的查询中删除 orderby 并查看是否仍然得到重复项。
另一个区别是 where 子句中的 OR。尝试仅使用第一部分 [ where dp.LastName.StartsWith(searchTerm) ] 并查看是否仍然出现重复项。
我遇到了同样的问题并通过解决方法解决了它。我将其发布在这里,因为它可能会帮助其他来到这里的人。
不要选择 dp ,而是使用
select new <ObjectName>
{
a = v.a
b = v.b
}.
这不会返回重复项。