【发布时间】:2011-12-18 14:59:18
【问题描述】:
当我从包含一些子项的数据库中检索项目列表(通过 .Include)并随机排序时,EF 给了我一个意想不到的结果..我创建/克隆了添加项..
为了更好地解释自己,我创建了一个小而简单的 EF CodeFirst 项目来重现该问题。 首先我会给你这个项目的代码。
项目
创建一个基本的MVC3项目并通过Nuget添加EntityFramework.SqlServerCompact包。
这会添加以下软件包的最新版本:
- EntityFramework v4.3.0
- SqlServerCompact v4.0.8482.1
- EntityFramework.SqlServerCompact v4.1.8482.2
- WebActivator v1.5
模型和 DbContext
using System.Collections.Generic;
using System.Data.Entity;
namespace RandomWithInclude.Models
{
public class PeopleContext : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<Address> Addresses { get; set; }
}
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class Address
{
public int ID { get; set; }
public string AdressLine { get; set; }
public virtual Person Person { get; set; }
}
}
数据库设置和种子数据:EF.SqlServerCompact.cs
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using RandomWithInclude.Models;
[assembly: WebActivator.PreApplicationStartMethod(typeof(RandomWithInclude.App_Start.EF), "Start")]
namespace RandomWithInclude.App_Start
{
public static class EF
{
public static void Start()
{
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
Database.SetInitializer(new DbInitializer());
}
}
public class DbInitializer : DropCreateDatabaseAlways<PeopleContext>
{
protected override void Seed(PeopleContext context)
{
var address1 = new Address {AdressLine = "Street 1, City 1"};
var address2 = new Address {AdressLine = "Street 2, City 2"};
var address3 = new Address {AdressLine = "Street 3, City 3"};
var address4 = new Address {AdressLine = "Street 4, City 4"};
var address5 = new Address {AdressLine = "Street 5, City 5"};
context.Addresses.Add(address1);
context.Addresses.Add(address2);
context.Addresses.Add(address3);
context.Addresses.Add(address4);
context.Addresses.Add(address5);
var person1 = new Person {Name = "Person 1", Addresses = new List<Address> {address1, address2}};
var person2 = new Person {Name = "Person 2", Addresses = new List<Address> {address3}};
var person3 = new Person {Name = "Person 3", Addresses = new List<Address> {address4, address5}};
context.Persons.Add(person1);
context.Persons.Add(person2);
context.Persons.Add(person3);
}
}
}
控制器:HomeController.cs
using System;
using System.Data.Entity;
using System.Linq;
using System.Web.Mvc;
using RandomWithInclude.Models;
namespace RandomWithInclude.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
var db = new PeopleContext();
var persons = db.Persons
.Include(p => p.Addresses)
.OrderBy(p => Guid.NewGuid());
return View(persons.ToList());
}
}
}
视图:Index.cshtml
@using RandomWithInclude.Models
@model IList<Person>
<ul>
@foreach (var person in Model)
{
<li>
@person.Name
</li>
}
</ul>
这应该是全部,你的应用程序应该编译:)
问题
如您所见,我们有 2 个简单的模型(Person 和 Address),Person 可以有多个 Address。
我们为生成的数据库播种3个人和5个地址。
如果我们从数据库中获取所有人员(包括地址)并随机化结果并打印出这些人员的姓名,那就是一切都出错了。
因此,我有时会得到 4 个人,有时会得到 5 个人,有时会得到 3 个人,而我预计会有 3 个。总是。
例如:
- 人 1
- 第三个人
- 人 1
- 第三个人
- 第二个人
所以.. 它正在复制/克隆数据!这并不酷..
似乎 EF 失去了对哪个地址是哪个人的孩子的跟踪..
生成的 SQL 查询是这样的:
SELECT
[Project1].[ID] AS [ID],
[Project1].[Name] AS [Name],
[Project1].[C2] AS [C1],
[Project1].[ID1] AS [ID1],
[Project1].[AdressLine] AS [AdressLine],
[Project1].[Person_ID] AS [Person_ID]
FROM ( SELECT
NEWID() AS [C1],
[Extent1].[ID] AS [ID],
[Extent1].[Name] AS [Name],
[Extent2].[ID] AS [ID1],
[Extent2].[AdressLine] AS [AdressLine],
[Extent2].[Person_ID] AS [Person_ID],
CASE WHEN ([Extent2].[ID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
FROM [People] AS [Extent1]
LEFT OUTER JOIN [Addresses] AS [Extent2] ON [Extent1].[ID] = [Extent2].[Person_ID]
) AS [Project1]
ORDER BY [Project1].[C1] ASC, [Project1].[ID] ASC, [Project1].[C2] ASC
解决方法
- 如果我从查询中删除
.Include(p =>p.Addresses),一切正常。但当然不会加载地址,并且每次访问该集合都会对数据库进行新的调用。 - 我可以先从数据库中获取数据,然后通过在 .OrderBy.. 之前添加一个 .ToList() 来进行随机化,如下所示:
var persons = db.Persons.Include(p => p.Addresses).ToList().OrderBy(p => Guid.NewGuid());
有人知道为什么会这样吗?
这可能是 SQL 生成中的错误吗?
【问题讨论】:
-
“这可能是 SQL 生成中的错误” - 您是否尝试过执行这两个查询(工作/不工作)并查看它们的结果是否不同?无论如何。我认为这只是您在框架中发现的另一个错误。请参阅msdn.microsoft.com/en-us/library/bb896317.aspx 和“Projecting to an Anonymous Type”,这听起来有点相关。在这个框架中有一些可疑的东西需要被杀死。
-
@stefan:是的,我试过了,当然会返回不同的结果,这确实指向了一个 sql 生成错误。但我不认为它已经在那里列出了。。
-
顺便说一句,SQL Server 上的 EF 4.1(在 2008 R2 上测试)也会出现问题,而不仅仅是 SQLCompact。我认为,@np-hard 的答案是正确的,问题出在 EF 的对象实现中,而不是在生成的 SQL 中。如果
OrderBy不在父实体的属性上,就会发生废话。
标签: asp.net-mvc linq entity-framework random sql-order-by