2

I have a base repository that looks like this:

public class BaseRepository<T> : IBaseRepository<T> where T : class { private DbContext _context; private IDbSet<T> _dbSet; protected DbContext Context { get { if (_context == null) { EFUnitOfWork currentUnitOfWork = (EFUnitOfWork)UnitOfWork.Current; _context = currentUnitOfWork.Context; } return _context; } } protected IDbSet<T> DbSet { get { if (_dbSet == null) { _dbSet = Context.Set<T>(); } return _dbSet; } } public void Add(T entity) { DbSet.Add(entity); } public void Attach(T entity) { DbSet.Attach(entity); } public void Delete(T entity) { DbSet.Remove(entity); } public void Update(T entity) { Context.Entry(entity).State = System.Data.EntityState.Modified; } public IQueryable<T> Get(string[] includes=null) { IQueryable<T> set = DbSet; if (includes != null) { foreach (string include in includes) { set = set.Include(include); } } return set; } 
  1. User user = _usersRepository.Get().SingleOrDefault(u => u.Username == "gigi"); This returns the user without the Roles property, which is ok.

  2. User user = _usersRepository.Get(new string[] { "Roles" }).SingleOrDefault(u => u.Username == "gigi"); This returns the user and the Roles property, which is ok.

  3. List<User> users = _usersRepository.Get().Where(u => u.Username.StartsWith("gi")).ToList();

  4. List<User> users = _usersRepository.Get(new string[] { "Roles" }).Where(u => u.Username.StartsWith("gi")).ToList();

Query 3 and 4 both return a list of users with the Roles property. Why is query 3 returns Roles?

LE: This is the call, i examine the users collection after the context is disposed.

 List<User> users = _usersRepository.Get().Where(u => u.Username.StartsWith("gi")).ToList(); UnitOfWork.Current.Dispose(); 

LE2: I did the same thing separately:

  1.  List<User> users; using (MyEntities ctx = new MyEntities ()) { users= ctx.Users.ToList(); } 
  2.  List<User> users; using (MyEntities ctx = new MyEntities ()) { users= ctx.Users.Include("Roles").ToList(); } 

In the first case the Roles are not loaded, in the second they are which is ok.

I don't see what i am doing wrong in the repository sample.

LE3: This is the Unit of work

 public class UnitOfWork { private const string HTTPCONTEXTKEY = "Repository.Key"; private static IUnitOfWorkFactory _unitOfWorkFactory; private static readonly Hashtable _threads = new Hashtable(); 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; } } } } 
4
  • Are you fetching the Roles table in your unit of work before you fetch the Users? Commented Sep 23, 2012 at 20:52
  • You're getting your DbContext from your UnitOfWork. If you fetch the rows from the Roles table in a DbContext and then fetch a User in the same DbContext, then EF will populate the User.Roles collection without an Include. Commented Sep 23, 2012 at 20:58
  • I'm not doing anything with the roles. Commented Sep 23, 2012 at 21:01
  • Oh, i understand now. Thanks. You are right. I was calling Query 3 after Query 1 Commented Sep 23, 2012 at 21:05

2 Answers 2

2

EF will try to populate as many properties as it can.

If you have loaded a database row into a DbContext, EF will remember that row's data for the lifetime of the DbContext.

Then, when you load any entity that references that row, EF will populate that property with or without an Include clause.

In your case, you are loading ( some of ) the Roles table in Query 2.
When you run Query 3, those rows are populated without an Include because they are already in the DbContext.

Sign up to request clarification or add additional context in comments.

1 Comment

Aaaah, good catch! I should not write "most likely"... I'll delete my answer.
1

This line looks suspiciously like a Singleton.

EFUnitOfWork currentUnitOfWork = (EFUnitOfWork)UnitOfWork.Current; 

If it is, and you're using the classic Singleton pattern where you use a static member to maintain the instance then you are playing with dynamite. You should never make your data context a static.

The reasons are many. The first is that this means you context will never get destroyed, and will continue to consume memory for change tracking until you exhaust memory (or the worker process restarts).

The second, and biggest reason is that statics are shared between all threads of the process, which means multiple users will be using the same context, and they will very likely stomp all over each other, destroying any kind of consistency.

EF data contexts are not thread safe, and they're also not concurrency safe (they are two different things).

This line:

UnitOfWork.Current.Dispose(); 

Is also very bad. You shouldn't be calling dispose like that, unless you are VERY careful. Again, if your context is static, then you could be disposing of it while another thread is using it.

All in all, your real problems are related to having data pre-loaded in your caches, and sometimes not. I suggest that you seriously reconsider how you are using your UnitOfWork. Ideally, you would use a dependency injection container to manage context lifetime, thus you can have a more consistent context state when you need it.

5 Comments

@gigi - Ok, that's better than using a static, but it's still a bad idea. The reason is that you're saving your context per thread, and you will have many contexts open (one per thread) for as long as your app runs. This will mean that the context will be in a different state depending on which thread executes, further you can very well run into concurrency issues where one context has cached data, and a different thread updates that data, then the first thread tries to update it.. You'll throw a concurrency exception.
@gigi - Just destroy the data context for each request. They are cheap to create and destroy, you're not saving any measureable performance by doing this. Internally, ADO manages a pool of database connections anyways, so the only thing you're doing here is causing yourself weird, hard to predict problems.
But what if i need to get entities from 4 repositories, shouldn't i create one context, all repositories use that context and after i finish querying just dispose the context?
@gigi - yes, you should use one context for all 4 repositories, and yes, the context should be released and disposed (not just disposed). If you use a dependency injection framework, then you can setup the creation of your context on a per-request basis, it's created the first time it's need and then automatically disposed when the request ends. You can do this manually, but it's a lot of work, and prone to errors where you forget to do it in one place but not another. By saving the context in your static hashtable the contexts are not being destroyed.
Thank you very much, i will consider what you told me and i will try to find a better way to manage repositories.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.