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