Кадрирование EF: как полностью отсоединить объекты * до их вставки в кеш HttpRuntime?

Некоторая предыстория:

Работа с:

  • .NET 4.5 (мышление перехода на 4.5.1, если оно безболезненно)
  • Веб-формы
  • Entity Framework 5, Lazy Loading enabled
  • Контекст на запрос
  • IIS 8
  • Datacenter Windows 2012

Опасность: использование памяти

В рамках проекта, который мы сейчас проводим, и, возможно, нашего первого крупного проекта, мы часто читаем некоторые более крупные фрагменты данных, поступающие из импорта CSV, которые, вероятно, останутся неизменными в течение очень длительных периодов времени.

Если кто-то явно не импортирует CSV-данные, они гарантированно будут такими же, это происходит в более чем одном месте в нашем проекте, и аналогичный подход используется для некоторых обычных документов, которые часто читаются пользователями. Мы решили кэшировать эти данные в кеше HttpRuntime.

Это происходит так, и мы тянем около 15 000 записей, состоящих в основном из строк.

//myObject and related methods are placeholders
public static List<myObject> GetMyCachedObjects()
{
    if (CacheManager.Exists(KeyConstants.keyConstantForMyObject))
    {
        return CacheManager.Get(KeyConstants.keyConstantForMyObject) as List<myObject>;
    }
    else
    {
        List<myObject> myObjectList = framework.objectProvider.GetMyObjects();
        CacheManager.Add(KeyConstants.keyConstantForMyObject, myObjectList, true, 5000);
        return myObjectList;
    }
}

Получение данных для вышеуказанного метода очень просто и выглядит следующим образом:

public List<myObject> GetMyObjects()
{
    return context.myObjectsTable.AsNoTracking().ToList();
}

Вероятно, есть вещи, которые нужно сказать о структуре кода, но это не моя проблема в настоящий момент.

Я начал профилировать наш проект, как только увидел высокую память и нашел много частей, где наш код мог быть оптимизирован. Я никогда не сталкивался с 300 одновременными пользователями, и наши внутренние тесты, сделанные самими, были недостаточными, чтобы показать проблемы с памятью. Я выделил и исправил многочисленные утечки памяти, но я хотел бы понять некоторые неизвестные Entity Framework.

Учитывая приведенный выше пример и используя ANTS Profiler, я заметил, что " myObject" и другие подобные объекты ссылаются на многие System.Data.Entity.DynamicProxies.myObject, кроме того, существует много EntityKeys, которые сохраняются для целых чисел. Они не принимают много, но их количество относительно велико.

Например, 124 экземпляра " myObject" ссылаются почти на 300 System.Data.Entity.DynamicProxies.

Обычно он выглядит так, независимо от объекта: Некоторая запись в кэш, некоторый объект, который я кэшировал, и теперь я заметил, что многие из них были отделены от предыдущего кэширования dbContext, динамических прокси и objectContext. Я не знаю, как развязать их.

enter image description here

Мой прогресс:

Я провел некоторое исследование и выяснил, что я могу кэшировать что-то, связанное с объектами Framework Entity Framework. Я вытащил их с помощью NoTracking, но в памяти все еще есть те DynamicProxies, которые, вероятно, увлекаются и другими вещами.

Важно: Я наблюдал некоторые живые экземпляры ObjectContext (74), медленно растущие, но не экземпляры моего unitOfWork, который держит dbContext. Кажется, что они правильно расположены на основе запроса.

Я знаю, как отделять, прикреплять или изменять состояние записи из моего dbContext, который завернут в unitOfWork, и я часто это делаю. Однако этого недостаточно, или я прошу о невозможности.

Вопросы:

  • В принципе, что я делаю неправильно с моим методом кэширования, когда дело доходит до Entity Framework?
  • Является ли растущее число объектных контекстов в памяти проблемой, я знаю, что кэш в конечном итоге истечет, но я беспокоюсь о открытых соединениях или чем-то еще, что может иметь этот контекст.
  • Должен ли я отделять все от контекста, прежде чем вставлять его в кеш?
  • Если да, то какой лучший подход. Особенно со списком я не могу думать ни о чем другом, кроме повторного набора коллекции и вызова отделить один за другим.
  • Бонусный вопрос: около 40% потребленной памяти является бесплатной (нераспределенной), я не знаю, почему .NET резервирует так много свободной памяти заранее.

Ответ 1

Вы можете попробовать использовать класс не-сущности с определенными свойствами с помощью метода SELECT.

public class MyObject2 {
   public int ID { get; set; }  
   public string Name { get; set; }
}

public List<MyObject2> GetObjects(){  
   return framework.provider.GetObjects().Select(
      x=> new MyObject2{
         ID = x.ID ,
         Name = x.Name
      }).ToList();
   );
}

Поскольку вы будете хранить простые объекты С#, вам не придется беспокоиться о динамических прокси. Вам вообще не нужно будет звонить в отдел. Также вы можете хранить только несколько свойств.

Даже если вы отключите отслеживание, вы увидите динамический прокси-сервер, потому что EF использует динамический класс, полученный из вашего класса, который хранит дополнительную информацию метаданных (отношение e.g. имя внешнего ключа и т.д. к другим объектам) для объекта.

Ответ 2

для уменьшения памяти здесь:

  • Re new контекст, часто  Не пытайтесь удалять контент из контекста. Или Отключите его.  Он висит вокруг, как пердеть в телефонной коробке
     например, context = new MyContext.
    Но если возможно, вы должны быть

используя (var context = new Mycontext) {....}
//Краткосрочные контексты - лучшая практика.

  • В Контексте вы можете установить Конфигурации
 this.Configuration.LazyLoadingEnabled = false;  
 this.Configuration.ProxyCreationEnabled = false;   //<<<<<<<<<<< THIS one   
 this.Configuration.AutoDetectChangesEnabled = false;

вы можете отключить прокси, если вы все еще чувствуете, что они забивают память. Но это может потребоваться, если вы примените using к контексту в первую очередь.

Ответ 3

Я бы немного пересмотрел решение:

  • Вы сохраняете все данные как одну запись в кеше

Я бы переместил это и имел запись в элемент кэша.

  • Вы используете кеш HTTPRuntime

Я бы использовал Appfabric Caching, также MS, также бесплатно.

  • Не уверен, где вы вызываете этот код из

Я бы назвал его при запуске приложения, тогда все данные хранятся в памяти, когда пользователю это нужно

  • Вы используете Entity SQL

Для этого я бы использовал устройство чтения данных Entity http://msdn.microsoft.com/en-us/library/system.data.entityclient.entitydatareader(v=vs.110).aspx

См. также:

http://msdn.microsoft.com/en-us/data/hh949853.aspx