Entity framework qui utilise beaucoup de mémoire
Voici une image de la FOURMIS memory profiler. Il parlent qu'il y a beaucoup d'objets en mémoire. Comment puis-je savoir ce que je fais de mal?
**UPDATE**
Voici mes classes de dépôt:
public class Repository<T> : IRepository<T> where T : class, IDataEntity
{
ObjectContext _context;
IObjectSet<T> _objectSet;
readonly string _entitySetName;
readonly string[] _keyNames;
private ObjectContext Context
{
get
{
if (_context == null)
{
_context = GetCurrentUnitOfWork<EFUnitOfWork>().Context;
}
return _context;
}
}
private IObjectSet<T> ObjectSet
{
get
{
if (_objectSet == null)
{
_objectSet = this.Context.CreateObjectSet<T>();
}
return _objectSet;
}
}
public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>() where TUnitOfWork : IUnitOfWork
{
return (TUnitOfWork)UnitOfWork.Current;
}
public virtual IEnumerable<T> GetQuery()
{
return ObjectSet;
}
public virtual IEnumerable<T> GetQuery(params Expression<Func<T, object>>[] includes)
{
return ObjectSet.IncludeMultiple(includes);
}
public virtual IEnumerable<T> GetQuery(
IEnumerable<Expression<Func<T, bool>>> filters,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy,
IEnumerable<Expression<Func<T, object>>> includes)
{
IQueryable<T> _query = ObjectSet;
if (filters != null)
{
foreach (var filter in filters)
{
_query = _query.Where(filter);
}
}
if (includes != null && includes.Count() > 0)
{
_query = _query.IncludeMultiple(includes.ToArray());
}
if (orderBy != null)
{
_query = orderBy(_query);
}
return _query;
}
public virtual IPaged<T> GetQuery(
IEnumerable<Expression<Func<T, bool>>> filters,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy,
int pageNumber, int pageSize,
IEnumerable<Expression<Func<T, object>>> includes)
{
IQueryable<T> _query = ObjectSet;
if (filters != null)
{
foreach (var filter in filters)
{
_query = _query.Where(filter);
}
}
if (orderBy != null)
{
_query = orderBy(_query);
}
IPaged<T> page = new Paged<T>(_query, pageNumber, pageSize, includes);
return page;
}
public virtual void Insert(T entity)
{
this.ObjectSet.AddObject(entity);
}
public virtual void Delete(T entity)
{
if (entity is ISoftDeletable)
{
((ISoftDeletable)entity).IsDeleted = true;
//Update(entity);
}
else
{
this.ObjectSet.DeleteObject(entity);
}
}
public virtual void Attach(T entity)
{
ObjectStateEntry entry = null;
if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == false)
{
this.ObjectSet.Attach(entity);
}
}
public virtual void Detach(T entity)
{
ObjectStateEntry entry = null;
if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == true)
{
this.ObjectSet.Detach(entity);
}
}
}
Maintenant, si j'ai Une classe qui contient les enregistrements de la table A, j'ai aussi créer une classe:
public class ARepository:BaseRepository<A> {
//Implementation of A's queries and specific db operations
}
Voici mon EFUnitOfWork classe:
public class EFUnitOfWork : IUnitOfWork, IDisposable
{
public ObjectContext Context { get; private set; }
public EFUnitOfWork(ObjectContext context)
{
Context = context;
context.ContextOptions.LazyLoadingEnabled = true;
}
public void Commit()
{
Context.SaveChanges();
}
public void Dispose()
{
if (Context != null)
{
Context.Dispose();
}
GC.SuppressFinalize(this);
}
}
Et UnitOfWork classe:
public static class UnitOfWork
{
private const string HTTPCONTEXTKEY = "MyProj.Domain.Business.Repository.HttpContext.Key";
private static IUnitOfWorkFactory _unitOfWorkFactory;
private static readonly Hashtable _threads = new Hashtable();
public static void Commit()
{
IUnitOfWork unitOfWork = GetUnitOfWork();
if (unitOfWork != null)
{
unitOfWork.Commit();
}
}
public static IUnitOfWork Current
{
get
{
IUnitOfWork unitOfWork = GetUnitOfWork();
if (unitOfWork == null)
{
_unitOfWorkFactory = ObjectFactory.GetInstance<IUnitOfWorkFactory>();
unitOfWork = _unitOfWorkFactory.Create();
SaveUnitOfWork(unitOfWork);
}
return unitOfWork;
}
}
private static IUnitOfWork GetUnitOfWork()
{
if (HttpContext.Current != null)
{
if (HttpContext.Current.Items.Contains(HTTPCONTEXTKEY))
{
return (IUnitOfWork)HttpContext.Current.Items[HTTPCONTEXTKEY];
}
return null;
}
else
{
Thread thread = Thread.CurrentThread;
if (string.IsNullOrEmpty(thread.Name))
{
thread.Name = Guid.NewGuid().ToString();
return null;
}
else
{
lock (_threads.SyncRoot)
{
return (IUnitOfWork)_threads[Thread.CurrentThread.Name];
}
}
}
}
private static void SaveUnitOfWork(IUnitOfWork unitOfWork)
{
if (HttpContext.Current != null)
{
HttpContext.Current.Items[HTTPCONTEXTKEY] = unitOfWork;
}
else
{
lock(_threads.SyncRoot)
{
_threads[Thread.CurrentThread.Name] = unitOfWork;
}
}
}
}
Voici comment j'utilise ceci:
public class TaskPriceRepository : BaseRepository<TaskPrice>
{
public void Set(TaskPrice entity)
{
TaskPrice taskPrice = GetQuery().SingleOrDefault(x => x.TaskId == entity.TaskId);
if (taskPrice != null)
{
CommonUtils.CopyObject<TaskPrice>(entity, ref taskPrice);
}
else
{
this.Insert(entity);
}
}
}
public class BranchRepository : BaseRepository<Branch>
{
public IList<Branch> GetBranchesList(Guid companyId, long? branchId, string branchName)
{
return Repository.GetQuery().
Where(b => companyId == b.CompanyId).
Where(b => b.IsDeleted == false).
Where(b => !branchId.HasValue || b.BranchId.Equals(branchId.Value)).
Where(b => branchName == null || b.BranchName.Contains(branchName)).
ToList();
}
}
[WebMethod]
public void SetTaskPrice(TaskPriceDTO taskPrice)
{
TaskPrice tp = taskPrice.ToEntity();
TaskPriceRepository rep = new TaskPriceRepository();
rep.Set(tp);
UnitOfWork.Commit();
}
[WebMethod]
public IList<Branch> GetBranchesList()
{
BranchRepository rep = new BranchRepository();
return rep.GetBranchesList(m_User.UserCompany.CompanyId, null, null).ToList();
}
J'espère que c'est assez d'info pour m'aider à résoudre le problème. Merci.
UPDATE 2
Il est également UnitOfWorkFactory qui initialise UnitOfWork:
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private static Func<ObjectContext> _objectContextDelegate;
private static readonly Object _lockObject = new object();
public static void SetObjectContext(Func<ObjectContext> objectContextDelegate)
{
_objectContextDelegate = objectContextDelegate;
}
public IUnitOfWork Create()
{
ObjectContext context;
lock (_lockObject)
{
context = _objectContextDelegate();
}
return new EFUnitOfWork(context);
}
}
Afin de l'utiliser, dans le démarrage de l'application j'utilise structuremap:
ObjectFactory.Initialize(x =>
{
x.For<IUnitOfWorkFactory>().Use<UnitOfWorkFactory>();
x.For(typeof(IRepository<>)).Use(typeof(Repository<>));
});
Que font vos requêtes?
En outre, ce n'votre EF requêtes? Êtes-vous accidentellement en tirant un grand nombre d'entités en mémoire quand vous avez seulement besoin d'un peu d'?
Vous devez décrire ce que vous faites avec EF, ce type d'application, êtes-vous du bâtiment et de la façon dont vous travaillez avec les entités. Sinon, cette question est candidat à être fermés que pas une vraie question, parce que nous ne pouvons pas répondre.
Mrnka: je pense ce post. J'ai planty de code.. je crois que la requête select ne vais pas en dire beaucoup, je vais poster quelque chose.
Mrnka, @Henk Holterman, @Julie Lerman: j'ai ajouté plus d'infos.
SELECT * FROM LargeTable
?En outre, ce n'votre EF requêtes? Êtes-vous accidentellement en tirant un grand nombre d'entités en mémoire quand vous avez seulement besoin d'un peu d'?
Vous devez décrire ce que vous faites avec EF, ce type d'application, êtes-vous du bâtiment et de la façon dont vous travaillez avec les entités. Sinon, cette question est candidat à être fermés que pas une vraie question, parce que nous ne pouvons pas répondre.
Mrnka: je pense ce post. J'ai planty de code.. je crois que la requête select ne vais pas en dire beaucoup, je vais poster quelque chose.
Mrnka, @Henk Holterman, @Julie Lerman: j'ai ajouté plus d'infos.
OriginalL'auteur Naor | 2011-05-15
Vous devez vous connecter pour publier un commentaire.
J'ai un pressentiment que vous ne jetez pas le contexte.
Je suggère de disposer du contexte à chaque fois que vous avez fait de l'interaction avec la base de données.
Utilisation
using
déclaration chaque fois que vous créez le contexte.[Modifier]
Aussi loin que je peux voir, vous le cache et ne jetez pas vos
EFUnitOfWork
objet. Il est à usage unique, ce qui est correct, mais je ne vois pas quand jetable est appelé. Semble que vous contenir une référence au contexte de l'application et de l'exécution.En outre, vous créez et maintenez un contexte par thread, ce qui le rendra encore pire.
Je ne peux pas vous dire pour sûr où vous devez mettre
Dispose
ouusing
, que je ne connais pas les usages.Vous pourriez le mettre probablement à votre
Commit
méthode, mais je ne sais pas si leCommit
appelée qu'une seule fois au cours de base de données de la session d'interaction.Aussi, votre conception peut être trop compliqué.
Si j'étais vous, je voudrais:
Si j'avais le temps je ferais solution à long terme.
Mais encore une fois, je ne peux pas dire si la complexité de votre conception est justifiée, car je ne sais pas comment grand votre demande est et ce qu'il fait et quelles sont les exigences.
Je suis d'accord que dans certains scénarios, il serait préférable de conserver le contexte. Cependant, je pense que dans la plupart des cas, il est plus robuste, évolutive et moins de bug enclins à céder dès que vous le pouvez.
Aza: Veuillez voir les mises à jour
Pas clair à partir de votre code comment EFUnitOfWork est instancié.
Aza: Vous droit - j'ai mis à jour le code avec l'usine qui crée la UnitOfWork.
OriginalL'auteur Alex Aza
Quelques choses qui viennent à mon esprit:
using(var context = CreateObjectContext())
blocfrom customer in context.Customers select customer
au lieu de fairefrom customer in context.Customers select customer.FirstOrDefault()
J'ai eu du mal à le faire Entity Framework pour travailler dans un N-l'application de la couche. Il n'est tout simplement pas approprié pour l'utilisation dans les N-tier apps comme est. Seulement EF 4.0. Vous pouvez lire tous mon aventure dans la prise de EF 3 dans un N-tier app.
http://www.codeproject.com/KB/linq/ef.aspx
Est-ce répondre à votre question?
AL Zabir: Veuillez voir les mises à jour
En regardant ton code, oui, il n'y a pas de disposer. Le UnitOfWork crée un Objectcontext sur le thread en cours, mais il n'est pas de la Jeter.
J'ai l'habitude de le faire dans mon application web: Sur Application_EndRequest, je boucle sur tous les éléments de la HttpContext.Les articles et voir ceux qui sont IDisposable et appellent leur méthode dispose. Cela va nettoyer tous les UnitOfWork. Mais encore plus architectural préoccupation est UnitOfWork ne doivent pas être stockés dans le Contexte. Vous pouvez avoir UnitOfWork provenir de n'importe où. Il peut provenir d'une couche de gestion, à l'intérieur de certaines affaires façade méthode peut-être. UnitOfwork est utilisé à l'intérieur d'une aide(...) bloc de clairement marquer l'endroit où le travail commence et où le travail se termine.
AL Zabir: Quelles sont les raisons UnitOfWork ne doivent pas être stockés dans leur Contexte?
OriginalL'auteur oazabir
Vous désactivez la
ObjectContext
de temps en temps. Si vous gardez unObjectContext
en vie pour un long moment, ce consomment de la mémoire liée à la taille de la EntityDataModel et le nombre d'Entités chargées dans ceObjectContext
.Tout comme Alex Aza est de l'assumer. Je ne vois pas l'élimination de la objectcontext / EFUnitOfwork. @Alex Aza: je suis d'accord avec vous que la conception a l'air plutôt d'être trop compliqué.
OriginalL'auteur Bernoulli IT