ADO.NET Entity Framework Иерархические данные - PullRequest
1 голос
/ 06 сентября 2011

Рассмотрим следующую модель базы данных:

Feed database model

И следующий код запроса:

using (var context = new DatabaseEntities())
{
    return context.Feed.ToHierarchy(f => f.Id_Parent, null);
}

Где ToHierarchy является расширением ObjectSet<TEntity> как:

public static List<TEntity> ToHierarchy<TEntity, TProperty>(this ObjectSet<TEntity> set, Func<TEntity, TProperty> parentIdProperty, TProperty idRoot) where TEntity : class
{
    return set.ToList().Where(f => parentIdProperty(f).Equals(idRoot)).ToList();
}

Это приведет к примеру ответа в формате JSON:

[
  {
    "Description":"...",
    "Details":[ ],
    "Id":1,
    "Id_Parent":null,
    "Title":"...",
    "URL":"..."
  },
  {
    "Description":"...",
    "Details":[
      {
        "Description":"...",
        "Details":[ ],
        "Id":4,
        "Id_Parent":3,
        "Title":"...",
        "URL":"..."
      },
      {
        "Description":"...",
        "Details":[
          {
            "Description":"...",
            "Details":[
              {
                "Description":"...",
                "Details":[ ],
                "Id":7,
                "Id_Parent":6,
                "Title":"...",
                "URL":"..."
              }
            ],
            "Id":6,
            "Id_Parent":5,
            "Title":"...",
            "URL":"..."
          }
        ],
        "Id":5,
        "Id_Parent":3,
        "Title":"...",
        "URL":null
      }
    ],
    "Id":3,
    "Id_Parent":null,
    "Title":"...",
    "URL":null
  }
]

Как вы, возможно, заметили, ToHierarchy метод должен (и, очевидно, действительно) извлекать все строки из заданного набора (плоские) и возвращать их иерархическое представление в соответствии с "родительским свойством".

Когда я был в середине моей реализации, я быстро попробовал свой код, и на удивление это сработало! Теперь я представляю, как странно это звучит для многих из вас, но я действительно не понимаю, почему или как этот кусок кода работает, хотя я вроде написал это самостоятельно ...

Не могли бы вы объяснить, как это работает?

P.S .: если вы посмотрите ближе, ToHierarchy - это не то же самое, что .Include("Details").

1 Ответ

1 голос
/ 06 сентября 2011

Это работает, потому что set.ToList загрузит все записи из таблицы базы данных в ваше приложение, а все остальное будет сделано EF и его механизмом отслеживания изменений, который должен обеспечить правильную ссылку между связанными сущностями.

Btw. вы фильтруете записи в памяти вашего приложения, а не в базе данных. Например, если ваша таблица содержит 10000 записей и ваш фильтр должен возвращать только 10, вы все равно загрузите все 10000 из базы данных.

Вы обнаружите, что реализовать это с EF довольно сложно, потому что EF не поддерживает иерархические данные. Вы всегда закончите с плохим решением. Единственным хорошим решением является использование хранимых процедур и некоторая поддержка иерархических запросов в базе данных - например, общих табличных выражений (CTE) на SQL-сервере.

Я только что сделал этот очень простой пример, и он работает так, как я описал в комментарии:

public class SelfReferencing
{
    public int Id { get; set; }
    public string Name { get; set; }
    public SelfReferencing Parent { get; set; }
    public ICollection<SelfReferencing> Children { get; set; } 
}

public class Context : DbContext
{
    public DbSet<SelfReferencing> SelfReferencings { get; set; }
}

public class Program
{
    static void Main(string[] args)
    {
        using (var context = new Context())
        {
            context.Database.Delete();
            context.Database.CreateIfNotExists();

            context.SelfReferencings.Add(
                new SelfReferencing()
                    {
                        Name = "A",
                        Children = new List<SelfReferencing>()
                            {
                                new SelfReferencing()
                                    {
                                        Name = "AA",
                                        Children = new List<SelfReferencing>()
                                            {
                                                new SelfReferencing()
                                                    {
                                                        Name = "AAA"
                                                    }
                                            }
                                    },
                                new SelfReferencing()
                                    {
                                        Name = "AB",
                                        Children = new List<SelfReferencing>()
                                            {
                                                new SelfReferencing()
                                                    {
                                                        Name = "ABA"
                                                    }
                                            }
                                    }
                            }
                    });

            context.SaveChanges();
        }

        using (var context = new Context())
        {
            context.Configuration.LazyLoadingEnabled = false;
            context.Configuration.ProxyCreationEnabled = false;

            var data = context.SelfReferencings.ToList();
        }
    }
}

В нем используется первый подход кода, но внутри он такой же, как при использовании EDMX. Когда я получаю data, у меня есть 5 объектов в списке, и все они правильно настроили Parent и Children свойства навигации.

...