实体框架 - 为什么选择导航属性?

时间:2012-09-23 17:34:12

标签: c# .net entity-framework navigation-properties

我有一个基本存储库,如下所示:

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"); 这将返回没有Roles属性的用户,这没关系。

  2. User user = _usersRepository.Get(new string[] { "Roles" }).SingleOrDefault(u => u.Username == "gigi");这将返回用户和Roles属性,这是正常的。

  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();

  5. 查询3和4都返回具有Roles属性的用户列表。 为什么查询3返回角色?

    LE:这是调用,我在处理上下文后检查用户集合。

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

    LE2:我分别做了同样的事情:

    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();
                  }
      
    3. 在第一种情况下,没有加载角色,在第二种情况下,它们是正常的。

      我没有看到我在存储库示例中做错了什么。

      LE3:这是工作单元

          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;
                      }
                  }
              }
          }
      

2 个答案:

答案 0 :(得分:2)

EF将尝试尽可能多地填充属性。

如果已将数据库行加载到DbContext中,EF将记住该行的DbContext生命周期内的数据。

然后,当您加载引用该行的任何实体时,EF将使用或不使用Include子句填充该属性。

在您的情况下,您正在加载(部分)查询2中的角色表。
运行查询3时,将填充这些行而不包含Include,因为它们已经在DbContext中。

答案 1 :(得分:1)

这条线看起来像是一个单身人士。

EFUnitOfWork currentUnitOfWork = (EFUnitOfWork)UnitOfWork.Current;

如果是,并且您正在使用经典的Singleton模式,您使用静态成员来维护实例,那么您正在使用炸药。您永远不应该使您的数据上下文成为静态。

原因很多。首先,这意味着您的上下文永远不会被破坏,并且将继续消耗内存以进行更改跟踪,直到耗尽内存(或工作进程重新启动)。

第二个也是最大的原因是静态在进程的所有线程之间共享,这意味着多个用户将使用相同的上下文,并且他们很可能会相互踩踏,破坏任何类型的一致性。 / p>

EF数据上下文不是线程安全的,它们也不是并发安全的(它们是两个不同的东西)。

这一行:

UnitOfWork.Current.Dispose();

也很糟糕。除非你非常小心,否则你不应该像这样召唤处理。同样,如果你的上下文是静态的,那么你可以在另一个线程正在使用它时处理它。

总而言之,您的真正问题与在缓存中预先加载数据有关,有时则不然。我建议您认真考虑如何使用UnitOfWork。理想情况下,您将使用依赖注入容器来管理上下文生存期,因此您可以在需要时拥有更一致的上下文状态。