Entity Framework Include OrderBy random генерирует повторяющиеся данные

Когда я извлекаю список элементов из базы данных, включая некоторые дочерние элементы (через .Include), и случайный порядок, EF дает мне неожиданный результат .. Я создаю / клонирую дополнительные элементы ..

Чтобы лучше объяснить себя, я создал небольшой и простой проект EF CodeFirst для воспроизведения проблемы. Сначала я дам вам код этого проекта.

Проект

Создайте базовый проект MVC3 и добавьте пакет EntityFramework.SqlServerCompact через Nuget.
Это добавляет последние версии следующих пакетов:

  • 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 может иметь несколько адресов.
Заряжаем сгенерированную базу данных на 3 человека и 5 адресов.
Если мы возьмем всех людей из базы данных, включая адреса, рандомизируем результаты и просто распечатаем имена этих людей, вот где все пойдет не так.

В результате у меня иногда бывает 4 человека, иногда 5, иногда 3, а я ожидаю 3. Всегда.
например:

  • Человек 1
  • Человек 3
  • Человек 1
  • Человек 3
  • Человек 2

Итак ... это копирование / клонирование данных! И это не круто ..
Просто кажется, что 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

Обходные пути

  1. Если Я удаляю .Include (p => p.Addresses) из запроса, все идет нормально. но, конечно, адреса не загружаются, и доступ к этой коллекции будет вызывать каждый раз новый вызов базы данных.
  2. Сначала я могу получить данные из базы данных, а потом их рандомизировать, просто добавив .ToList () перед .OrderBy .. вот так: var person = db.Persons.Include (p => p.Addresses) ) .ToList (). OrderBy (p => Guid.NewGuid ());

Кто-нибудь знает, почему это происходит вот так?
Может быть, это ошибка в SQL поколение?

17
задан Filip Cornelissen 24 February 2012 в 14:25
поделиться