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

C# 实体框架-为什么选择导航属性?,c#,.net,entity-framework,navigation-properties,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 (_contex

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

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;
    }
LE2:我分别做了同样的事情:

  • 列出用户;
    使用(MyEntities ctx=new MyEntities())
    {
    users=ctx.users.ToList();
    }
    
  • 列出用户;
    使用(MyEntities ctx=new MyEntities())
    {
    users=ctx.users.Include(“角色”).ToList();
    }
    
  • 在第一种情况下不加载角色,在第二种情况下加载角色,这是正常的

    在存储库示例中,我看不出我做错了什么

    这是工作单元

        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;
                    }
                }
            }
        }
    
    公共类UnitOfWork
    {
    私有常量字符串HTTPCONTEXTKEY=“Repository.Key”;
    私有静态IUnitOfWorkFactory(联合工厂);
    私有静态只读哈希表_threads=new Hashtable();
    公共静态工作电流
    {
    得到
    {
    IUnitOfWork unitOfWork=GetUnitOfWork();
    如果(unitOfWork==null)
    {
    _unitOfWorkFactory=ObjectFactory.GetInstance();
    unitOfWork=\u unitOfWorkFactory.Create();
    保存unitOfWork(unitOfWork);
    }
    返回工作单元;
    }
    }
    私有静态IUnitOfWork GetUnitOfWork()
    {
    if(HttpContext.Current!=null)
    {
    if(HttpContext.Current.Items.Contains(HTTPCONTEXTKEY))
    {
    返回(IUnitOfWork)HttpContext.Current.Items[HTTPCONTEXTKEY];
    }
    返回null;
    }
    其他的
    {
    Thread Thread=Thread.CurrentThread;
    if(string.IsNullOrEmpty(thread.Name))
    {
    thread.Name=Guid.NewGuid().ToString();
    返回null;
    }
    其他的
    {
    锁定(_threads.SyncRoot)
    {
    return(IUnitOfWork)_threads[Thread.CurrentThread.Name];
    }
    }
    }
    }
    私有静态void SaveUnitOfWork(IUnitOfWork unitOfWork)
    {
    if(HttpContext.Current!=null)
    {
    HttpContext.Current.Items[HTTPCONTEXTKEY]=工作单元;
    }
    其他的
    {
    锁定(_threads.SyncRoot)
    {
    _threads[Thread.CurrentThread.Name]=工作单元;
    }
    }
    }
    }
    
    这条线看起来很像单身汉

    EFUnitOfWork currentUnitOfWork = (EFUnitOfWork)UnitOfWork.Current;
    
    如果是,并且您使用的是经典的单例模式,即使用静态成员来维护实例,那么您就是在玩dynamite。永远不要将数据上下文设置为静态

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

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

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

    这一行:

    UnitOfWork.Current.Dispose();
    
    也很糟糕。你不应该那样打电话,除非你非常小心。同样,如果您的上下文是静态的,那么您可以在另一个线程使用它时处理它


    总而言之,您真正的问题与在缓存中预加载数据有关,有时与此无关。我建议你们认真地重新考虑你们是如何使用你们的单位工作的。理想情况下,您可以使用依赖项注入容器来管理上下文生存期,这样,当您需要时,您可以拥有更一致的上下文状态。

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

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

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

    在本例中,您正在查询2中加载(部分)角色表。

    运行查询3时,这些行将在不包含Include的情况下填充,因为它们已经在DbContext中。

    在获取用户之前,是否先获取工作单元中的角色表?您是从工作单元中获取DbContext。如果从DbContext中的Roles表中获取行,然后在同一DbContext中获取用户,则EF将填充User.Roles集合,但不包含。我不会对角色做任何操作。哦,我现在明白了。谢谢你是对的。我在查询1Aaaah之后拨打查询3,接得好!我不应该写“最有可能”。。。我将删除我的答案。@gigi-好的,这比使用静态的要好,但这仍然是个坏主意。原因是你正在为每个线程保存上下文,并且只要你的应用程序运行,就会有许多上下文打开(每个线程一个)。这将意味着上下文将处于不同的状态,具体取决于执行哪个线程。此外,您可能会遇到并发问题,其中一个上下文缓存了数据,另一个线程更新该数据,然后第一个线程尝试更新该数据。。您将抛出一个并发异常。@gigi-只需销毁每个请求的数据上下文即可。它们的创建和销毁成本很低,这样做并不能节省任何可测量的性能。在内部,ADO以任何方式管理一个数据库连接池,因此您在这里所做的唯一一件事就是给自己带来奇怪的、难以预测的问题。但是如果我需要从4个存储库中获取实体,那该怎么办呢
        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;
                    }
                }
            }
        }
    
    EFUnitOfWork currentUnitOfWork = (EFUnitOfWork)UnitOfWork.Current;
    
    UnitOfWork.Current.Dispose();