Стремительная загрузка в .Net4 Link-to-EF; может быть, как DataLoadOptions - PullRequest
3 голосов
/ 23 ноября 2010

Я использую VS2010 .Net4 Linq-EntityFramework и хотел бы явно загрузить некоторые дочерние данные. Я хотел бы предоставить функциональность, аналогичную DataLoadOptions или LoadWith , которые доступны для Linq-to-SQL IIUC, но недоступны для Linq-to-EF.

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

Я пытаюсь предоставить безопасную для загрузки схему загрузки, как описано в Блог MosesOfEgypt . Я изменил поколение T4, и у меня, как мне кажется, последняя проблема. В .Net4 свойства сущности возвращают ObjectSet . Но, к сожалению, функция Include возвращает ObjectQuery , который является базовым классом для ObjectSet .

Вот подмножество класса ObjectContext, сгенерированное из модифицированного шаблона T4:

    #region DataLoadOptions Functionality

            public DataLoadOptions LoadOptions { get; set; }

            private ObjectSet<TEntity> ApplyDataLoadOptions<TEntity>(string queryString) where TEntity : class
            {
                var query = CreateObjectSet<TEntity>(queryString);

                if (LoadOptions != null)
                {
                    var members = LoadOptions.GetPreloadedMembers<TEntity>();

                    foreach (var member in members)
                    {
 **********              query = query.Include(member.Name);
                    }
                }
                return query;
            }

    #endregion



    #region ObjectSet Properties

        /// <summary>
        /// No Metadata Documentation available.
        /// </summary>
        public ObjectSet<Address> Addresses
        {
            get
            {
                if ((_Addresses == null))
                {
                    _Addresses = ApplyDataLoadOptions<Address>("Addresses");
                }
                return _Addresses;
            }
        }

    #endregion

Строка, начинающаяся с «*», - это место, где происходит приведение от ObjectQuery к ObjectSet . И это недопустимое преобразование, поэтому, если оно явно приведено во время разработки, произойдет сбой во время выполнения, если я не делаю это неправильно.

Одним из решений может быть написание метода расширения для ObjectSet.Include , чтобы он возвращал ObjectSet вместо ObjectQuery . Я хотел бы знать, как найти исходный код функции ObjectQuery.Include , если это возможно. И я не уверен, что эти решения будут работать.

Также интересно, был ли способ повысить результаты функции Включить с ObjectQuery до ObjectSet . Опять же, не уверен, что это сработает.

Буду признателен за любую помощь в реализации возможности DataLoadOptions для Linq-to-EF в .Net4.

Ответы [ 2 ]

1 голос
/ 24 ноября 2010

Не совсем понятно, о чем ты здесь спрашиваешь.

.Include возвращает ObjectQuery<T>, который реализует IQueryable<T>.Почему вы хотите разыграть его как ObjectSet<T> ??

WRT "строго типизированный" Include, краткий ответ: вы не можете.Но вы можете сделать его более приятным с помощью синтаксического описания методов расширения.

Мы создаем перечисления для каждой сущности (может быть, излишним, но я ненавижу магические строки), которые содержат каждую ассоциацию.Наш репозиторий принимает массив этих перечислений.Затем мы используем метод расширения для перечисления для преобразования в Include.

Пример (упрощенный) Код репозитория:

public ICollection<Order> GetOrdersWithUser(Expression<Func<Order,bool>> predicate, OrderAssocations[] includes)
{
   return _ctx.Orders.WithAssociations(includes).Where(predicate).ToList();
}

И расширение:

public static ObjectQuery<Order> WithAssociations(this ObjectQuery<Order> source, OrderAssociations[] includes)
{
   var query = source;

   foreach (var include in includes)
   {
      query = query.Include(include.ToNavigationalProperty()));
   }

   return query;
}

.ToNavigationalProperty() - это другой метод расширения перечисления, который просто возвращает соответствующее свойство Navigational.

0 голосов
/ 23 ноября 2010

Не уверен, что это ответит на ваш вопрос ... однако вот как я сделал "динамическую" нетерпеливую загрузку:

/// <summary>
    /// Flags to indicate which child entities to eager load
    /// </summary>
    [Flags]
    public enum IncludeFlags
    {
        None = 1,
        All = 1 << 1,
        ChildEntity1 = 1 << 2,
        ChildEntity2 = 1 << 3,
        ChildEntity3 = 1 << 4
    }

    /// <summary>
    /// Method to create my object query
    /// </summary>
    /// <param name="context">Database context</param>
    /// <param name="includeFlags">Indicates which flags to Include</param>
    /// <returns></returns>
    private static ObjectQuery<MyEntity> GetContext(DataBaseContext context, IncludeFlags includeFlags)
    {
        ObjectQuery<MyEntity> query = new ObjectQuery<MyEntity>("MyEntity", context);

        if ((includeFlags & IncludeFlags.None) != IncludeFlags.None)
        {
            bool getAll = ((includeFlags & IncludeFlags.All) == IncludeFlags.All);
            if (getAll || ((includeFlags & IncludeFlags.ChildEntity1) == IncludeFlags.ChildEntity1))
                query = query.Include("ChildEntity1");

            if (getAll || ((includeFlags & IncludeFlags.ChildEntity2) == IncludeFlags.ChildEntity2))
                query = query.Include("ChildEntity2");

            if (getAll || ((includeFlags & IncludeFlags.ChildEntity3) == IncludeFlags.ChildEntity3))
                query = query.Include("ChildEntity2.ChildEntity3");
        }

        return query;
    }

    public static MyEntity[] GetMyEntities(IncludeFlags flags = IncludeFlags.None)
    {
        DataBaseContext db = new DataBaseContext();
        from e in GetContext(db, flags)
        select e;

        return e.ToArray();
    }

    public void GetMyEntities()
    {
        MyEntity[] entities = Test.GetMyEntities(); // Does not load any child entities
        MyEntity[] entities2 = Test.GetMyEntities(IncludeFlags.All); // Loads them all
        MyEntity[] entities3 = Test.GetMyEntities(IncludeFlags.ChildEntity1 | IncludeFlags.ChildEntity2); // Only loads child 1 and 2
        MyEntity[] entities4 = Test.GetMyEntities(IncludeFlags.ChildEntity3); // Only loads ChildEntity2.ChildEntity3
    }

Надеюсь, это поможет.

...